flipper 1.1.2 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +7 -1
  3. data/.github/workflows/examples.yml +7 -1
  4. data/Changelog.md +1 -647
  5. data/Gemfile +3 -2
  6. data/README.md +1 -1
  7. data/Rakefile +2 -2
  8. data/exe/flipper +5 -0
  9. data/flipper.gemspec +5 -1
  10. data/lib/flipper/adapters/http/client.rb +25 -16
  11. data/lib/flipper/adapters/strict.rb +11 -8
  12. data/lib/flipper/cli.rb +244 -0
  13. data/lib/flipper/cloud/configuration.rb +7 -1
  14. data/lib/flipper/cloud/middleware.rb +5 -5
  15. data/lib/flipper/cloud/telemetry/submitter.rb +2 -2
  16. data/lib/flipper/cloud.rb +1 -1
  17. data/lib/flipper/engine.rb +32 -17
  18. data/lib/flipper/instrumentation/log_subscriber.rb +12 -3
  19. data/lib/flipper/metadata.rb +3 -1
  20. data/lib/flipper/test_help.rb +43 -0
  21. data/lib/flipper/version.rb +11 -1
  22. data/lib/generators/flipper/setup_generator.rb +63 -0
  23. data/spec/fixtures/environment.rb +1 -0
  24. data/spec/flipper/adapter_builder_spec.rb +1 -2
  25. data/spec/flipper/adapters/http/client_spec.rb +61 -0
  26. data/spec/flipper/adapters/http_spec.rb +92 -75
  27. data/spec/flipper/adapters/strict_spec.rb +11 -9
  28. data/spec/flipper/cli_spec.rb +189 -0
  29. data/spec/flipper/cloud/configuration_spec.rb +33 -35
  30. data/spec/flipper/cloud/dsl_spec.rb +5 -5
  31. data/spec/flipper/cloud/middleware_spec.rb +8 -8
  32. data/spec/flipper/cloud/telemetry/backoff_policy_spec.rb +8 -9
  33. data/spec/flipper/cloud/telemetry/submitter_spec.rb +24 -24
  34. data/spec/flipper/cloud/telemetry_spec.rb +1 -1
  35. data/spec/flipper/cloud_spec.rb +6 -7
  36. data/spec/flipper/engine_spec.rb +109 -57
  37. data/spec/flipper/instrumentation/log_subscriber_spec.rb +9 -2
  38. data/spec/flipper_spec.rb +1 -1
  39. data/spec/spec_helper.rb +1 -0
  40. data/spec/support/spec_helpers.rb +10 -4
  41. data/test_rails/generators/flipper/setup_generator_test.rb +64 -0
  42. data/test_rails/system/test_help_test.rb +51 -0
  43. metadata +20 -9
  44. data/spec/support/climate_control.rb +0 -7
@@ -12,10 +12,9 @@ RSpec.describe Flipper::Cloud::Configuration do
12
12
  end
13
13
 
14
14
  it "can set token from ENV var" do
15
- with_env "FLIPPER_CLOUD_TOKEN" => "from_env" do
16
- instance = described_class.new(required_options.reject { |k, v| k == :token })
17
- expect(instance.token).to eq("from_env")
18
- end
15
+ ENV["FLIPPER_CLOUD_TOKEN"] = "from_env"
16
+ instance = described_class.new(required_options.reject { |k, v| k == :token })
17
+ expect(instance.token).to eq("from_env")
19
18
  end
20
19
 
21
20
  it "can set instrumenter" do
@@ -30,10 +29,9 @@ RSpec.describe Flipper::Cloud::Configuration do
30
29
  end
31
30
 
32
31
  it "can set read_timeout from ENV var" do
33
- with_env "FLIPPER_CLOUD_READ_TIMEOUT" => "9" do
34
- instance = described_class.new(required_options.reject { |k, v| k == :read_timeout })
35
- expect(instance.read_timeout).to eq(9)
36
- end
32
+ ENV["FLIPPER_CLOUD_READ_TIMEOUT"] = "9"
33
+ instance = described_class.new(required_options.reject { |k, v| k == :read_timeout })
34
+ expect(instance.read_timeout).to eq(9)
37
35
  end
38
36
 
39
37
  it "can set open_timeout" do
@@ -42,10 +40,9 @@ RSpec.describe Flipper::Cloud::Configuration do
42
40
  end
43
41
 
44
42
  it "can set open_timeout from ENV var" do
