summaryrefslogtreecommitdiff
path: root/qa/spec/specs/helpers/feature_flag_spec.rb
blob: a1300ecf073eae8bdc4f05f929f63983ae7ed8a9 (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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# frozen_string_literal: true

require 'rspec/core/sandbox'

RSpec.describe QA::Specs::Helpers::FeatureFlag do
  include QA::Support::Helpers::StubEnv
  include QA::Specs::Helpers::RSpec

  around do |ex|
    RSpec::Core::Sandbox.sandboxed do |config|
      config.add_formatter QA::Support::Formatters::ContextFormatter
      config.add_formatter QA::Support::Formatters::QuarantineFormatter
      config.add_formatter QA::Support::Formatters::FeatureFlagFormatter

      # If there is an example-within-an-example, we want to make sure the inner example
      # does not get a reference to the outer example (the real spec) if it calls
      # something like `pending`
      config.before(:context) { RSpec.current_example = nil }

      config.color_mode = :off

      ex.run
    end
  end

  describe '.skip_or_run_feature_flag_tests_or_contexts' do
    shared_examples 'runs with given feature flag metadata' do |metadata|
      it do
        group = describe_successfully 'Feature flag test', feature_flag: metadata do
          it('passes') {}
        end

        expect(group.examples.first.execution_result.status).to eq(:passed)
      end
    end

    shared_examples 'skips with given feature flag metadata' do |metadata|
      it do
        group = describe_successfully 'Feature flag test', feature_flag: metadata do
          it('is skipped') {}
        end

        expect(group.examples.first.execution_result.status).to eq(:pending)
      end
    end

    context 'when run on staging' do
      before(:context) do
        QA::Runtime::Scenario.define(:gitlab_address, 'https://staging.gitlab.com')
      end

      context 'when no scope is defined' do
        it_behaves_like 'runs with given feature flag metadata', { name: 'no_scope_ff' }

        it 'is skipped if quarantine tag is also applied' do
          group = describe_successfully(
            'Feature flag with no scope',
            feature_flag: { name: 'quarantine_with_ff' },
            quarantine: {
              issue: 'https://gitlab.com/test-group/test/-/issues/123',
              type: 'bug'
            }
          ) do
            it('is skipped') {}
          end

          expect(group.examples.first.execution_result.status).to eq(:pending)
        end
      end

      it_behaves_like 'runs with given feature flag metadata', { name: 'actor_ff', scope: :project }

      it_behaves_like 'skips with given feature flag metadata', { name: 'global_ff', scope: :global }

      context 'when should be skipped in a specific job' do
        before do
          stub_env('CI_JOB_NAME', 'job-to-skip')
        end

        it 'is skipped for that job' do
          group = describe_successfully(
            'Test should be skipped',
            feature_flag: { name: 'skip_job_ff' },
            except: { job: 'job-to-skip' }
          ) do
            it('does not run on staging in specified job') {}
          end

          expect(group.examples.first.execution_result.status).to eq(:pending)
        end
      end

      context 'when should only run in a specific job' do
        before do
          stub_env('CI_JOB_NAME', 'job-to-run')
        end

        it 'is run for that job' do
          group = describe_successfully(
            'Test should run',
            feature_flag: { name: 'run_job_ff' },
            only: { job: 'job-to-run' }
          ) do
            it('runs on staging in specified job') {}
          end

          expect(group.examples.first.execution_result.status).to eq(:passed)
        end

        it 'skips if test is set to only run in a job different from current CI job' do
          group = describe_successfully(
            'Test should be skipped',
            feature_flag: { name: 'skip_job_ff' },
            only: { job: 'other-job' }
          ) do
            it('does not run on staging in specified job') {}
          end

          expect(group.examples.first.execution_result.status).to eq(:pending)
        end
      end
    end

    context 'when run on production' do
      before(:context) do
        QA::Runtime::Scenario.define(:gitlab_address, 'https://gitlab.com')
      end

      context 'when no scope is defined' do
        it_behaves_like 'skips with given feature flag metadata', { name: 'no_scope_ff' }

        context 'for only one test in the example group' do
          it 'only skips specified test and runs all others' do
            group = describe_successfully 'Feature flag set for one test' do
              it('is skipped', feature_flag: { name: 'single_test_ff' }) {}
              it('passes') {}
            end

            expect(group.examples[0].execution_result.status).to eq(:pending)
            expect(group.examples[1].execution_result.status).to eq(:passed)
          end
        end
      end

      it_behaves_like 'skips with given feature flag metadata', { name: 'actor_ff', scope: :project }

      it_behaves_like 'skips with given feature flag metadata', { name: 'global_ff', scope: :global }
    end

    # The nightly package job, for example, does not run against a live environment with
    # a defined gitlab_address. In this case, feature_flag tag logic can be safely ignored
    context 'when run without a gitlab address specified' do
      before(:context) do
        QA::Runtime::Scenario.define(:gitlab_address, nil)
      end

      it_behaves_like 'runs with given feature flag metadata', { name: 'no_scope_ff' }

      it_behaves_like 'runs with given feature flag metadata', { name: 'actor_ff', scope: :project }

      it_behaves_like 'runs with given feature flag metadata', { name: 'global_ff', scope: :global }
    end
  end
end