flipper 1.1.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +9 -2
  3. data/.github/workflows/examples.yml +8 -2
  4. data/Changelog.md +1 -647
  5. data/Gemfile +3 -2
  6. data/README.md +3 -1
  7. data/Rakefile +2 -2
  8. data/docs/images/banner.jpg +0 -0
  9. data/exe/flipper +5 -0
  10. data/flipper.gemspec +5 -1
  11. data/lib/flipper/adapters/actor_limit.rb +28 -0
  12. data/lib/flipper/adapters/cache_base.rb +143 -0
  13. data/lib/flipper/adapters/http/client.rb +25 -16
  14. data/lib/flipper/adapters/operation_logger.rb +18 -88
  15. data/lib/flipper/adapters/read_only.rb +6 -39
  16. data/lib/flipper/adapters/strict.rb +16 -18
  17. data/lib/flipper/adapters/wrapper.rb +54 -0
  18. data/lib/flipper/cli.rb +263 -0
  19. data/lib/flipper/cloud/configuration.rb +9 -4
  20. data/lib/flipper/cloud/middleware.rb +5 -5
  21. data/lib/flipper/cloud/telemetry/instrumenter.rb +4 -8
  22. data/lib/flipper/cloud/telemetry/submitter.rb +2 -2
  23. data/lib/flipper/cloud/telemetry.rb +10 -2
  24. data/lib/flipper/cloud.rb +1 -1
  25. data/lib/flipper/engine.rb +32 -17
  26. data/lib/flipper/instrumentation/log_subscriber.rb +12 -3
  27. data/lib/flipper/metadata.rb +3 -1
  28. data/lib/flipper/poller.rb +6 -5
  29. data/lib/flipper/serializers/gzip.rb +3 -5
  30. data/lib/flipper/serializers/json.rb +3 -5
  31. data/lib/flipper/spec/shared_adapter_specs.rb +17 -16
  32. data/lib/flipper/test/shared_adapter_test.rb +17 -17
  33. data/lib/flipper/test_help.rb +43 -0
  34. data/lib/flipper/typecast.rb +3 -3
  35. data/lib/flipper/version.rb +11 -1
  36. data/lib/flipper.rb +3 -1
  37. data/lib/generators/flipper/setup_generator.rb +63 -0
  38. data/package-lock.json +41 -0
  39. data/package.json +10 -0
  40. data/spec/fixtures/environment.rb +1 -0
  41. data/spec/flipper/adapter_builder_spec.rb +1 -2
  42. data/spec/flipper/adapters/actor_limit_spec.rb +20 -0
  43. data/spec/flipper/adapters/http/client_spec.rb +61 -0
  44. data/spec/flipper/adapters/http_spec.rb +102 -76
  45. data/spec/flipper/adapters/strict_spec.rb +11 -9
  46. data/spec/flipper/cli_spec.rb +164 -0
  47. data/spec/flipper/cloud/configuration_spec.rb +35 -36
  48. data/spec/flipper/cloud/dsl_spec.rb +5 -5
  49. data/spec/flipper/cloud/middleware_spec.rb +8 -8
  50. data/spec/flipper/cloud/telemetry/backoff_policy_spec.rb +8 -9
  51. data/spec/flipper/cloud/telemetry/submitter_spec.rb +24 -24
  52. data/spec/flipper/cloud/telemetry_spec.rb +53 -1
  53. data/spec/flipper/cloud_spec.rb +10 -9
  54. data/spec/flipper/engine_spec.rb +140 -58
  55. data/spec/flipper/instrumentation/log_subscriber_spec.rb +9 -2
  56. data/spec/flipper/middleware/memoizer_spec.rb +7 -4
  57. data/spec/flipper_spec.rb +1 -1
  58. data/spec/spec_helper.rb +1 -0
  59. data/spec/support/fail_on_output.rb +8 -0
  60. data/spec/support/spec_helpers.rb +12 -5
  61. data/test/adapters/actor_limit_test.rb +20 -0
  62. data/test_rails/generators/flipper/setup_generator_test.rb +64 -0
  63. data/test_rails/system/test_help_test.rb +51 -0
  64. metadata +31 -9
  65. data/spec/support/climate_control.rb +0 -7
@@ -4,8 +4,10 @@ require 'flipper/engine'
4
4
  RSpec.describe Flipper::Engine do
5
5
  let(:application) do
6
6
  Class.new(Rails::Application) do
7
+ config.load_defaults Rails::VERSION::STRING.to_f
7
8
  config.eager_load = false