45
- with_env "FLIPPER_CLOUD_OPEN_TIMEOUT" => "9" do
46
- instance = described_class.new(required_options.reject { |k, v| k == :open_timeout })
47
- expect(instance.open_timeout).to eq(9)
48
- end
43
+ ENV["FLIPPER_CLOUD_OPEN_TIMEOUT"] = "9"
44
+ instance = described_class.new(required_options.reject { |k, v| k == :open_timeout })
45
+ expect(instance.open_timeout).to eq(9)
49
46
  end
50
47
 
51
48
  it "can set write_timeout" do
@@ -54,10 +51,9 @@ RSpec.describe Flipper::Cloud::Configuration do
54
51
  end
55
52
 
56
53
  it "can set write_timeout from ENV var" do
57
- with_env "FLIPPER_CLOUD_WRITE_TIMEOUT" => "9" do
58
- instance = described_class.new(required_options.reject { |k, v| k == :write_timeout })
59
- expect(instance.write_timeout).to eq(9)
60
- end
54
+ ENV["FLIPPER_CLOUD_WRITE_TIMEOUT"] = "9"
55
+ instance = described_class.new(required_options.reject { |k, v| k == :write_timeout })
56
+ expect(instance.write_timeout).to eq(9)
61
57
  end
62
58
 
63
59
  it "can set sync_interval" do
@@ -66,10 +62,9 @@ RSpec.describe Flipper::Cloud::Configuration do
66
62
  end
67
63
 
68
64
  it "can set sync_interval from ENV var" do
69
- with_env "FLIPPER_CLOUD_SYNC_INTERVAL" => "15" do
70
- instance = described_class.new(required_options.reject { |k, v| k == :sync_interval })
71
- expect(instance.sync_interval).to eq(15)
72
- end
65
+ ENV["FLIPPER_CLOUD_SYNC_INTERVAL"] = "15"
66
+ instance = described_class.new(required_options.reject { |k, v| k == :sync_interval })
67
+ expect(instance.sync_interval).to eq(15)
73
68
  end
74
69
 
75
70
  it "passes sync_interval into sync adapter" do
@@ -86,6 +81,12 @@ RSpec.describe Flipper::Cloud::Configuration do
86
81
  expect(instance.debug_output).to eq(STDOUT)
87
82
  end
88
83
 
84
+ it "defaults debug_output to STDOUT if FLIPPER_CLOUD_DEBUG_OUTPUT_STDOUT set to true" do
85
+ ENV["FLIPPER_CLOUD_DEBUG_OUTPUT_STDOUT"] = "true"
86
+ instance = described_class.new(required_options)
87
+ expect(instance.debug_output).to eq(STDOUT)
88
+ end
89
+
89
90
  it "defaults adapter block" do
90
91
  # The initial sync of http to local invokes this web request.
91
92
  stub_request(:get, /flippercloud\.io/).to_return(status: 200, body: "{}")
@@ -121,10 +122,9 @@ RSpec.describe Flipper::Cloud::Configuration do
121
122
  end
122
123
 
123
124
  it "can override URL using ENV var" do
124
- with_env "FLIPPER_CLOUD_URL" => "https://example.com" do
125
- instance = described_class.new(required_options.reject { |k, v| k == :url })
126
- expect(instance.url).to eq("https://example.com")
127
- end
125
+ ENV["FLIPPER_CLOUD_URL"] = "https://example.com"
126
+ instance = described_class.new(required_options.reject { |k, v| k == :url })
127
+ expect(instance.url).to eq("https://example.com")
128
128
  end
129
129
 
130
130
  it "defaults sync_method to :poll" do
@@ -143,12 +143,11 @@ RSpec.describe Flipper::Cloud::Configuration do
143
143
  end
144
144
 
145
145
  it "sets sync_method to :webhook if FLIPPER_CLOUD_SYNC_SECRET set" do
146
- with_env "FLIPPER_CLOUD_SYNC_SECRET" => "abc" do
147
- instance = described_class.new(required_options)
146
+ ENV["FLIPPER_CLOUD_SYNC_SECRET"] = "abc"
147
+ instance = described_class.new(required_options)
148
148
 
149
- expect(instance.sync_method).to eq(:webhook)
150
- expect(instance.adapter).to be_instance_of(Flipper::Adapters::DualWrite)
151
- end
149
+ expect(instance.sync_method).to eq(:webhook)
150
+ expect(instance.adapter).to be_instance_of(Flipper::Adapters::DualWrite)
152
151
  end
