summaryrefslogtreecommitdiff
path: root/qa/qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_with_manual_jobs_spec.rb
blob: 37d1e20111d6a2436d15c8b842ee83cc6ec341fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# frozen_string_literal: true

module QA
  RSpec.describe 'Verify', :runner, product_group: :pipeline_execution do
    describe 'Run pipeline with manual jobs' do
      let(:executor) { "qa-runner-#{SecureRandom.hex(4)}" }

      let(:project) do
        Resource::Project.fabricate_via_api! do |project|
          project.name = 'pipeline-with-manual-job'
          project.description = 'Project for pipeline with manual job'
        end
      end

      let!(:runner) do
        Resource::ProjectRunner.fabricate! do |runner|
          runner.project = project
          runner.name = executor
          runner.tags = [executor]
        end
      end

      let!(:ci_file) do
        Resource::Repository::Commit.fabricate_via_api! do |commit|
          commit.project = project
          commit.commit_message = 'Add .gitlab-ci.yml'
          commit.add_files(
            [
              {
                file_path: '.gitlab-ci.yml',
                content: <<~YAML
                  default:
                    tags: ["#{executor}"]

                  stages:
                    - Stage1
                    - Stage2
                    - Stage3

                  Prep:
                    stage: Stage1
                    script: exit 0
                    when: manual

                  Build:
                    stage: Stage2
                    needs: ['Prep']
                    script: exit 0
                    parallel: 6

                  Test:
                    stage: Stage3
                    needs: ['Build']
                    script: exit 0

                  Deploy:
                    stage: Stage3
                    needs: ['Test']
                    script: exit 0
                    parallel: 6
                YAML
              }
            ]
          )
        end
      end

      before do
        make_sure_to_have_a_skipped_pipeline

        Flow::Login.sign_in
        project.visit!
        Flow::Pipeline.visit_latest_pipeline(status: 'skipped')
      end

      after do
        runner&.remove_via_api!
      end

      it(
        'does not leave any job in skipped state',
        testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349158'
      ) do
        Page::Project::Pipeline::Show.perform do |show|
          show.click_job_action('Prep') # Trigger pipeline manually

          show.wait_until(max_duration: 300, sleep_interval: 2, reload: false) do
            project.latest_pipeline[:status] == 'success'
          end

          aggregate_failures do
            expect(show).to have_build('Test', status: :success)

            show.click_job_dropdown('Build')
            expect(show).not_to have_skipped_job_in_group

            show.click_job_dropdown('Build') # Close Build dropdown
            show.click_job_dropdown('Deploy')
            expect(show).not_to have_skipped_job_in_group
          end
        end
      end

      private

      # Wait for first pipeline to finish and have "skipped" status
      # If it takes too long, create new pipeline and retry (2 times)
      def make_sure_to_have_a_skipped_pipeline
        attempts ||= 1
        Runtime::Logger.info('Waiting for pipeline to have status "skipped"...')
        Support::Waiter.wait_until(max_duration: 120, sleep_interval: 3, retry_on_exception: true) do
          project.latest_pipeline[:status] == 'skipped'
        end
      rescue Support::Repeater::WaitExceededError
        raise 'Failed to create skipped pipeline after 3 attempts.' unless (attempts += 1) < 4

        Runtime::Logger.debug(
          "Previous pipeline took too long to finish. Potential jobs with problems:\n#{problematic_jobs}"
        )
        Runtime::Logger.info("Triggering a new pipeline...")
        trigger_new_pipeline
        retry
      end

      def trigger_new_pipeline
        original_count = project.pipelines.length
        Resource::Pipeline.fabricate_via_api! do |pipeline|
          pipeline.project = project
        end

        Support::Waiter.wait_until(sleep_interval: 1) { project.pipelines.length > original_count }
      end

      # We know that all the jobs in pipeline are purposely skipped
      # The pipeline should have status "skipped" almost right away after being created
      # If pipeline is held up, likely because there are some jobs that
      # doesn't have either "skipped" or "manual" status
      def problematic_jobs
        pipeline = Resource::Pipeline.fabricate_via_api! do |pipeline|
          pipeline.project = project
          pipeline.id = project.latest_pipeline[:id]
        end

        acceptable_statuses = %w[skipped manual]
        pipeline.pipeline_jobs.select { |job| !(acceptable_statuses.include? job[:status]) }
      end
    end
  end
end