flipper 1.2.1 → 1.3.0.pre

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 (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