153
152
 
154
153
  it "can set sync_secret" do
@@ -157,10 +156,9 @@ RSpec.describe Flipper::Cloud::Configuration do
157
156
  end
158
157
 
159
158
  it "can override sync_secret using ENV var" do
160
- with_env "FLIPPER_CLOUD_SYNC_SECRET" => "from_env" do
161
- instance = described_class.new(required_options.reject { |k, v| k == :sync_secret })
162
- expect(instance.sync_secret).to eq("from_env")
163
- end
159
+ ENV["FLIPPER_CLOUD_SYNC_SECRET"] = "from_env"
160
+ instance = described_class.new(required_options.reject { |k, v| k == :sync_secret })
161
+ expect(instance.sync_secret).to eq("from_env")
164
162
  end
165
163
 
166
164
  it "can sync with cloud" do
@@ -233,9 +231,9 @@ RSpec.describe Flipper::Cloud::Configuration do
233
231
  stub = stub_request(:get, "https://www.flippercloud.io/adapter/features?exclude_gate_names=true").
234
232
  with({
235
233
  headers: {
236
- 'Flipper-Cloud-Token'=>'asdf',
234
+ 'flipper-cloud-token'=>'asdf',
237
235
  },
238
- }).to_return(status: 200, body: body, headers: {})
236
+ }).to_return(status: 200, body: body)
239
237
  instance = described_class.new(required_options)
240
238
  instance.sync
241
239
 
@@ -18,7 +18,7 @@ RSpec.describe Flipper::Cloud::DSL do
18
18
  stub = stub_request(:get, "https://www.flippercloud.io/adapter/features?exclude_gate_names=true").
19
19
  with({
20
20
  headers: {
21
- 'Flipper-Cloud-Token'=>'asdf',
21
+ 'flipper-cloud-token'=>'asdf',
22
22
  },
23
23
  }).to_return(status: 200, body: '{"features": {}}', headers: {})
