flipper 1.2.1 → 1.3.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -1
  3. data/.github/workflows/examples.yml +1 -1
  4. data/Gemfile +0 -1
  5. data/README.md +1 -0
  6. data/lib/flipper/adapters/cache_base.rb +143 -0
  7. data/lib/flipper/adapters/operation_logger.rb +18 -88
  8. data/lib/flipper/adapters/read_only.rb +6 -39
  9. data/lib/flipper/adapters/strict.rb +5 -10
  10. data/lib/flipper/adapters/wrapper.rb +54 -0
  11. data/lib/flipper/cli.rb +38 -15
  12. data/lib/flipper/cloud/configuration.rb +2 -3
  13. data/lib/flipper/cloud/telemetry/instrumenter.rb +4 -8
  14. data/lib/flipper/cloud/telemetry.rb +10 -2
  15. data/lib/flipper/poller.rb +6 -5
  16. data/lib/flipper/serializers/gzip.rb +3 -5
  17. data/lib/flipper/serializers/json.rb +3 -5
  18. data/lib/flipper/spec/shared_adapter_specs.rb +17 -16
  19. data/lib/flipper/test/shared_adapter_test.rb +17 -17
  20. data/lib/flipper/test_help.rb +15 -10
  21. data/lib/flipper/typecast.rb +3 -3
  22. data/lib/flipper/version.rb +1 -1
  23. data/lib/flipper.rb +1 -0
  24. data/package-lock.json +41 -0
  25. data/package.json +10 -0
  26. data/spec/flipper/adapters/http_spec.rb +11 -2
  27. data/spec/flipper/cli_spec.rb +23 -23
  28. data/spec/flipper/cloud/configuration_spec.rb +29 -37
  29. data/spec/flipper/cloud/telemetry/backoff_policy_spec.rb +8 -9
  30. data/spec/flipper/cloud/telemetry_spec.rb +52 -0
  31. data/spec/flipper/cloud_spec.rb +6 -5
  32. data/spec/flipper/engine_spec.rb +39 -49
  33. data/spec/flipper/middleware/memoizer_spec.rb +7 -4
  34. data/spec/support/fail_on_output.rb +8 -0
  35. data/spec/support/spec_helpers.rb +2 -1
  36. data/test_rails/system/test_help_test.rb +8 -3
  37. metadata +9 -5
  38. data/spec/support/climate_control.rb +0 -7
@@ -160,8 +160,16 @@ module Flipper
160
160
  # thus may have a telemetry-interval header for us to respect.
161
161
  response ||= error.response if error && error.respond_to?(:response)
162
162
 
163
- if response && interval = response["telemetry-interval"]
164
- self.interval = interval.to_f
163
+ if response
164
+ if Flipper::Typecast.to_boolean(response["telemetry-shutdown"])
165
+ debug "action=telemetry_shutdown message=The server has requested that telemetry be shut down."
166
+ stop
167
+ return
168
+ end
169
+
170
+ if interval = response["telemetry-interval"]
171
+ self.interval = interval.to_f
172
+ end
165
173
  end
166
174
  rescue => error
167
175
  error "action=post_to_cloud error=#{error.inspect}"
@@ -20,6 +20,8 @@ module Flipper
20
20
  instances.each {|_, instance| instance.stop }.clear
21
21
  end
22
22
 
23
+ MINIMUM_POLL_INTERVAL = 10
24
+
23
25
  def initialize(options = {})
24
26
  @thread = nil
25
27
  @pid = Process.pid
@@ -30,9 +32,9 @@ module Flipper
30
32
  @last_synced_at = Concurrent::AtomicFixnum.new(0)
31
33
  @adapter = Adapters::Memory.new(nil, threadsafe: true)
32
34
 
33
- if @interval < 1
34
- warn "Flipper::Cloud poll interval must be greater than or equal to 1 but was #{@interval}. Setting @interval to 1."
35
- @interval = 1
35
+ if @interval < MINIMUM_POLL_INTERVAL
36
+ warn "Flipper::Cloud poll interval must be greater than or equal to #{MINIMUM_POLL_INTERVAL} but was #{@interval}. Setting @interval to #{MINIMUM_POLL_INTERVAL}."
37
+ @interval = MINIMUM_POLL_INTERVAL
36
38
  end
37
39
 
38
40
  @start_automatically = options.fetch(:start_automatically, true)
@@ -64,8 +66,7 @@ module Flipper
64
66
  # you can instrument these using poller.flipper