8
9
  config.logger = ActiveSupport::Logger.new($stdout)
10
+ config.active_support.remove_deprecated_time_with_zone_name = false
9
11
  end.instance
10
12
  end
11
13
 
@@ -33,86 +35,102 @@ RSpec.describe Flipper::Engine do
33
35
  let(:adapter) { Flipper.adapter.adapter }
34
36
 
35
37
  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
38
+ ENV['FLIPPER_STRICT'] = 'true'
39
+ subject
40
+ expect(config.strict).to eq(:raise)
41
+ expect(adapter).to be_instance_of(Flipper::Adapters::Strict)
41
42
  end
42
43
 
43
44
  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
45
+ ENV['FLIPPER_STRICT'] = 'warn'
46
+ subject
47
+ expect(config.strict).to eq(:warn)
48
+ expect(adapter).to be_instance_of(Flipper::Adapters::Strict)
49
+ expect(adapter.handler).to be(:warn)
50
50
  end
51
51
 
52
52
  it 'can set strict=false from ENV' do
53
- with_env 'FLIPPER_STRICT' => 'false' do
53
+ ENV['FLIPPER_STRICT'] = 'false'
54
+ subject
55
+ expect(config.strict).to eq(false)
56
+ expect(adapter).not_to be_instance_of(Flipper::Adapters::Strict)
57
+ end
58
+
59
+ [true, :raise, :warn].each do |value|
60
+ it "can set strict=#{value.inspect} in initializer" do
61
+ initializer { config.strict = value }
54
62
  subject
55
- expect(config.strict).to eq(false)
56
- expect(adapter).to be_instance_of(Flipper::Adapters::Memory)
63
+ expect(adapter).to be_instance_of(Flipper::Adapters::Strict)
64
+ expect(adapter.handler).to be(value)
57
65
  end
58
66
  end
59
67
 
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)
68
+ it "can set strict=false in initializer" do
69
+ initializer { config.strict = false }
70
+ subject
71
+ expect(config.strict).to eq(false)
72
+ expect(adapter).not_to be_instance_of(Flipper::Adapters::Strict)
65
73
  end
66
74
 
67
- %w(development test).each do |env|
75
+ it "defaults to strict=:warn in RAILS_ENV=development" do
76
+ Rails.env = "development"
77
+ subject
78
+ expect(config.strict).to eq(:warn)
79
+ expect(adapter).to be_instance_of(Flipper::Adapters::Strict)
80
+ end
81
+
82
+ %w(production test).each do |env|
68
83
  it "defaults to strict=warn in RAILS_ENV=#{env}" do
69
84
  Rails.env = env
70
85
  expect(Rails.env).to eq(env)
71
86
  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))
87
+ expect(config.strict).to eq(false)
88
+ expect(adapter).not_to be_instance_of(Flipper::Adapters::Strict)
75
89
  end
76
90
  end
91
+
92
+ it "defaults to strict=warn in RAILS_ENV=development" do
93
+ Rails.env = "development"
94
+ expect(Rails.env).to eq("development")
95
+ subject
96
+ expect(config.strict).to eq(:warn)
97
+ expect(adapter).to be_instance_of(Flipper::Adapters::Strict)
98
+ expect(adapter.handler).to be(:warn)
99
+ end
77
100
  end
78
101
 
79
102
  context 'cloudless' do
80
103
  it_behaves_like 'config.strict'
81
104
 
82
105
  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
106
+ ENV['FLIPPER_ENV_KEY'] = 'flopper'
107
+ subject
108
+ expect(config.env_key).to eq('flopper')
87
109
  end
88
110
 
89
111
  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
112
+ ENV['FLIPPER_MEMOIZE'] = 'false'
113
+ subject
114
+ expect(config.memoize).to eq(false)
94
115
  end
95
116
 
96
117
  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
118
+ ENV['FLIPPER_PRELOAD'] = 'false'
119
+ subject
120
+ expect(config.preload).to eq(false)
101
121
  end
102
122
 
103
123
  it 'can set instrumenter from ENV' do
104
124
  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
125
+ ENV['FLIPPER_INSTRUMENTER'] = 'My::Cool::Instrumenter'
126
+ subject
127
+ expect(config.instrumenter).to eq(My::Cool::Instrumenter)
109
128
  end
110
129
 
111
130
  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
131
+ ENV['FLIPPER_LOG'] = 'false'
132
+ subject
133
+ expect(config.log).to eq(false)
116
134
  end
117
135
 