24
24
  cloud_configuration = Flipper::Cloud::Configuration.new({
@@ -65,11 +65,11 @@ RSpec.describe Flipper::Cloud::DSL do
65
65
 
66
66
  it "sends writes to cloud and local" do
67
67
  add_stub = stub_request(:post, "https://www.flippercloud.io/adapter/features").
68
- with({headers: {'Flipper-Cloud-Token'=>'asdf'}}).
69
- to_return(status: 200, body: '{}', headers: {})
68
+ with({headers: {'flipper-cloud-token'=>'asdf'}}).
69
+ to_return(status: 200, body: '{}')
70
70
  enable_stub = stub_request(:post, "https://www.flippercloud.io/adapter/features/foo/boolean").
71
- with(headers: {'Flipper-Cloud-Token'=>'asdf'}).
72
- to_return(status: 200, body: '{}', headers: {})
71
+ with(headers: {'flipper-cloud-token'=>'asdf'}).
72
+ to_return(status: 200, body: '{}')
73
73
 
74
74
  subject.enable(:foo)
75
75
 
@@ -101,8 +101,8 @@ RSpec.describe Flipper::Cloud::Middleware do
101
101
  post '/', request_body, env
102
102
 
103
103
  expect(last_response.status).to eq(402)
104
- expect(last_response.headers["Flipper-Cloud-Response-Error-Class"]).to eq("Flipper::Adapters::Http::Error")
105
- expect(last_response.headers["Flipper-Cloud-Response-Error-Message"]).to include("Failed with status: 402")
104
+ expect(last_response.headers["flipper-cloud-response-error-class"]).to eq("Flipper::Adapters::Http::Error")
105
+ expect(last_response.headers["flipper-cloud-response-error-message"]).to include("Failed with status: 402")
106
106
  expect(stub).to have_been_requested
107
107
  end
108
108
  end
@@ -124,8 +124,8 @@ RSpec.describe Flipper::Cloud::Middleware do
124
124
  post '/', request_body, env
125
125
 
126
126
  expect(last_response.status).to eq(500)
127
- expect(last_response.headers["Flipper-Cloud-Response-Error-Class"]).to eq("Flipper::Adapters::Http::Error")
128
- expect(last_response.headers["Flipper-Cloud-Response-Error-Message"]).to include("Failed with status: 503")
127
+ expect(last_response.headers["flipper-cloud-response-error-class"]).to eq("Flipper::Adapters::Http::Error")
128
+ expect(last_response.headers["flipper-cloud-response-error-message"]).to include("Failed with status: 503")
129
129
  expect(stub).to have_been_requested
130
130
  end
131
131
  end
@@ -147,8 +147,8 @@ RSpec.describe Flipper::Cloud::Middleware do
147
147
  post '/', request_body, env
148
148
 
149
149
  expect(last_response.status).to eq(500)
150
- expect(last_response.headers["Flipper-Cloud-Response-Error-Class"]).to eq("Net::OpenTimeout")
151
- expect(last_response.headers["Flipper-Cloud-Response-Error-Message"]).to eq("execution expired")
150
+ expect(last_response.headers["flipper-cloud-response-error-class"]).to eq("Net::OpenTimeout")
151
+ expect(last_response.headers["flipper-cloud-response-error-message"]).to eq("execution expired")
152
152
  expect(stub).to have_been_requested
153
153
  end
154
154
  end
@@ -277,13 +277,13 @@ RSpec.describe Flipper::Cloud::Middleware do
277
277
  stub = stub_request(:get, "https://www.flippercloud.io/adapter/features?exclude_gate_names=true").
278
278
  with({
279
279
  headers: {
280
- 'Flipper-Cloud-Token' => token,
280
+ 'flipper-cloud-token' => token,
281
281
  },
282
282
  })
283
283
  if status == :timeout
284
284
  stub.to_timeout
285
285
  else
286
- stub.to_return(status: status, body: response_body, headers: {})
286
+ stub.to_return(status: status, body: response_body)
287
287
  end
288
288
  end
289
289
  end
@@ -49,19 +49,18 @@ RSpec.describe Flipper::Cloud::Telemetry::BackoffPolicy do
49
49
  end
50
50
 
51
51
  it "from env" do
52
- env = {
52
+ ENV.update(
53
53
  "FLIPPER_BACKOFF_MIN_TIMEOUT_MS" => "1000",
54
54
  "FLIPPER_BACKOFF_MAX_TIMEOUT_MS" => "2000",
55
55
  "FLIPPER_BACKOFF_MULTIPLIER" => "1.9",
56
56
  "FLIPPER_BACKOFF_RANDOMIZATION_FACTOR" => "0.1",
57
- }
58
- with_env env do
59
- policy = described_class.new
60
- expect(policy.min_timeout_ms).to eq(1000)
61
- expect(policy.max_timeout_ms).to eq(2000)
62
- expect(policy.multiplier).to eq(1.9)
63
- expect(policy.randomization_factor).to eq(0.1)
64
- end
57
+ )
58
+
59
+ policy = described_class.new
60
+ expect(policy.min_timeout_ms).to eq(1000)
61
+ expect(policy.max_timeout_ms).to eq(2000)
62
+ expect(policy.multiplier).to eq(1.9)
63
+ expect(policy.randomization_factor).to eq(0.1)
65
64
  end
66
65
  end
67
66
 
@@ -43,33 +43,33 @@ RSpec.describe Flipper::Cloud::Telemetry::Submitter do
43
43
  ]
44
44
  }
45
45
  expected_headers = {
46
- 'Accept' => 'application/json',
47
- 'Client-Engine' => defined?(RUBY_ENGINE) ? RUBY_ENGINE : "",
48
- 'Client-Hostname' => Socket.gethostname,
49
- 'Client-Language' => 'ruby',
50
- 'Client-Language-Version' => "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})",
51
- 'Client-Pid' => Process.pid.to_s,
52
- 'Client-Platform' => RUBY_PLATFORM,
53
- 'Client-Thread' => Thread.current.object_id.to_s,
54
- 'Content-Encoding' => 'gzip',
55
- 'Content-Type' => 'application/json',
56
- 'Flipper-Cloud-Token' => 'asdf',
57
- 'Schema-Version' => 'V1',
58
- 'User-Agent' => "Flipper HTTP Adapter v#{Flipper::VERSION}",
46
+ 'accept' => 'application/json',
47
+ 'client-engine' => defined?(RUBY_ENGINE) ? RUBY_ENGINE : "",
48
+ 'client-hostname' => Socket.gethostname,
49
+ 'client-language' => 'ruby',
50
+ 'client-language-version' => "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})",
51
+ 'client-pid' => Process.pid.to_s,
52
+ 'client-platform' => RUBY_PLATFORM,
53
+ 'client-thread' => Thread.current.object_id.to_s,
54
+ 'content-encoding' => 'gzip',
55
+ 'content-type' => 'application/json',
56
+ 'flipper-cloud-token' => 'asdf',
57
+ 'schema-version' => 'V1',
58
+ 'user-agent' => "Flipper HTTP Adapter v#{Flipper::VERSION}",
59
59
  }