65
67
  end
66
68
 
67
- sleep_interval = interval - (Concurrent.monotonic_time - start)
68
- sleep sleep_interval if sleep_interval.positive?
69
+ sleep interval
69
70
  end
70
71
  end
71
72
 
@@ -3,10 +3,8 @@ require "stringio"
3
3
 
4
4
  module Flipper
5
5
  module Serializers
6
- module Gzip
7
- module_function
8
-
9
- def serialize(source)
6
+ class Gzip
7
+ def self.serialize(source)
10
8
  return if source.nil?
11
9
  output = StringIO.new
12
10
  gz = Zlib::GzipWriter.new(output)
@@ -15,7 +13,7 @@ module Flipper
15
13
  output.string
16
14
  end
17
15
 
18
- def deserialize(source)
16
+ def self.deserialize(source)
19
17
  return if source.nil?
20
18
  Zlib::GzipReader.wrap(StringIO.new(source), &:read)
21
19
  end
@@ -2,15 +2,13 @@ require "json"
2
2
 
3
3
  module Flipper
4
4
  module Serializers
5
- module Json
6
- module_function
7
-
8
- def serialize(source)
5
+ class Json
6
+ def self.serialize(source)
9
7
  return if source.nil?
10
8
  JSON.generate(source)
11
9
  end
12
10
 
13
- def deserialize(source)
11
+ def self.deserialize(source)
14
12
  return if source.nil?
15
13
  JSON.parse(source)
16
14
  end
@@ -108,19 +108,19 @@ RSpec.shared_examples_for 'a flipper adapter' do
108
108
  actor22 = Flipper::Actor.new('22')
109
109
  actor_asdf = Flipper::Actor.new('asdf')
110
110
 
111
- expect(subject.enable(feature, actor_gate, Flipper::Types::Actor.new(actor22))).to eq(true)
112
- expect(subject.enable(feature, actor_gate, Flipper::Types::Actor.new(actor_asdf))).to eq(true)
111
+ expect(feature.enable(actor22)).to be(true)
112
+ expect(feature.enable(actor_asdf)).to be(true)
113
113
 
114
- result = subject.get(feature)
115
- expect(result[:actors]).to eq(Set['22', 'asdf'])
114
+ expect(feature).to be_enabled(actor22)
115
+ expect(feature).to be_enabled(actor_asdf)
116
116
 
117
- expect(subject.disable(feature, actor_gate, Flipper::Types::Actor.new(actor22))).to eq(true)
118
- result = subject.get(feature)
119
- expect(result[:actors]).to eq(Set['asdf'])
117
+ expect(feature.disable(actor22)).to be(true)
118
+ expect(feature).not_to be_enabled(actor22)
119
+ expect(feature).to be_enabled(actor_asdf)
120
120
 
121
- expect(subject.disable(feature, actor_gate, Flipper::Types::Actor.new(actor_asdf))).to eq(true)
122
- result = subject.get(feature)
123
- expect(result[:actors]).to eq(Set.new)
121
+ expect(feature.disable(actor_asdf)).to eq(true)
122
+ expect(feature).not_to be_enabled(actor22)
123
+ expect(feature).not_to be_enabled(actor_asdf)
124
124
  end
125
125
 
126
126
  it 'can enable, disable and get value for percentage of actors gate' do
@@ -182,9 +182,10 @@ RSpec.shared_examples_for 'a flipper adapter' do
182
182
  end
183
183
 
184
184
  it 'converts the actor value to a string' do
185
- expect(subject.enable(feature, actor_gate, Flipper::Types::Actor.new(Flipper::Actor.new(22)))).to eq(true)
186
- result = subject.get(feature)
187
- expect(result[:actors]).to eq(Set['22'])
185
+ actor = Flipper::Actor.new(22)
186
+ expect(feature).not_to be_enabled(actor)
187
+ feature.enable_actor actor
188
+ expect(feature).to be_enabled(actor)
188
189
  end
189
190
 
190
191
  it 'converts group value to a string' do
@@ -295,9 +296,9 @@ RSpec.shared_examples_for 'a flipper adapter' do
295
296
 
296
297
  it 'can double enable an actor without error' do
297
298
  actor = Flipper::Actor.new('Flipper::Actor;22')