118
136
  it 'sets defaults' do
@@ -153,13 +171,51 @@ RSpec.describe Flipper::Engine do
153
171
  if: nil
154
172
  })
155
173
  end
174
+
175
+ context "test_help" do
176
+ it "is loaded if RAILS_ENV=test" do
177
+ Rails.env = "test"
178
+ allow(Flipper::Engine.instance).to receive(:require).and_call_original
179
+ expect(Flipper::Engine.instance).to receive(:require).with("flipper/test_help")
180
+ subject
181
+ expect(config.test_help).to eq(true)
182
+ end
183
+
184
+ it "is loaded if FLIPPER_TEST_HELP=true" do
185
+ ENV["FLIPPER_TEST_HELP"] = "true"
186
+ allow(Flipper::Engine.instance).to receive(:require).and_call_original
187
+ expect(Flipper::Engine.instance).to receive(:require).with("flipper/test_help")
188
+ subject
189
+ expect(config.test_help).to eq(true)
190
+ end
191
+
192
+ it "is loaded if config.flipper.test_help = true" do
193
+ initializer { config.test_help = true }
194
+ allow(Flipper::Engine.instance).to receive(:require).and_call_original
195
+ expect(Flipper::Engine.instance).to receive(:require).with("flipper/test_help")
196
+ subject
197
+ end
198
+
199
+ it "is not loaded if FLIPPER_TEST_HELP=false" do
200
+ ENV["FLIPPER_TEST_HELP"] = "false"
201
+ allow(Flipper::Engine.instance).to receive(:require).and_call_original
202
+ expect(Flipper::Engine.instance).to receive(:require).with("flipper/test_help").never
203
+ subject
204
+ end
205
+
206
+ it "is not loaded if config.flipper.test_help = false" do
207
+ Rails.env = "true"
208
+ initializer { config.test_help = false }
209
+ allow(Flipper::Engine.instance).to receive(:require).and_call_original
210
+ expect(Flipper::Engine.instance).to receive(:require).with("flipper/test_help").never
211
+ subject
212
+ end
213
+ end
156
214
  end
157
215
 
158
216
  context 'with cloud' do
159
- around do |example|
160
- with_env "FLIPPER_CLOUD_TOKEN" => "test-token" do
161
- example.run
162
- end
217
+ before do
218
+ ENV["FLIPPER_CLOUD_TOKEN"] = "test-token"
163
219
  end
164
220
 
165
221
  # App for Rack::Test
@@ -167,7 +223,8 @@ RSpec.describe Flipper::Engine do
167
223
 
168
224
  it_behaves_like 'config.strict' do
169
225
  let(:adapter) do
170
- dual_write = Flipper.adapter.adapter
226
+ memoizable = Flipper.adapter
227
+ dual_write = memoizable.adapter
171
228
  poll = dual_write.local
172
229
  poll.adapter
173
230
  end
@@ -179,14 +236,13 @@ RSpec.describe Flipper::Engine do
179
236
  application.initialize!
180
237
 
181
238
  expect(Flipper.instance).to be_a(Flipper::Cloud::DSL)
182
- expect(Flipper.instance.instrumenter).to be(ActiveSupport::Notifications)
239
+ expect(Flipper.instance.instrumenter).to be_a(Flipper::Cloud::Telemetry::Instrumenter)
240
+ expect(Flipper.instance.instrumenter.instrumenter).to be(ActiveSupport::Notifications)
183
241
  end
184
242
 
185
243
  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
244
+ before do
245
+ ENV["FLIPPER_CLOUD_SYNC_SECRET"] = "test-secret"
190
246
  end
191
247
 
192
248
  let(:request_body) do
@@ -210,7 +266,7 @@ RSpec.describe Flipper::Engine do
210
266
  application.initialize!
211
267
 
212
268
  stub = stub_request(:get, "https://www.flippercloud.io/adapter/features?exclude_gate_names=true").with({
213
- headers: { "Flipper-Cloud-Token" => ENV["FLIPPER_CLOUD_TOKEN"] },
269
+ headers: { "flipper-cloud-token" => ENV["FLIPPER_CLOUD_TOKEN"] },
214
270
  }).to_return(status: 200, body: JSON.generate({ features: {} }), headers: {})
215
271
 
216
272
  post "/_flipper", request_body, { "HTTP_FLIPPER_CLOUD_SIGNATURE" => signature_header_value }
@@ -231,10 +287,9 @@ RSpec.describe Flipper::Engine do
231
287
 