60
60
  stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
61
61
  with(headers: expected_headers) { |request|
62
62
  gunzipped = Flipper::Typecast.from_gzip(request.body)
63
63
  body = Flipper::Typecast.from_json(gunzipped)
64
64
  body == expected_body
65
- }.to_return(status: 200, body: "{}", headers: {})
65
+ }.to_return(status: 200, body: "{}")
66
66
  subject.call(enabled_metrics)
67
67
  end
68
68
 
69
69
  it "defaults backoff_policy" do
70
70
  stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
71
- to_return(status: 429, body: "{}", headers: {}).
72
- to_return(status: 200, body: "{}", headers: {})
71
+ to_return(status: 429, body: "{}").
72
+ to_return(status: 200, body: "{}")
73
73
  instance = described_class.new(cloud_configuration)
74
74
  expect(instance.backoff_policy.min_timeout_ms).to eq(1_000)
75
75
  expect(instance.backoff_policy.max_timeout_ms).to eq(30_000)
@@ -77,7 +77,7 @@ RSpec.describe Flipper::Cloud::Telemetry::Submitter do
77
77
 
78
78
  it "tries 10 times by default" do
79
79
  stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
80
- to_return(status: 500, body: "{}", headers: {})
80
+ to_return(status: 500, body: "{}")
81
81
  subject.call(enabled_metrics)
82
82
  expect(subject.backoff_policy.retries).to eq(9) # 9 retries + 1 initial attempt
83
83
  end
@@ -111,19 +111,19 @@ RSpec.describe Flipper::Cloud::Telemetry::Submitter do
111
111
 
112
112
  it "retries on 429" do
113
113
  stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
114
- to_return(status: 429, body: "{}", headers: {}).
115
- to_return(status: 429, body: "{}", headers: {}).
116
- to_return(status: 200, body: "{}", headers: {})
114
+ to_return(status: 429, body: "{}").
115
+ to_return(status: 429, body: "{}").
116
+ to_return(status: 200, body: "{}")
117
117
  subject.call(enabled_metrics)
118
118
  expect(subject.backoff_policy.retries).to eq(2)
119
119
  end
120
120
 
121
121
  it "retries on 500" do
122
122
  stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
123
- to_return(status: 500, body: "{}", headers: {}).
124
- to_return(status: 503, body: "{}", headers: {}).
125
- to_return(status: 502, body: "{}", headers: {}).
126
- to_return(status: 200, body: "{}", headers: {})
123
+ to_return(status: 500, body: "{}").
124
+ to_return(status: 503, body: "{}").
125
+ to_return(status: 502, body: "{}").
126
+ to_return(status: 200, body: "{}")
127
127
  subject.call(enabled_metrics)
128
128
  expect(subject.backoff_policy.retries).to eq(3)
129
129
  end
@@ -4,7 +4,7 @@ require 'flipper/cloud/configuration'
4
4
  RSpec.describe Flipper::Cloud::Telemetry do
5
5
  it "phones home and does not update telemetry interval if missing" do
6
6
  stub = stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
7
- to_return(status: 200, body: "{}", headers: {})
7
+ to_return(status: 200, body: "{}")
8
8
 
9
9
  cloud_configuration = Flipper::Cloud::Configuration.new(token: "test")
10
10
 
@@ -35,7 +35,7 @@ RSpec.describe Flipper::Cloud do
35
35
  expect(client.uri.scheme).to eq('https')
36
36
  expect(client.uri.host).to eq('www.flippercloud.io')
37
37
  expect(client.uri.path).to eq('/adapter')
38
- expect(client.headers['Flipper-Cloud-Token']).to eq(token)
38
+ expect(client.headers["flipper-cloud-token"]).to eq(token)
39
39
  expect(@instance.instrumenter).to be(Flipper::Instrumenters::Noop)
40
40
  end
41
41
  end
@@ -55,9 +55,8 @@ RSpec.describe Flipper::Cloud do
55
55
  end
56
56
 
57
57
  it 'can initialize with no token explicitly provided' do
