flipflop 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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