298
- expect(subject.enable(feature, actor_gate, Flipper::Types::Actor.new(actor))).to eq(true)
299
- expect(subject.enable(feature, actor_gate, Flipper::Types::Actor.new(actor))).to eq(true)
300
- expect(subject.get(feature).fetch(:actors)).to eq(Set['Flipper::Actor;22'])
299
+ expect(feature.enable(actor)).to be(true)
300
+ expect(feature.enable(actor)).to be(true)
301
+ expect(feature).to be_enabled(actor)
301
302
  end
302
303
 
303
304
  it 'can double enable a group without error' do
@@ -104,19 +104,19 @@ module Flipper
104
104
  actor22 = Flipper::Actor.new('22')
105
105
  actor_asdf = Flipper::Actor.new('asdf')
106
106
 
107
- assert_equal true, @adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(actor22))
108
- assert_equal true, @adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(actor_asdf))
107
+ assert_equal true, @feature.enable(actor22)
108
+ assert_equal true, @feature.enable(actor_asdf)
109
109
 
110
- result = @adapter.get(@feature)
111
- assert_equal Set['22', 'asdf'], result[:actors]
110
+ assert @feature.enabled?(actor22)
111
+ assert @feature.enabled?(actor_asdf)
112
112
 
113
- assert true, @adapter.disable(@feature, @actor_gate, Flipper::Types::Actor.new(actor22))
114
- result = @adapter.get(@feature)
115
- assert_equal Set['asdf'], result[:actors]
113
+ assert_equal true, @feature.disable(actor22)
114
+ refute @feature.enabled?(actor22)
115
+ assert @feature.enabled?(actor_asdf)
116
116
 
117
- assert_equal true, @adapter.disable(@feature, @actor_gate, Flipper::Types::Actor.new(actor_asdf))
118
- result = @adapter.get(@feature)
119
- assert_equal Set.new, result[:actors]
117
+ assert_equal true, @feature.disable(actor_asdf)
118
+ refute @feature.enabled?(actor22)
119
+ refute @feature.enabled?(actor_asdf)
120
120
  end
121
121
 
122
122
  def test_can_enable_disable_get_value_for_percentage_of_actors_gate
@@ -178,10 +178,10 @@ module Flipper
178
178
  end
179
179
 
180
180
  def test_converts_the_actor_value_to_a_string
181
- assert_equal true,
182
- @adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(Flipper::Actor.new(22)))
183
- result = @adapter.get(@feature)
184
- assert_equal Set['22'], result[:actors]
181
+ actor = Flipper::Actor.new(22)
182
+ refute @feature.enabled?(actor)
183
+ @feature.enable_actor actor
184
+ assert @feature.enabled?(actor)
185
185
  end
186
186
 
187
187
  def test_converts_group_value_to_a_string
@@ -292,9 +292,9 @@ module Flipper
292
292
 
293
293
  def test_can_double_enable_an_actor_without_error
294
294
  actor = Flipper::Actor.new('Flipper::Actor;22')
295
- assert_equal true, @adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(actor))
296
- assert_equal true, @adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(actor))
297
- assert_equal Set['Flipper::Actor;22'], @adapter.get(@feature).fetch(:actors)
295
+ assert_equal true, @feature.enable(actor)
296
+ assert_equal true, @feature.enable(actor)
297
+ assert @feature.enabled?(actor)
298
298
  end
299
299
 
300
300
  def test_can_double_enable_a_group_without_error
@@ -1,17 +1,24 @@
1
1
  module Flipper
2
2
  module TestHelp
3
+ extend self
4
+
3
5
  def flipper_configure
4
- # Create a single shared memory adapter instance for each test
5
- @flipper_adapter = Flipper::Adapters::Memory.new
6
+ # Use a shared Memory adapter for all tests. This is instantiated outside of the
7
+ # `configure` block so the same instance is returned in new threads.
8
+ adapter = Flipper::Adapters::Memory.new
6
9
 
7
10
  Flipper.configure do |config|
8
- config.adapter { @flipper_adapter }
11
+ config.adapter { adapter }
9
12
  config.default { Flipper.new(config.adapter) }
10
13
  end
11
14
  end
12
15
 
13
16
  def flipper_reset
14
- Flipper.instance = nil # Reset previous flipper instance
17
+ # Remove all features
18
+ Flipper.features.each(&:remove) rescue nil
19
+
20
+ # Reset previous DSL instance
21
+ Flipper.instance = nil
15
22
  end
16
23
  end
17
24
  end
@@ -19,19 +26,17 @@ end
19
26
  if defined?(RSpec) && RSpec.respond_to?(:configure)
20
27
  RSpec.configure do |config|