58
- with_env 'FLIPPER_CLOUD_TOKEN' => 'asdf' do
59
- expect(described_class.new).to be_instance_of(Flipper::Cloud::DSL)
60
- end
58
+ ENV['FLIPPER_CLOUD_TOKEN'] = 'asdf'
59
+ expect(described_class.new).to be_instance_of(Flipper::Cloud::DSL)
61
60
  end
62
61
 
63
62
  it 'can set instrumenter' do
@@ -104,7 +103,7 @@ RSpec.describe Flipper::Cloud do
104
103
 
105
104
  it 'can import' do
106
105
  stub_request(:post, /www\.flippercloud\.io\/adapter\/features.*/).
107
- with(headers: {'Flipper-Cloud-Token'=>'asdf'}).to_return(status: 200, body: "{}", headers: {})
106
+ with(headers: {'flipper-cloud-token'=>'asdf'}).to_return(status: 200, body: "{}", headers: {})
108
107
 
109
108
  flipper = Flipper.new(Flipper::Adapters::Memory.new)
110
109
 
@@ -130,7 +129,7 @@ RSpec.describe Flipper::Cloud do
130
129
 
131
130
  it 'raises error for failure while importing' do
132
131
  stub_request(:post, /www\.flippercloud\.io\/adapter\/features.*/).
133
- with(headers: {'Flipper-Cloud-Token'=>'asdf'}).to_return(status: 500, body: "{}")
132
+ with(headers: {'flipper-cloud-token'=>'asdf'}).to_return(status: 500, body: "{}")
134
133
 
135
134
  flipper = Flipper.new(Flipper::Adapters::Memory.new)
136
135
 
@@ -155,7 +154,7 @@ RSpec.describe Flipper::Cloud do
155
154
 
156
155
  it 'raises error for timeout while importing' do
157
156
  stub_request(:post, /www\.flippercloud\.io\/adapter\/features.*/).
158
- with(headers: {'Flipper-Cloud-Token'=>'asdf'}).to_timeout
157
+ with(headers: {'flipper-cloud-token'=>'asdf'}).to_timeout
159
158
 
160
159
  flipper = Flipper.new(Flipper::Adapters::Memory.new)
161
160
 
@@ -33,86 +33,102 @@ RSpec.describe Flipper::Engine do
33
33
  let(:adapter) { Flipper.adapter.adapter }
34
34
 
35
35
  it 'can set strict=true from ENV' do
36
- with_env 'FLIPPER_STRICT' => 'true' do
37
- subject
38
- expect(config.strict).to eq(:raise)
39
- expect(adapter).to be_instance_of(Flipper::Adapters::Strict)
40
- end
36
+ ENV['FLIPPER_STRICT'] = 'true'
37
+ subject
38
+ expect(config.strict).to eq(:raise)
39
+ expect(adapter).to be_instance_of(Flipper::Adapters::Strict)
41
40
  end
42
41
 
43
42
  it 'can set strict=warn from ENV' do
44
- with_env 'FLIPPER_STRICT' => 'warn' do
45
- subject
46
- expect(config.strict).to eq(:warn)
47
- expect(adapter).to be_instance_of(Flipper::Adapters::Strict)
48
- expect(adapter.handler).to be(Flipper::Adapters::Strict::HANDLERS.fetch(:warn))
49
- end
43
+ ENV['FLIPPER_STRICT'] = 'warn'
44
+ subject
45
+ expect(config.strict).to eq(:warn)
46
+ expect(adapter).to be_instance_of(Flipper::Adapters::Strict)
47
+ expect(adapter.handler).to be(:warn)
50
48
  end
51
49
 
52
50
  it 'can set strict=false from ENV' do
53
- with_env 'FLIPPER_STRICT' => 'false' do
51
+ ENV['FLIPPER_STRICT'] = 'false'
52
+ subject
53
+ expect(config.strict).to eq(false)
54
+ expect(adapter).to be_instance_of(Flipper::Adapters::Memory)
55
+ end
56
+
57
+ [true, :raise, :warn].each do |value|
58
+ it "can set strict=#{value.inspect} in initializer" do
59
+ initializer { config.strict = value }
54
60
  subject
55
- expect(config.strict).to eq(false)
56
- expect(adapter).to be_instance_of(Flipper::Adapters::Memory)
61
+ expect(adapter).to be_instance_of(Flipper::Adapters::Strict)
62
+ expect(adapter.handler).to be(value)
57
63
  end
58
64
  end
59
65
 
