flipflop 2.0.0

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 (59) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +5 -0
  3. data/.travis.yml +51 -0
  4. data/Gemfile +20 -0
  5. data/LICENSE +22 -0
  6. data/README.md +261 -0
  7. data/Rakefile +16 -0
  8. data/app/assets/stylesheets/flipflop.scss +109 -0
  9. data/app/controllers/concerns/flipflop/environment_filters.rb +5 -0
  10. data/app/controllers/flipflop/features_controller.rb +59 -0
  11. data/app/controllers/flipflop/strategies_controller.rb +30 -0
  12. data/app/models/flipflop/feature.rb +3 -0
  13. data/app/views/flipflop/features/index.html.erb +60 -0
  14. data/app/views/layouts/flipflop.html.erb +1 -0
  15. data/config/routes.rb +5 -0
  16. data/flipflop.gemspec +23 -0
  17. data/lib/flipflop/configurable.rb +27 -0
  18. data/lib/flipflop/engine.rb +58 -0
  19. data/lib/flipflop/facade.rb +23 -0
  20. data/lib/flipflop/feature_cache.rb +64 -0
  21. data/lib/flipflop/feature_definition.rb +15 -0
  22. data/lib/flipflop/feature_set.rb +99 -0
  23. data/lib/flipflop/strategies/abstract_strategy.rb +103 -0
  24. data/lib/flipflop/strategies/active_record_strategy.rb +43 -0
  25. data/lib/flipflop/strategies/cookie_strategy.rb +44 -0
  26. data/lib/flipflop/strategies/default_strategy.rb +15 -0
  27. data/lib/flipflop/strategies/lambda_strategy.rb +25 -0
  28. data/lib/flipflop/strategies/query_string_strategy.rb +17 -0
  29. data/lib/flipflop/strategies/session_strategy.rb +29 -0
  30. data/lib/flipflop/strategies/test_strategy.rb +40 -0
  31. data/lib/flipflop/version.rb +3 -0
  32. data/lib/flipflop.rb +26 -0
  33. data/lib/generators/flipflop/features/USAGE +8 -0
  34. data/lib/generators/flipflop/features/features_generator.rb +7 -0
  35. data/lib/generators/flipflop/features/templates/features.rb +21 -0
  36. data/lib/generators/flipflop/install/install_generator.rb +21 -0
  37. data/lib/generators/flipflop/migration/USAGE +5 -0
  38. data/lib/generators/flipflop/migration/migration_generator.rb +23 -0
  39. data/lib/generators/flipflop/migration/templates/create_features.rb +10 -0
  40. data/lib/generators/flipflop/routes/USAGE +7 -0
  41. data/lib/generators/flipflop/routes/routes_generator.rb +5 -0
  42. data/test/integration/app_test.rb +32 -0
  43. data/test/integration/dashboard_test.rb +162 -0
  44. data/test/test_helper.rb +96 -0
  45. data/test/unit/configurable_test.rb +104 -0
  46. data/test/unit/feature_cache_test.rb +142 -0
  47. data/test/unit/feature_definition_test.rb +42 -0
  48. data/test/unit/feature_set_test.rb +136 -0
  49. data/test/unit/flipflop_test.rb +99 -0
  50. data/test/unit/strategies/abstract_strategy_request_test.rb +42 -0
  51. data/test/unit/strategies/abstract_strategy_test.rb +124 -0
  52. data/test/unit/strategies/active_record_strategy_test.rb +157 -0
  53. data/test/unit/strategies/cookie_strategy_test.rb +126 -0
  54. data/test/unit/strategies/default_strategy_test.rb +44 -0
  55. data/test/unit/strategies/lambda_strategy_test.rb +137 -0
  56. data/test/unit/strategies/query_string_strategy_test.rb +70 -0
  57. data/test/unit/strategies/session_strategy_test.rb +101 -0
  58. data/test/unit/strategies/test_strategy_test.rb +76 -0
  59. metadata +134 -0