21
28
  config.include Flipper::TestHelp
22
- config.before(:each) do
23
- flipper_reset
24
- flipper_configure
25
- end
29
+ config.before(:suite) { Flipper::TestHelp.flipper_configure }
30
+ config.before(:each) { flipper_reset }
26
31
  end
27
32
  end
28
-
29
33
  if defined?(ActiveSupport)
30
34
  ActiveSupport.on_load(:active_support_test_case) do
35
+ Flipper::TestHelp.flipper_configure
36
+
31
37
  ActiveSupport::TestCase.class_eval do
32
38
  include Flipper::TestHelp
33
39
 
34
- setup :flipper_configure
35
40
  setup :flipper_reset
36
41
  end
37
42
  end
@@ -3,8 +3,8 @@ require "flipper/serializers/json"
3
3
  require "flipper/serializers/gzip"
4
4
 
5
5
  module Flipper
6
- module Typecast
7
- TruthMap = {
6
+ class Typecast
7
+ TRUTH_MAP = {
8
8
  true => true,
9
9
  1 => true,
10
10
  'true' => true,
@@ -15,7 +15,7 @@ module Flipper
15
15
  #
16
16
  # Returns true or false.
17
17
  def self.to_boolean(value)
18
- !!TruthMap[value]
18
+ !!TRUTH_MAP[value]
19
19
  end
20
20
 
21
21
  # Internal: Convert value to an integer.
@@ -1,5 +1,5 @@
1
1
  module Flipper
2
- VERSION = '1.2.1'.freeze
2
+ VERSION = '1.3.0.pre'.freeze
3
3
 
4
4
  REQUIRED_RUBY_VERSION = '2.6'.freeze
5
5
  NEXT_REQUIRED_RUBY_VERSION = '3.0'.freeze
data/lib/flipper.rb CHANGED
@@ -174,6 +174,7 @@ end
174
174
 
175
175
  require 'flipper/actor'
176
176
  require 'flipper/adapter'
177
+ require 'flipper/adapters/wrapper'
177
178
  require 'flipper/adapters/memoizable'
178
179
  require 'flipper/adapters/memory'
179
180
  require 'flipper/adapters/strict'
data/package-lock.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "flipper",
3
+ "lockfileVersion": 3,
4
+ "requires": true,
5
+ "packages": {
6
+ "": {
7
+ "hasInstallScript": true,
8
+ "dependencies": {
9
+ "@popperjs/core": "^2.11.8",
10
+ "bootstrap": "^5.3.3"
11
+ }
12
+ },
13
+ "node_modules/@popperjs/core": {
14
+ "version": "2.11.8",
15
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
16
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
17
+ "funding": {
18
+ "type": "opencollective",
19
+ "url": "https://opencollective.com/popperjs"
20
+ }
21
+ },
22
+ "node_modules/bootstrap": {
23
+ "version": "5.3.3",
24
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
25
+ "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
26
+ "funding": [
27
+ {
28
+ "type": "github",
29
+ "url": "https://github.com/sponsors/twbs"
30
+ },
31
+ {
32
+ "type": "opencollective",
33
+ "url": "https://opencollective.com/bootstrap"
34
+ }
35
+ ],
36
+ "peerDependencies": {
37
+ "@popperjs/core": "^2.11.8"
38
+ }
39
+ }
40
+ }
41
+ }
data/package.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "private": true,
3
+ "dependencies": {
4
+ "@popperjs/core": "^2.11.8",
5
+ "bootstrap": "^5.3.3"
6
+ },
7
+ "scripts": {
8
+ "postinstall": "script/vendor-assets"
9
+ }
10
+ }
@@ -1,6 +1,15 @@
1
1
  require 'flipper/adapters/http'
2
2
  require 'flipper/adapters/pstore'
3
- require 'rack/handler/webrick'
3
+
4
+ rack_handler = begin
5
+ # Rack 3+
6
+ require 'rackup/handler/webrick'
7
+ Rackup::Handler::WEBrick
8
+ rescue LoadError
9
+ require 'rack/handler/webrick'
10
+ Rack::Handler::WEBrick
11
+ end
12
+
4
13
 
5
14
  FLIPPER_SPEC_API_PORT = ENV.fetch('FLIPPER_SPEC_API_PORT', 9001).to_i
6
15
 
@@ -36,7 +45,7 @@ RSpec.describe Flipper::Adapters::Http do
36
45
  ],
37
46
  }
38
47
  @server = WEBrick::HTTPServer.new(server_options)