60
- it "defaults to strict=false in RAILS_ENV=production" do
61
- Rails.env = "production"
62
- subject
63
- expect(config.strict).to eq(false)
64
- expect(adapter).to be_instance_of(Flipper::Adapters::Memory)
66
+ it "can set strict=false in initializer" do
67
+ initializer { config.strict = false }
68
+ subject
69
+ expect(config.strict).to eq(false)
70
+ expect(adapter).to be_instance_of(Flipper::Adapters::Memory)
65
71
  end
66
72
 
67
- %w(development test).each do |env|
73
+ it "defaults to strict=:warn in RAILS_ENV=development" do
74
+ Rails.env = "development"
75
+ subject
76
+ expect(config.strict).to eq(:warn)
77
+ expect(adapter).to be_instance_of(Flipper::Adapters::Strict)
78
+ end
79
+
80
+ %w(production test).each do |env|
68
81
  it "defaults to strict=warn in RAILS_ENV=#{env}" do
69
82
  Rails.env = env
70
83
  expect(Rails.env).to eq(env)
71
84
  subject
72
- expect(config.strict).to eq(:warn)
73
- expect(adapter).to be_instance_of(Flipper::Adapters::Strict)
74
- expect(adapter.handler).to be(Flipper::Adapters::Strict::HANDLERS.fetch(:warn))
85
+ expect(config.strict).to eq(false)
86
+ expect(adapter).to be_instance_of(Flipper::Adapters::Memory)
75
87
  end
76
88
  end
89
+
90
+ it "defaults to strict=warn in RAILS_ENV=development" do
91
+ Rails.env = "development"
92
+ expect(Rails.env).to eq("development")
93
+ subject
94
+ expect(config.strict).to eq(:warn)
95
+ expect(adapter).to be_instance_of(Flipper::Adapters::Strict)
96
+ expect(adapter.handler).to be(:warn)
97
+ end
77
98
  end
78
99
 
79
100
  context 'cloudless' do
80
101
  it_behaves_like 'config.strict'
81
102
 
82
103
  it 'can set env_key from ENV' do
83
- with_env 'FLIPPER_ENV_KEY' => 'flopper' do
84
- subject
85
- expect(config.env_key).to eq('flopper')
86
- end
104
+ ENV['FLIPPER_ENV_KEY'] = 'flopper'
105
+ subject
106
+ expect(config.env_key).to eq('flopper')
87
107
  end
88
108
 
89
109
  it 'can set memoize from ENV' do
90
- with_env 'FLIPPER_MEMOIZE' => 'false' do
91
- subject
92
- expect(config.memoize).to eq(false)
93
- end
110
+ ENV['FLIPPER_MEMOIZE'] = 'false'
111
+ subject
112
+ expect(config.memoize).to eq(false)
94
113
  end
95
114
 
96
115
  it 'can set preload from ENV' do
97
- with_env 'FLIPPER_PRELOAD' => 'false' do
98
- subject
99
- expect(config.preload).to eq(false)
100
- end
116
+ ENV['FLIPPER_PRELOAD'] = 'false'
117
+ subject
118
+ expect(config.preload).to eq(false)
101
119
  end
102
120
 
103
121
  it 'can set instrumenter from ENV' do
104
122
  stub_const('My::Cool::Instrumenter', Class.new)
105
- with_env 'FLIPPER_INSTRUMENTER' => 'My::Cool::Instrumenter' do
106
- subject
107
- expect(config.instrumenter).to eq(My::Cool::Instrumenter)
108
- end
123
+ ENV['FLIPPER_INSTRUMENTER'] = 'My::Cool::Instrumenter'
124
+ subject
125
+ expect(config.instrumenter).to eq(My::Cool::Instrumenter)
109
126
  end
110
127
 
111
128
  it 'can set log from ENV' do
112
- with_env 'FLIPPER_LOG' => 'false' do
113
- subject
114
- expect(config.log).to eq(false)
115
- end
129
+ ENV['FLIPPER_LOG'] = 'false'
130
+ subject
131
+ expect(config.log).to eq(false)
116
132
  end
117
133
 
118
134
  it 'sets defaults' do
@@ -153,13 +169,51 @@ RSpec.describe Flipper::Engine do
153
169
  if: nil
154
170
  })
155
171
  end
