flipper 1.1.2 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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)