232
288
  context "without FLIPPER_CLOUD_TOKEN" do
233
289
  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
290
+ ENV["FLIPPER_CLOUD_TOKEN"] = nil
291
+ application.initialize!
292
+ expect(Flipper.instance).to be_a(Flipper::DSL)
238
293
 
239
294
  post "/_flipper"
240
295
  expect(last_response.status).to eq(404)
@@ -282,6 +337,33 @@ RSpec.describe Flipper::Engine do
282
337
  expect(ActiveRecord::Base.ancestors).to include(Flipper::Model::ActiveRecord)
283
338
  end
284
339
 
340
+ describe "config.actor_limit" do
341
+ let(:adapter) do
342
+ application.initialize!
343
+ Flipper.adapter.adapter.adapter
344
+ end
345
+
346
+ it "defaults to 100" do
347
+ expect(adapter).to be_instance_of(Flipper::Adapters::ActorLimit)
348
+ expect(adapter.limit).to eq(100)
349
+ end
350
+
351
+ it "can be set from FLIPPER_ACTOR_LIMIT env" do
352
+ ENV["FLIPPER_ACTOR_LIMIT"] = "500"
353
+ expect(adapter.limit).to eq(500)
354
+ end
355
+
356
+ it "can be set from an initializer" do
357
+ initializer { config.actor_limit = 99 }
358
+ expect(adapter.limit).to eq(99)
359
+ end
360
+
361
+ it "can be disabled from an initializer" do
362
+ initializer { config.actor_limit = false }
363
+ expect(adapter).not_to be_instance_of(Flipper::Adapters::ActorLimit)
364
+ end
365
+ end
366
+
285
367
  # Add app initializer in the same order as config/initializers/*
286
368
  def initializer(&block)
287
369
  application.initializer 'spec', before: :load_config_initializers do
@@ -1,6 +1,6 @@
1
1
  require 'logger'
2
- require 'flipper/adapters/instrumented'
3
2
  require 'flipper/instrumentation/log_subscriber'
3
+ require 'flipper/adapters/instrumented'
4
4
 
5
5
  begin
6
6
  require 'active_support/isolated_execution_state'
@@ -8,6 +8,9 @@ rescue LoadError
8
8
  # ActiveSupport::IsolatedExecutionState is only available in Rails 5.2+
9
9
  end
10
10
 
11
+ # Don't log in other tests, we'll manually re-attach when this one starts
12
+ Flipper::Instrumentation::LogSubscriber.detach
13
+
11
14
  RSpec.describe Flipper::Instrumentation::LogSubscriber do
12
15
  let(:adapter) do
13
16
  memory = Flipper::Adapters::Memory.new
@@ -32,8 +35,12 @@ RSpec.describe Flipper::Instrumentation::LogSubscriber do
32
35
  described_class.logger = nil
33
36
  end
34
37
 
38
+ before(:all) do
39
+ described_class.attach
40
+ end
41
+
35
42
  after(:all) do
36
- ActiveSupport::Notifications.unsubscribe("flipper")
43
+ described_class.detach
37
44
  end
38
45
 
39
46
  let(:log) { @io.string }
@@ -458,7 +458,7 @@ RSpec.describe Flipper::Middleware::Memoizer do
458
458
  logged_memory = Flipper::Adapters::OperationLogger.new(memory)
459
459
  cache = ActiveSupport::Cache::MemoryStore.new
460
460
  cache.clear
461
- cached = Flipper::Adapters::ActiveSupportCacheStore.new(logged_memory, cache, expires_in: 10)
461
+ cached = Flipper::Adapters::ActiveSupportCacheStore.new(logged_memory, cache)
462
462
  logged_cached = Flipper::Adapters::OperationLogger.new(cached)
463
463
  memo = {}
464
464
  flipper = Flipper.new(logged_cached)
@@ -471,15 +471,18 @@ RSpec.describe Flipper::Middleware::Memoizer do
471
471
 
472
472
  get '/', {}, 'flipper' => flipper
473
473
  expect(logged_cached.count(:get_all)).to be(1)
474
- expect(logged_memory.count(:get_all)).to be(1)
474
+ expect(logged_memory.count(:features)).to be(1)
475
+ expect(logged_memory.count(:get_multi)).to be(1)
475
476
 
476
477
  get '/', {}, 'flipper' => flipper
477
478
  expect(logged_cached.count(:get_all)).to be(2)