39
- @server.mount '/', Rack::Handler::WEBrick, app
48
+ @server.mount '/', rack_handler, app
40
49
 
41
50
  Thread.new { @server.start }
42
51
  Timeout.timeout(1) { :wait until @started }
@@ -1,13 +1,33 @@
1
1
  require "flipper/cli"
2
2
 
3
3
  RSpec.describe Flipper::CLI do
4
+ let(:stdout) { StringIO.new }
5
+ let(:stderr) { StringIO.new }
6
+ let(:cli) { Flipper::CLI.new(stdout: stdout, stderr: stderr) }
7
+
8
+ before do
9
+ # Prentend stdout/stderr a TTY to test colorization
10
+ allow(stdout).to receive(:tty?).and_return(true)
11
+ allow(stderr).to receive(:tty?).and_return(true)
12
+ end
13
+
4
14
  # Infer the command from the description
5
15
  subject(:argv) do
6
16
  descriptions = self.class.parent_groups.map {|g| g.metadata[:description_args] }.reverse.flatten.drop(1)
7
17
  descriptions.map { |arg| Shellwords.split(arg) }.flatten
8
18
  end
9
19
 
10
- subject { run argv }
20
+ subject do
21
+ status = 0
22
+
23
+ begin
24
+ cli.run(argv)
25
+ rescue SystemExit => e
26
+ status = e.status
27
+ end
28
+
29
+ OpenStruct.new(status: status, stdout: stdout.string, stderr: stderr.string)
30
+ end
11
31
 
12
32
  before do
13
33
  ENV["FLIPPER_REQUIRE"] = "./spec/fixtures/environment"
@@ -16,14 +36,14 @@ RSpec.describe Flipper::CLI do
16
36
  describe "enable" do
17
37
  describe "feature" do
18
38
  it do