@@ -0,0 +1,136 @@
1
+ require File.expand_path("../../test_helper", __FILE__)
2
+
3
+ class NullStrategy < Flipflop::Strategies::AbstractStrategy
4
+ def enabled?(feature)
5
+ end
6
+ end
7
+
8
+ class TrueStrategy < Flipflop::Strategies::AbstractStrategy
9
+ def enabled?(feature)
10
+ true
11
+ end
12
+ end
13
+
14
+ class FalseStrategy < Flipflop::Strategies::AbstractStrategy
15
+ def enabled?(feature)
16
+ false
17
+ end
18
+ end
19
+
20
+ describe Flipflop::FeatureSet do
21
+ subject do
22
+ Flipflop::FeatureSet.current.reset!
23
+ Flipflop::FeatureSet.current.tap do |set|
24
+ set.add(Flipflop::FeatureDefinition.new(:one))
25
+ end
26
+ end
27
+
28
+ describe "current" do
29
+ it "should return same instance" do
30
+ current = subject
31
+ assert_equal current, Flipflop::FeatureSet.current
32
+ end
33
+
34
+ it "should return same instance in different thread" do
35
+ current = subject
36
+ assert_equal current, Thread.new { Flipflop::FeatureSet.current }.value
37
+ end
38
+ end
39
+
40
+ describe "test" do
41
+ it "should freeze strategies" do
42
+ subject.test!
43
+ assert_raises RuntimeError do
44
+ subject.use(Flipflop::Strategies::AbstractStrategy.new)
45
+ end
46
+ end
47
+
48
+ it "should replace strategies with test strategy" do
49
+ subject.test!
50
+ assert_equal [Flipflop::Strategies::TestStrategy], subject.strategies.map(&:class)
51
+ end
52
+
53
+ it "should replace strategies with given strategy" do
54
+ subject.test!(Flipflop::Strategies::LambdaStrategy.new)
55
+ assert_equal [Flipflop::Strategies::LambdaStrategy], subject.strategies.map(&:class)
56
+ end
57
+
58
+ it "should return test strategy" do
59
+ returned = subject.test!(strategy = Flipflop::Strategies::LambdaStrategy.new)
60
+ assert_equal strategy, returned
61
+ end
62
+ end
63
+
64
+ describe "enabled" do
65
+ it "should return false by default" do
66
+ subject.use(NullStrategy.new)
67
+ assert_equal false, subject.enabled?(:one)
68
+ end
69
+
70
+ it "should return value of next true strategy if unknown" do
71
+ subject.use(NullStrategy.new)
72
+ subject.use(TrueStrategy.new)
73
+ assert_equal true, subject.enabled?(:one)
74
+ end
75
+
76
+ it "should return value of next false strategy if unknown" do
77
+ subject.use(NullStrategy.new)
78
+ subject.use(FalseStrategy.new)
79
+ assert_equal false, subject.enabled?(:one)
80
+ end
81
+
82
+ it "should stop resolving at first true value" do
83
+ subject.use(TrueStrategy.new)
84
+ subject.use(FalseStrategy.new)
85
+ subject.use(NullStrategy.new)
86
+ assert_equal true, subject.enabled?(:one)
87
+ end
88
+
89
+ it "should stop resolving at first false value" do
90
+ subject.use(FalseStrategy.new)
91
+ subject.use(TrueStrategy.new)
92
+ subject.use(NullStrategy.new)
93
+ assert_equal false, subject.enabled?(:one)
94
+ end
95
+ end
96
+
97
+ describe "add" do
98
+ it "should add feature" do
99
+ subject.add(feature = Flipflop::FeatureDefinition.new(:feature))
100
+ assert_equal feature, subject.feature(feature.key)
101
+ end
102
+
103
+ it "should freeze feature" do
104
+ subject.add(feature = Flipflop::FeatureDefinition.new(:feature))
105
+ assert subject.feature(feature.key).frozen?
106
+ end
107
+ end
108
+
109
+ describe "use" do
110
+ it "should add strategy" do
111
+ subject.use(strategy = NullStrategy.new)
112
+ assert_equal strategy, subject.strategy(strategy.key)
113
+ end
114
+
115
+ it "should freeze strategy" do
116
+ subject.use(strategy = NullStrategy.new)
117
+ assert subject.strategy(strategy.key).frozen?
118
+ end
119
+ end
120
+
121
+ describe "feature" do
122
+ it "should raise if feature is unknown" do
123
+ assert_raises Flipflop::FeatureError do
124
+ subject.feature(:unknown)
125
+ end
126
+ end
127
+ end
128
+
129
+ describe "strategy" do
130
+ it "should raise if strategy is unknown" do
131
+ assert_raises Flipflop::StrategyError do
132
+ subject.strategy("12345")
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,99 @@
1
+ require File.expand_path("../../test_helper", __FILE__)
2
+
3
+ describe Flipflop do
4
+ before do
5
+ Flipflop.configure do
6
+ feature :one, default: true
7
+ feature :two, default: false
8
+ end
9
+ end
10
+
11
+ describe "configure" do
12
+ before do
13
+ Flipflop.configure do
14
+ feature :config_feature, default: true
15
+ end
16
+ end
17
+
18
+ it "should reset feature set" do
19
+ Flipflop.configure do
20
+ end
21
+ assert_equal [], Flipflop::FeatureSet.current.features
22
+ end
23
+
24
+ it "should add features" do
25
+ assert_equal [:config_feature],
26
+ Flipflop::FeatureSet.current.features.map(&:key)
27
+ end
28
+
29
+ it "should freeze features" do
30
+ assert_raises RuntimeError do
31
+ Flipflop::FeatureSet.current.add(Flipflop::FeatureDefinition.new(:foo))
32
+ end
33
+ end
34
+
35
+ it "should freeze strategies" do
36
+ assert_raises RuntimeError do
37
+ Flipflop::FeatureSet.current.use(Flipflop::Strategies::AbstractStrategy.new)
38
+ end
39
+ end
40
+ end
41
+
42
+ describe "enabled?" do
43
+ it "should return true for enabled features" do
44
+ assert_equal true, Flipflop.on?(:one)
45
+ end
46
+
47
+ it "should return false for disabled features" do
48
+ assert_equal false, Flipflop.on?(:two)
49
+ end
50
+
51
+ it "should call strategy once if cached" do
52
+ called = 0
53
+ counter = Class.new(Flipflop::Strategies::AbstractStrategy) do
54
+ define_method :enabled? do |feature|
55
+ called += 1
56
+ false
57
+ end
58
+ end
59
+
60
+ Flipflop.configure do
61
+ strategy counter
62
+ feature :one, default: true
63
+ end
64
+
65
+ begin
66
+ Flipflop::FeatureCache.current.enable!
67
+ Flipflop.on?(:one)
68
+ Flipflop.on?(:one)
69
+ assert_equal 1, called
70
+ ensure
71
+ Flipflop::FeatureCache.current.disable!
72
+ end
73
+ end
74
+ end
75
+
76
+ describe "dynamic predicate method" do
77
+ it "should respond to feature predicate" do
78
+ assert Flipflop.respond_to?(:one?)
79
+ end
80
+
81
+ it "should not respond to incorrectly formatted predicate" do
82
+ refute Flipflop.respond_to?(:foobar!)
83
+ end
84
+
85
+ it "should return true for enabled features" do
86
+ assert_equal true, Flipflop.one?
87
+ end
88
+
89
+ it "should return false for disabled features" do
90
+ assert_equal false, Flipflop.two?
91
+ end
92
+
93
+ it "raises error for incorrectly formatted predicate" do
94
+ assert_raises NoMethodError do
95
+ Flipflop.foobar!
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,42 @@
1
+ require File.expand_path("../../../test_helper", __FILE__)
2
+
3
+ describe Flipflop::Strategies::AbstractStrategy::RequestInterceptor do
4
+ subject do
5
+ Class.new(ActionController::Metal) do
6
+ class << self
7
+ attr_accessor :request
8
+ end
9
+
10
+ include AbstractController::Callbacks
11
+ include Flipflop::Strategies::AbstractStrategy::RequestInterceptor
12
+
13
+ def index
14
+ self.class.request = Flipflop::Strategies::AbstractStrategy::RequestInterceptor.request
15
+ end
16
+ end
17
+ end
18
+
19
+ after do
20
+ Flipflop::Strategies::AbstractStrategy::RequestInterceptor.request = nil
21
+ end
22
+
23
+ it "should add before filter to controller" do
24
+ filters = subject._process_action_callbacks.select { |f| f.kind == :before }
25
+ assert_equal 1, filters.length
26
+ end
27
+
28
+ it "should add after filter to controller" do
29
+ filters = subject._process_action_callbacks.select { |f| f.kind == :after }
30
+ assert_equal 1, filters.length
31
+ end
32
+
33
+ it "should set request" do
34
+ subject.action(:index).call({})
35
+ assert_instance_of ActionDispatch::Request, subject.request
36
+ end
37
+
38
+ it "should clear request" do
39
+ subject.action(:index).call({})
40
+ assert_nil Flipflop::Strategies::AbstractStrategy::RequestInterceptor.request
41
+ end
42
+ end
@@ -0,0 +1,124 @@
1
+ require File.expand_path("../../../test_helper", __FILE__)
2
+
3
+ describe Flipflop::Strategies::AbstractStrategy do
4
+ after do
5
+ Flipflop::Strategies::AbstractStrategy::RequestInterceptor.request = nil
6
+ end
7
+
8
+ describe "with defaults" do
9
+ subject do
10
+ Flipflop::Strategies::AbstractStrategy.new.freeze
11
+ end
12
+
13
+ it "should have default name" do
14
+ assert_equal "abstract", subject.name
15
+ end
16
+
17
+ it "should have no default description" do
18
+ assert_nil subject.description
19
+ end
20
+
21
+ it "should not be switchable" do
22
+ assert_equal false, subject.switchable?
23
+ end
24
+
25
+ it "should not be hidden" do
26
+ assert_equal false, subject.hidden?
27
+ end
28
+
29
+ it "should have unique key" do
30
+ assert_match /^\d+$/, subject.key
31
+ end
32
+
33
+ describe "request" do
34
+ it "should return request" do
35
+ Flipflop::Strategies::AbstractStrategy::RequestInterceptor.request = 3
36
+ assert_equal 3, subject.send(:request)
37
+ end
38
+
39
+ it "should raise if request is missing" do
40
+ Flipflop::Strategies::AbstractStrategy::RequestInterceptor.request = nil
41
+ assert_raises Flipflop::StrategyError do
42
+ subject.send(:request)
43
+ end
44
+ end
45
+
46
+ it "should raise with message if request is missing" do
47
+ Flipflop::Strategies::AbstractStrategy::RequestInterceptor.request = nil
48
+ message = nil
49
+ begin
50
+ subject.send(:request)
51
+ rescue => err
52
+ message = err.message
53
+ end
54
+ assert_equal "Strategy 'abstract' required request, but was used outside request context.", message
55
+ end
56
+
57
+ it "should raise if request is missing in thread" do
58
+ Flipflop::Strategies::AbstractStrategy::RequestInterceptor.request = 3
59
+ assert_raises Flipflop::StrategyError do
60
+ Thread.new { subject.send(:request) }.value
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "request predicate" do
66
+ it "should return true if request is present" do
67
+ Flipflop::Strategies::AbstractStrategy::RequestInterceptor.request = 3
68
+ assert_equal true, subject.send(:request?)
69
+ end
70
+
71
+ it "should return false if request is missing" do
72
+ Flipflop::Strategies::AbstractStrategy::RequestInterceptor.request = nil
73
+ assert_equal false, subject.send(:request?)
74
+ end
75
+ end
76
+ end
77
+
78
+ describe "with options" do
79
+ subject do
80
+ Flipflop::Strategies::AbstractStrategy.new(
81
+ name: "strategy",
82
+ description: "my strategy",
83
+ hidden: true,
84
+ ).freeze
85
+ end
86
+
87
+ it "should have specified name" do
88
+ assert_equal "strategy", subject.name
89
+ end
90
+
91
+ it "should have specified description" do
92
+ assert_equal "my strategy", subject.description
93
+ end
94
+
95
+ it "should be hidden" do
96
+ assert_equal true, subject.hidden?
97
+ end
98
+ end
99
+
100
+ describe "with unknown options" do
101
+ subject do
102
+ Flipflop::Strategies::AbstractStrategy.new(
103
+ unknown: "one",
104
+ other: "two",
105
+ ).freeze
106
+ end
107
+
108
+ it "should raise error" do
109
+ assert_raises Flipflop::StrategyError do
110
+ subject
111
+ end
112
+ end
113
+
114
+ it "should raise with message" do
115
+ message = nil
116
+ begin
117
+ subject
118
+ rescue => err
119
+ message = err.message
120
+ end
121
+ assert_equal "Strategy 'abstract' did not understand option :unknown, :other.", message
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,157 @@
1
+ require File.expand_path("../../../test_helper", __FILE__)
2
+
3
+ class ResultSet
4
+ def initialize(key, results = [])
5
+ @key, @results = key, results
6
+ end
7
+
8
+ def first_or_initialize
9
+ @results.first or My::Feature.new(@key, false)
10
+ end
11
+
12
+ def first
13
+ @results.first
14
+ end
15
+ end
16
+
17
+ module My
18
+ class Feature < Struct.new(:key, :enabled)
19
+ class << self
20
+ attr_accessor :results
21
+
22
+ def where(conditions)
23
+ results[conditions[:key].to_sym]
24
+ end
25
+ end
26
+
27
+ alias_method :enabled?, :enabled
28
+
29
+ def destroy
30
+ My::Feature.results[key] = ResultSet.new(key)
31
+ end
32
+
33
+ def save!
34
+ My::Feature.results[key] = ResultSet.new(key, [self])
35
+ end
36
+ end
37
+ end
38
+
39
+ describe Flipflop::Strategies::ActiveRecordStrategy do
40
+ describe "with defaults" do
41
+ subject do
42
+ Flipflop::Strategies::ActiveRecordStrategy.new(class: My::Feature).freeze
43
+ end
44
+
45
+ it "should have default name" do
46
+ assert_equal "active_record", subject.name
47
+ end
48
+
49
+ it "should have default description" do
50
+ assert_equal "Stores features in database. Applies to all users.",
51
+ subject.description
52
+ end
53
+
54
+ it "should be switchable" do
55
+ assert_equal true, subject.switchable?
56
+ end
57
+
58
+ it "should have unique key" do
59
+ assert_match /^\d+$/, subject.key
60
+ end
61
+
62
+ describe "with enabled feature" do
63
+ before do
64
+ My::Feature.results = {
65
+ one: ResultSet.new(:one, [My::Feature.new(:one, true)]),
66
+ }
67
+ end
68
+
69
+ it "should have feature enabled" do
70
+ assert_equal true, subject.enabled?(:one)
71
+ end
72
+
73
+ it "should be able to switch feature off" do
74
+ subject.switch!(:one, false)
75
+ assert_equal false, subject.enabled?(:one)
76
+ end
77
+
78
+ it "should be able to clear feature" do
79
+ subject.clear!(:one)
80
+ assert_nil subject.enabled?(:one)
81
+ end
82
+ end
83
+
84
+ describe "with disabled feature" do
85
+ before do
86
+ My::Feature.results = {
87
+ two: ResultSet.new(:two, [My::Feature.new(:two, false)]),
88
+ }
89
+ end
90
+
91
+ it "should not have feature enabled" do
92
+ assert_equal false, subject.enabled?(:two)
93
+ end
94
+
95
+ it "should be able to switch feature on" do
96
+ subject.switch!(:two, true)
97
+ assert_equal true, subject.enabled?(:two)
98
+ end
99
+
100
+ it "should be able to clear feature" do
101
+ subject.clear!(:two)
102
+ assert_nil subject.enabled?(:two)
103
+ end
104
+ end
105
+
106
+ describe "with unsaved feature" do
107
+ before do
108
+ My::Feature.results = {
109
+ three: ResultSet.new(:three),
110
+ }
111
+ end
112
+
113
+ it "should not know feature" do
114
+ assert_nil subject.enabled?(:three)
115
+ end
116
+
117
+ it "should be able to switch feature on" do
118
+ subject.switch!(:three, true)
119
+ assert_equal true, subject.enabled?(:three)
120
+ end
121
+ end
122
+ end
123
+
124
+ describe "with string class name" do
125
+ subject do
126
+ Flipflop::Strategies::ActiveRecordStrategy.new(class: "My::Feature").freeze
127
+ end
128
+
129
+ before do
130
+ My::Feature.results = {
131
+ one: ResultSet.new(:one, [My::Feature.new(:one, true)]),
132
+ }
133
+ end
134
+
135
+ it "should be able to switch feature off" do
136
+ subject.switch!(:one, false)
137
+ assert_equal false, subject.enabled?(:one)
138
+ end
139
+ end
140
+
141
+ describe "with symbol class name" do
142
+ subject do
143
+ Flipflop::Strategies::ActiveRecordStrategy.new(class: :"My::Feature").freeze
144
+ end
145
+
146
+ before do
147
+ My::Feature.results = {
148
+ one: ResultSet.new(:one, [My::Feature.new(:one, true)]),
149
+ }
150
+ end
151
+
152
+ it "should be able to switch feature off" do
153
+ subject.switch!(:one, false)
154
+ assert_equal false, subject.enabled?(:one)
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,126 @@
1
+ require File.expand_path("../../../test_helper", __FILE__)
2
+
3
+ describe Flipflop::Strategies::CookieStrategy do
4
+ subject do
5
+ Flipflop::Strategies::CookieStrategy.new.freeze
6
+ end
7
+
8
+ describe "in request context" do
9
+ before do
10
+ Flipflop::Strategies::AbstractStrategy::RequestInterceptor.request = create_request
11
+ end
12
+
13
+ after do
14
+ Flipflop::Strategies::AbstractStrategy::RequestInterceptor.request = nil
15
+ end
16
+
17
+ it "should have default name" do
18
+ assert_equal "cookie", subject.name
19
+ end
20
+
21
+ it "should have default description" do
22
+ assert_equal "Stores features in a browser cookie. Applies to current user.",
23
+ subject.description
24
+ end
25
+
26
+ it "should be switchable" do
27
+ assert_equal true, subject.switchable?
28
+ end
29
+
30
+ it "should have unique key" do
31
+ assert_match /^\d+$/, subject.key
32
+ end
33
+
34
+ describe "with enabled feature" do
35
+ before do
36
+ subject.send(:request).cookie_jar[subject.send(:cookie_name, :one)] = "1"
37
+ end
38
+
39
+ it "should have feature enabled" do
40
+ assert_equal true, subject.enabled?(:one)
41
+ end
42
+
43
+ it "should be able to switch feature off" do
44
+ subject.switch!(:one, false)
45
+ assert_equal false, subject.enabled?(:one)
46
+ end
47
+
48
+ it "should be able to clear feature" do
49
+ subject.clear!(:one)
50
+ assert_nil subject.enabled?(:one)
51
+ end
52
+ end
53
+
54
+ describe "with disabled feature" do
55
+ before do
56
+ subject.send(:request).cookie_jar[subject.send(:cookie_name, :two)] = "0"
57
+ end
58
+
59
+ it "should not have feature enabled" do
60
+ assert_equal false, subject.enabled?(:two)
61
+ end
62
+
63
+ it "should be able to switch feature on" do
64
+ subject.switch!(:two, true)
65
+ assert_equal true, subject.enabled?(:two)
66
+ end
67
+
68
+ it "should be able to clear feature" do
69
+ subject.clear!(:two)
70
+ assert_nil subject.enabled?(:two)
71
+ end
72
+ end
73
+
74
+ describe "with uncookied feature" do
75
+ it "should not know feature" do
76
+ assert_nil subject.enabled?(:three)
77
+ end
78
+
79
+ it "should be able to switch feature on" do
80
+ subject.switch!(:three, true)
81
+ assert_equal true, subject.enabled?(:three)
82
+ end
83
+ end
84
+
85
+ describe "with options" do
86
+ subject do
87
+ Flipflop::Strategies::CookieStrategy.new(
88
+ domain: :all,
89
+ path: "/foo",
90
+ httponly: true,
91
+ ).freeze
92
+ end
93
+
94
+ it "should pass options when setting value" do
95
+ subject.switch!(:one, true)
96
+ subject.send(:request).cookie_jar.write(headers = {})
97
+ assert_equal "flipflop_one=1; domain=.example.com; path=/foo; HttpOnly",
98
+ headers["Set-Cookie"]
99
+ end
100
+
101
+ it "should pass options when deleting value" do
102
+ subject.switch!(:one, true)
103
+ subject.clear!(:one)
104
+ subject.send(:request).cookie_jar.write(headers = {})
105
+ assert_equal "flipflop_one=; domain=.example.com; path=/foo; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000; HttpOnly",
106
+ headers["Set-Cookie"]
107
+ end
108
+ end
109
+ end
110
+
111
+ describe "outside request context" do
112
+ it "should not know feature" do
113
+ assert_nil subject.enabled?(:one)
114
+ end
115
+
116
+ it "should not be switchable" do
117
+ assert_equal false, subject.switchable?
118
+ end
119
+
120
+ it "should not be able to switch feature on" do
121
+ assert_raises Flipflop::StrategyError do
122
+ subject.switch!(:one, true)
123
+ end
124
+ end
125
+ end
126
+ end