172
+
173
+ context "test_help" do
174
+ it "is loaded if RAILS_ENV=test" do
175
+ Rails.env = "test"
176
+ allow(Flipper::Engine.instance).to receive(:require).and_call_original
177
+ expect(Flipper::Engine.instance).to receive(:require).with("flipper/test_help")
178
+ subject
179
+ expect(config.test_help).to eq(true)
180
+ end
181
+
182
+ it "is loaded if FLIPPER_TEST_HELP=true" do
183
+ ENV["FLIPPER_TEST_HELP"] = "true"
184
+ allow(Flipper::Engine.instance).to receive(:require).and_call_original
185
+ expect(Flipper::Engine.instance).to receive(:require).with("flipper/test_help")
186
+ subject
187
+ expect(config.test_help).to eq(true)
188
+ end
189
+
190
+ it "is loaded if config.flipper.test_help = true" do
191
+ initializer { config.test_help = true }
192
+ allow(Flipper::Engine.instance).to receive(:require).and_call_original
193
+ expect(Flipper::Engine.instance).to receive(:require).with("flipper/test_help")
194
+ subject
195
+ end
196
+
197
+ it "is not loaded if FLIPPER_TEST_HELP=false" do
198
+ ENV["FLIPPER_TEST_HELP"] = "false"
199
+ allow(Flipper::Engine.instance).to receive(:require).and_call_original
200
+ expect(Flipper::Engine.instance).to receive(:require).with("flipper/test_help").never
201
+ subject
202
+ end
203
+
204
+ it "is not loaded if config.flipper.test_help = false" do
205
+ Rails.env = "true"
206
+ initializer { config.test_help = false }
207
+ allow(Flipper::Engine.instance).to receive(:require).and_call_original
208
+ expect(Flipper::Engine.instance).to receive(:require).with("flipper/test_help").never
209
+ subject
210
+ end
211
+ end
156
212
  end
157
213
 
158
214
  context 'with cloud' do
159
- around do |example|
160
- with_env "FLIPPER_CLOUD_TOKEN" => "test-token" do
161
- example.run
162
- end
215
+ before do
216
+ ENV["FLIPPER_CLOUD_TOKEN"] = "test-token"
163
217
  end
164
218
 
165
219
  # App for Rack::Test
@@ -167,7 +221,8 @@ RSpec.describe Flipper::Engine do
167
221
 
168
222
  it_behaves_like 'config.strict' do
169
223
  let(:adapter) do
170
- dual_write = Flipper.adapter.adapter
224
+ memoizable = Flipper.adapter
225
+ dual_write = memoizable.adapter
171
226
  poll = dual_write.local
172
227
  poll.adapter
173
228
  end
@@ -183,10 +238,8 @@ RSpec.describe Flipper::Engine do
183
238
  end
184
239
 
185
240
  context "with CLOUD_SYNC_SECRET" do
186
- around do |example|
187
- with_env "FLIPPER_CLOUD_SYNC_SECRET" => "test-secret" do
188
- example.run
189
- end
241
+ before do
242
+ ENV["FLIPPER_CLOUD_SYNC_SECRET"] = "test-secret"
190
243
  end
191
244
 
192
245
  let(:request_body) do
@@ -210,7 +263,7 @@ RSpec.describe Flipper::Engine do
210
263
  application.initialize!
211
264
 
212
265
  stub = stub_request(:get, "https://www.flippercloud.io/adapter/features?exclude_gate_names=true").with({
213
- headers: { "Flipper-Cloud-Token" => ENV["FLIPPER_CLOUD_TOKEN"] },
266
+ headers: { "flipper-cloud-token" => ENV["FLIPPER_CLOUD_TOKEN"] },
214
267
  }).to_return(status: 200, body: JSON.generate({ features: {} }), headers: {})
215
268
 
216
269
  post "/_flipper", request_body, { "HTTP_FLIPPER_CLOUD_SIGNATURE" => signature_header_value }
@@ -231,10 +284,9 @@ RSpec.describe Flipper::Engine do
231
284
 
232
285
  context "without FLIPPER_CLOUD_TOKEN" do
233
286
  it "gracefully skips configuring webhook app" do
234
- with_env "FLIPPER_CLOUD_TOKEN" => nil do
235
- application.initialize!
236
- expect(Flipper.instance).to be_a(Flipper::DSL)
237
- end
287
+ ENV["FLIPPER_CLOUD_TOKEN"] = nil
288
+ application.initialize!
289
+ expect(Flipper.instance).to be_a(Flipper::DSL)
238
290
 
239
291
  post "/_flipper"
240
292
  expect(last_response.status).to eq(404)