19
- expect(subject).to have_attributes(status: 0, stdout: /feature.*enabled/)
39
+ expect(subject).to have_attributes(status: 0, stdout: /feature.*\e\[32m.*enabled/)
20
40
  expect(Flipper).to be_enabled(:feature)
21
41
  end
22
42
  end
23
43
 
24
44
  describe "-a User;1 feature" do
25
45
  it do
26
- expect(subject).to have_attributes(status: 0, stdout: /feature.*enabled.*User;1/m)
46
+ expect(subject).to have_attributes(status: 0, stdout: /feature.*\e\[33m.*enabled.*User;1/m)
27
47
  expect(Flipper).to be_enabled(:feature, Flipper::Actor.new("User;1"))
28
48
  end
29
49
  end
@@ -141,24 +161,4 @@ RSpec.describe Flipper::CLI do
141
161
  it { should have_attributes(status: 0, stdout: /enabled.*admins/m) }
142
162
  end
143
163
  end
144
-
145
- def run(argv)
146
- original_stdout = $stdout
147
- original_stderr = $stderr
148
-
149
- $stdout = StringIO.new
150
- $stderr = StringIO.new
151
- status = 0
152
-
153
- begin
154
- Flipper::CLI.run(argv)
155
- rescue SystemExit => e
156
- status = e.status
157
- end
158
-
159
- OpenStruct.new(status: status, stdout: $stdout.string, stderr: $stderr.string)
160
- ensure
161
- $stdout = original_stdout
162
- $stderr = original_stderr
163
- end
164
164
  end
@@ -12,16 +12,16 @@ 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
22
21
  instrumenter = Object.new
23
22
  instance = described_class.new(required_options.merge(instrumenter: instrumenter))
24
- expect(instance.instrumenter).to be(instrumenter)
23
+ expect(instance.instrumenter).to be_a(Flipper::Cloud::Telemetry::Instrumenter)
24
+ expect(instance.instrumenter.instrumenter).to be(instrumenter)
25
25
  end
26
26
 
27
27
  it "can set read_timeout" do
@@ -30,10 +30,9 @@ RSpec.describe Flipper::Cloud::Configuration do
30
30
  end
31
31
 
32
32
  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
33
+ ENV["FLIPPER_CLOUD_READ_TIMEOUT"] = "9"
34
+ instance = described_class.new(required_options.reject { |k, v| k == :read_timeout })
35
+ expect(instance.read_timeout).to eq(9)
37
36
  end
38
37
 
39
38
  it "can set open_timeout" do
@@ -42,10 +41,9 @@ RSpec.describe Flipper::Cloud::Configuration do
42
41
  end
43
42
 
44
43
  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
44
+ ENV["FLIPPER_CLOUD_OPEN_TIMEOUT"] = "9"
45
+ instance = described_class.new(required_options.reject { |k, v| k == :open_timeout })
46
+ expect(instance.open_timeout).to eq(9)
49
47
  end
50
48
 
51
49
  it "can set write_timeout" do
@@ -54,10 +52,9 @@ RSpec.describe Flipper::Cloud::Configuration do
54
52
  end
55
53
 
56
54
  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
55
+ ENV["FLIPPER_CLOUD_WRITE_TIMEOUT"] = "9"
56
+ instance = described_class.new(required_options.reject { |k, v| k == :write_timeout })
57
+ expect(instance.write_timeout).to eq(9)
61
58
  end
62
59
 
63
60
  it "can set sync_interval" do
@@ -66,10 +63,9 @@ RSpec.describe Flipper::Cloud::Configuration do
66
63
  end
67
64
 
68
65
  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
66
+ ENV["FLIPPER_CLOUD_SYNC_INTERVAL"] = "15"
67
+ instance = described_class.new(required_options.reject { |k, v| k == :sync_interval })
68
+ expect(instance.sync_interval).to eq(15)
73
69
  end
74
70
 
75
71
  it "passes sync_interval into sync adapter" do
@@ -87,10 +83,9 @@ RSpec.describe Flipper::Cloud::Configuration do
87
83
  end
88
84
 
89
85
  it "defaults debug_output to STDOUT if FLIPPER_CLOUD_DEBUG_OUTPUT_STDOUT set to true" do
90
- with_env "FLIPPER_CLOUD_DEBUG_OUTPUT_STDOUT" => "true" do
91
- instance = described_class.new(required_options)
86
+ ENV["FLIPPER_CLOUD_DEBUG_OUTPUT_STDOUT"] = "true"
87
+ instance = described_class.new(required_options)
92
88
  expect(instance.debug_output).to eq(STDOUT)
93
- end
94
89
  end
95
90
 
96
91
  it "defaults adapter block" do
@@ -128,10 +123,9 @@ RSpec.describe Flipper::Cloud::Configuration do
128
123
  end
129
124
 
130
125
  it "can override URL using ENV var" do
131
- with_env "FLIPPER_CLOUD_URL" => "https://example.com" do
132
- instance = described_class.new(required_options.reject { |k, v| k == :url })
133
- expect(instance.url).to eq("https://example.com")
134
- end
126
+ ENV["FLIPPER_CLOUD_URL"] = "https://example.com"
127
+ instance = described_class.new(required_options.reject { |k, v| k == :url })
128
+ expect(instance.url).to eq("https://example.com")
135
129
  end
136
130
 
137
131
  it "defaults sync_method to :poll" do
@@ -150,12 +144,11 @@ RSpec.describe Flipper::Cloud::Configuration do
150
144
  end
151
145
 
152
146
  it "sets sync_method to :webhook if FLIPPER_CLOUD_SYNC_SECRET set" do
153
- with_env "FLIPPER_CLOUD_SYNC_SECRET" => "abc" do
154
- instance = described_class.new(required_options)
147
+ ENV["FLIPPER_CLOUD_SYNC_SECRET"] = "abc"
148
+ instance = described_class.new(required_options)
155
149
 
156
- expect(instance.sync_method).to eq(:webhook)
157
- expect(instance.adapter).to be_instance_of(Flipper::Adapters::DualWrite)
158
- end
150
+ expect(instance.sync_method).to eq(:webhook)
151
+ expect(instance.adapter).to be_instance_of(Flipper::Adapters::DualWrite)
159
152
  end
160
153
 
161
154
  it "can set sync_secret" do
@@ -164,10 +157,9 @@ RSpec.describe Flipper::Cloud::Configuration do
164
157
  end
165
158
 
166
159
  it "can override sync_secret using ENV var" do
167
- with_env "FLIPPER_CLOUD_SYNC_SECRET" => "from_env" do
168
- instance = described_class.new(required_options.reject { |k, v| k == :sync_secret })
169
- expect(instance.sync_secret).to eq("from_env")
170
- end
160
+ ENV["FLIPPER_CLOUD_SYNC_SECRET"] = "from_env"
161
+ instance = described_class.new(required_options.reject { |k, v| k == :sync_secret })
162
+ expect(instance.sync_secret).to eq("from_env")
171
163
  end
172
164
 
173
165
  it "can sync with cloud" do
@@ -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