478
- expect(logged_memory.count(:get_all)).to be(1)
479
+ expect(logged_memory.count(:features)).to be(1)
480
+ expect(logged_memory.count(:get_multi)).to be(1)
479
481
 
480
482
  get '/', {}, 'flipper' => flipper
481
483
  expect(logged_cached.count(:get_all)).to be(3)
482
- expect(logged_memory.count(:get_all)).to be(1)
484
+ expect(logged_memory.count(:features)).to be(1)
485
+ expect(logged_memory.count(:get_multi)).to be(1)
483
486
  end
484
487
  end
485
488
  end
data/spec/flipper_spec.rb CHANGED
@@ -241,7 +241,7 @@ RSpec.describe Flipper do
241
241
  stub = stub_request(:get, "https://www.flippercloud.io/adapter/features?exclude_gate_names=true").
242
242
  with({
243
243
  headers: {
244
- 'Flipper-Cloud-Token'=>'asdf',
244
+ 'flipper-cloud-token'=>'asdf',
245
245
  },
246
246
  }).to_return(status: 200, body: '{"features": {}}', headers: {})
247
247
  cloud_configuration = Flipper::Cloud::Configuration.new({
data/spec/spec_helper.rb CHANGED
@@ -18,6 +18,7 @@ require 'flipper'
18
18
  require 'flipper/api'
19
19
  require 'flipper/spec/shared_adapter_specs'
20
20
  require 'flipper/ui'
21
+ require 'flipper/test_help'
21
22
 
22
23
  Dir[FlipperRoot.join('spec/support/**/*.rb')].sort.each { |f| require f }
23
24
 
@@ -0,0 +1,8 @@
1
+ if ENV["CI"] || ENV["FAIL_ON_OUTPUT"]
2
+ RSpec.configure do |config|
3
+ config.around do |example|
4
+ output = silence { example.run }
5
+ fail "Use `silence { }` to avoid printing to STDOUT/STDERR\n#{output}" unless output.empty?
6
+ end
7
+ end
8
+ end
@@ -1,8 +1,11 @@
1
1
  require 'ice_age'
2
2
  require 'json'
3
3
  require 'rack/test'
4
+ require 'rack/session'
4
5
 
5
6
  module SpecHelpers
7
+ extend self
8
+
6
9
  def self.included(base)
7
10
  base.let(:flipper) { build_flipper }
8
11
  base.let(:app) { build_app(flipper) }
@@ -10,7 +13,7 @@ module SpecHelpers
10
13
 
11
14
  def build_app(flipper, options = {})
12
15
  Flipper::UI.app(flipper, options) do |builder|
13
- builder.use Rack::Session::Cookie, secret: 'test'
16
+ builder.use Rack::Session::Cookie, secret: 'test' * 16 # Rack 3+ wants a 64-character secret
14
17
  end
15
18
  end
16
19
 
@@ -27,7 +30,11 @@ module SpecHelpers
27
30
  end
28
31
 
29
32
  def json_response
30
- JSON.parse(last_response.body)
33
+ body = last_response.body
34
+ if last_response["content-encoding"] == 'gzip'
35
+ body = Flipper::Typecast.from_gzip(body)
36
+ end
37
+ JSON.parse(body)
31
38
  end
32
39
 
33
40
  def api_error_code_reference_url
@@ -76,11 +83,11 @@ module SpecHelpers
76
83
 
77
84
  yield
78
85
 
79
- $stderr = original_stderr
80
- $stdout = original_stdout
81
-
82
86
  # Return output
83
87
  output.string
88
+ ensure
89
+ $stderr = original_stderr
90
+ $stdout = original_stdout
84
91
  end
85
92
  end
86
93
 
@@ -0,0 +1,20 @@
1
+ require "test_helper"
2
+ require "flipper/test/shared_adapter_test"
3
+ require "flipper/adapters/actor_limit"
4
+
5
+ class Flipper::Adapters::ActorLimitTest < MiniTest::Test
6
+ prepend Flipper::Test::SharedAdapterTests
7
+
8
+ def setup
9
+ @memory = Flipper::Adapters::Memory.new
10
+ @adapter = Flipper::Adapters::ActorLimit.new(@memory, 5)
11
+ end
12
+
13
+ def test_enable_fails_when_limit_exceeded
14
+ 5.times { |i| @feature.enable Flipper::Actor.new("User;#{i}") }
15
+
16
+ assert_raises Flipper::Adapters::ActorLimit::LimitExceeded do
17
+ @feature.enable Flipper::Actor.new("User;6")
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,64 @@
1
+ require "helper"
2
+ require "generators/flipper/setup_generator"
3
+
4
+ class SetupGeneratorTest < Rails::Generators::TestCase
5
+ tests Flipper::Generators::SetupGenerator
6
+ ROOT = File.expand_path("../../../tmp/generators", __dir__)
7
+ destination ROOT
8
+ setup :prepare_destination
9
+
10
+ test "invokes flipper:active_record generator if ActiveRecord adapter defined" do
11
+ begin
12
+ load 'flipper/adapters/active_record.rb'
13
+ run_generator
14
+ assert_migration "db/migrate/create_flipper_tables.rb"
15
+ ensure
16
+ Flipper::Adapters.send(:remove_const, :ActiveRecord)
17
+ end
18
+ end
19
+
20
+ test "does not invoke flipper:active_record generator if ActiveRecord adapter not defined" do
21
+ # Ensure adapter not defined
22
+ Flipper::Adapters.send(:remove_const, :ActiveRecord) rescue nil
23
+
24
+ run_generator
25
+ assert_no_migration "db/migrate/create_flipper_tables.rb"
26
+ end
27
+
28
+ %w(.env.development .env.local .env).each do |file|
29
+ test "configures Flipper Cloud token in #{file} if it exists" do
30
+ File.write("#{ROOT}/#{file}", "")
31
+ run_generator %w(--token abc123)
32
+ assert_file file, /^FLIPPER_CLOUD_TOKEN=abc123$/m
33
+ end
34
+ end
35
+
36
+ test "configures Flipper Cloud token in .env.development before .env" do
37
+ File.write("#{ROOT}/.env.development", "")
38
+ File.write("#{ROOT}/.env", "")
39
+
40
+ run_generator %w(--token abc123)
41
+ assert_file ".env.development", /^FLIPPER_CLOUD_TOKEN=abc123$/m
42
+ assert_file ".env", ""
43
+ end
44
+
45
+ test "does not write to .env if no token provided" do
46
+ File.write("#{ROOT}/.env", "")
47
+ run_generator
48
+ assert_file ".env", ""
49
+ end
50
+
51
+ test "configures Flipper Cloud token in config/credentials.yml.enc if credentials.yml.enc exist" do
52
+ Dir.chdir(ROOT) do
53
+ FileUtils.mkdir_p("config")
54
+ ENV["RAILS_MASTER_KEY"] = "a" * 32
55
+ Rails.application = Class.new(Rails::Application)
56
+ Rails.application.credentials.write("")
57
+
58
+ run_generator %w(--token abc123)
59
+ assert_file "config/credentials.yml.enc"
60
+ expected_config = { flipper: { cloud_token: "abc123" } }
61
+ assert_equal expected_config, Rails.application.credentials.config
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,51 @@
1
+ require_relative "../helper"
2
+
3
+ # Not worth trying to test on old Rails versions
4
+ return unless Rails::VERSION::MAJOR >= 7
5
+
6
+ require "capybara/cuprite"
7
+ require "flipper"
8
+ require "flipper/test_help"
9
+
10
+ require 'action_dispatch/system_testing/server'
11
+ ActionDispatch::SystemTesting::Server.silence_puma = true
12
+
13
+ class TestApp < Rails::Application
14
+ config.load_defaults "#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}"
15
+ config.eager_load = false
16
+ config.logger = ActiveSupport::Logger.new(StringIO.new)
17
+ routes.append do
18
+ root to: "features#index"
19
+ end
20
+ end
21
+
22
+ TestApp.initialize!
23
+
24
+ class FeaturesController < ActionController::Base
25
+ def index
26
+ render json: Flipper.enabled?(:test) ? "Enabled" : "Disabled"
27
+ end
28
+ end
29
+
30
+ class TestHelpTest < ActionDispatch::SystemTestCase
31
+ # Any driver that runs the app in a separate thread will test what we want here.
32
+ driven_by :cuprite, options: { process_timeout: 60 }
33
+
34
+ setup do
35
+ # Reconfigure Flipper since other tests change the adapter.
36
+ flipper_configure
37
+
38
+ # Ensure this test uses this app instance
39
+ Rails.application = TestApp.instance
40
+ end
41
+
42
+ test "configures a shared adapter between tests and app" do
43
+ Flipper.disable(:test)
44
+ visit "/"
45
+ assert_selector "*", text: "Disabled"
46
+
47
+ Flipper.enable(:test)
48
+ visit "/"
49
+ assert_selector "*", text: "Enabled"
50
+ end
51
+ end