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,21 @@
1
+ Flipflop.configure do
2
+ # Strategies will be used in the order listed here.
3
+ strategy :cookie
4
+ strategy :active_record
5
+ strategy :default
6
+
7
+ # Other strategies:
8
+ #
9
+ # strategy :session
10
+ # strategy :query_string
11
+ #
12
+ # strategy :my_strategy do |feature|
13
+ # # ... your custom code here; return true/false/nil.
14
+ # end
15
+
16
+ # Declare your features, e.g:
17
+ #
18
+ # feature :world_domination,
19
+ # default: true,
20
+ # description: "Take over the world."
21
+ end
@@ -0,0 +1,21 @@
1
+ require "generators/flipflop/features/features_generator"
2
+ require "generators/flipflop/migration/migration_generator"
3
+ require "generators/flipflop/routes/routes_generator"
4
+
5
+ class Flipflop::InstallGenerator < Rails::Generators::Base
6
+ def invoke_generators
7
+ Flipflop::FeaturesGenerator.new([], options).invoke_all
8
+ Flipflop::MigrationGenerator.new([], options).invoke_all
9
+ Flipflop::RoutesGenerator.new([], options).invoke_all
10
+ end
11
+
12
+ def configure_dashboard
13
+ environment <<-CONFIG
14
+ # Replace this with your own 'before_action' filter in ApplicationController
15
+ # to implement access control for the Flipflop dashboard, or provide a
16
+ # filter as lambda directly.
17
+ config.flipflop.dashboard_access_filter = :require_development
18
+
19
+ CONFIG
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ Description:
2
+ Generates migration to create the features table for the Feature model.
3
+
4
+ Example:
5
+ rails generate flipflop:migration
@@ -0,0 +1,23 @@
1
+ require "rails"
2
+ require "rails/generators/migration"
3
+ require "rails/generators/active_record"
4
+
5
+ class Flipflop::MigrationGenerator < Rails::Generators::Base
6
+ include Rails::Generators::Migration
7
+
8
+ source_root File.expand_path("../templates", __FILE__)
9
+
10
+ def create_migration_file
11
+ migration_template("create_features.rb", "db/migrate/create_features.rb")
12
+ end
13
+
14
+ # Stubbed in railties/lib/rails/generators/migration.rb
15
+ #
16
+ # This implementation is a simplified version of:
17
+ # activerecord/lib/rails/generators/active_record/migration.rb
18
+ #
19
+ # See: http://www.ruby-forum.com/topic/203205
20
+ def self.next_migration_number(dirname)
21
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
22
+ end
23
+ end
@@ -0,0 +1,10 @@
1
+ class CreateFeatures < ActiveRecord::Migration<%= Rails.version >= "5" ? "[#{ActiveRecord::Migration.current_version}]" : "" %>
2
+ def change
3
+ create_table :features do |t|
4
+ t.string :key, null: false
5
+ t.boolean :enabled, null: false, default: false
6
+
7
+ t.timestamps null: false
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ Description:
2
+ Add routes for the Flipflop dashboard.
3
+
4
+ Example:
5
+ rails generate flipflop:routes
6
+
7
+ This will add routes to app/routes.rb
@@ -0,0 +1,5 @@
1
+ class Flipflop::RoutesGenerator < Rails::Generators::Base
2
+ def add_route
3
+ route %{mount Flipflop::Engine => "/flipflop"}
4
+ end
5
+ end
@@ -0,0 +1,32 @@
1
+ require File.expand_path("../../test_helper", __FILE__)
2
+
3
+ describe Flipflop do
4
+ before do
5
+ @app = TestApp.new
6
+ end
7
+
8
+ subject do
9
+ @app
10
+ end
11
+
12
+ describe "middleware" do
13
+ it "should include cache middleware" do
14
+ middlewares = Rails.application.middleware.map(&:klass)
15
+ assert_includes middlewares, Flipflop::FeatureCache::Middleware
16
+ end
17
+ end
18
+
19
+ describe "module" do
20
+ before do
21
+ Flipflop::FeatureSet.current.instance_variable_set(:@features, {})
22
+ Module.new do
23
+ extend Flipflop::Configurable
24
+ feature :world_domination
25
+ end
26
+ end
27
+
28
+ it "should allow querying for features" do
29
+ assert_equal false, Flipflop.world_domination?
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,162 @@
1
+ require File.expand_path("../../test_helper", __FILE__)
2
+
3
+ require "capybara/dsl"
4
+
5
+ describe Flipflop do
6
+ include Capybara::DSL
7
+
8
+ before do
9
+ @app = TestApp.new
10
+ end
11
+
12
+ subject do
13
+ @app
14
+ end
15
+
16
+ describe "outside development and test" do
17
+ before do
18
+ Rails.env.stub(:test?, false) do
19
+ visit "/flipflop"
20
+ end
21
+ end
22
+
23
+ it "should be forbidden" do
24
+ assert_equal 403, page.status_code
25
+ end
26
+ end
27
+
28
+ describe "without features" do
29
+ before do
30
+ visit "/flipflop"
31
+ end
32
+
33
+ it "should show feature table with header" do
34
+ assert_equal ["Cookie", "Active record", "Default"],
35
+ all("thead th").map(&:text)[3..-1]
36
+ end
37
+
38
+ it "should show no features" do
39
+ assert all("tbody tr").empty?
40
+ end
41
+ end
42
+
43
+ describe "with features" do
44
+ before do
45
+ Flipflop::FeatureSet.current.instance_variable_set(:@features, {})
46
+ Module.new do
47
+ extend Flipflop::Configurable
48
+ feature :world_domination, description: "Try and take over the world!"
49
+ feature :shiny_things, default: true
50
+ end
51
+
52
+ Capybara.current_session.driver.browser.clear_cookies
53
+ Flipflop::Feature.delete_all
54
+
55
+ visit "/flipflop"
56
+ end
57
+
58
+ it "should show feature rows" do
59
+ assert_equal ["World domination", "Shiny things"],
60
+ all("tr[data-feature] td.name").map(&:text)
61
+ end
62
+
63
+ it "should show feature descriptions" do
64
+ assert_equal ["Try and take over the world!", "Shiny things."],
65
+ all("tr[data-feature] td.description").map(&:text)
66
+ end
67
+
68
+ describe "with cookie strategy" do
69
+ it "should enable feature" do
70
+ within("tr[data-feature=world-domination] td[data-strategy=cookie]") do
71
+ click_on "on"
72
+ end
73
+
74
+ within("tr[data-feature=world-domination]") do
75
+ assert_equal "on", first("td.status").text
76
+ assert_equal "on", first("td[data-strategy=cookie] input.active[type=submit]").value
77
+ end
78
+ end
79
+
80
+ it "should disable feature" do
81
+ within("tr[data-feature=world-domination] td[data-strategy=cookie]") do
82
+ click_on "off"
83
+ end
84
+
85
+ within("tr[data-feature=world-domination]") do
86
+ assert_equal "off", first("td.status").text
87
+ assert_equal "off", first("td[data-strategy=cookie] input.active[type=submit]").value
88
+ end
89
+ end
90
+
91
+ it "should enable and clear feature" do
92
+ within("tr[data-feature=world-domination] td[data-strategy=cookie]") do
93
+ click_on "on"
94
+ end
95
+
96
+ within("tr[data-feature=world-domination] td[data-strategy=cookie]") do
97
+ click_on "clear"
98
+ end
99
+
100
+ within("tr[data-feature=world-domination]") do
101
+ assert_equal "off", first("td.status").text
102
+ refute has_selector?("td[data-strategy=cookie] input.active[type=submit]")
103
+ end
104
+ end
105
+ end
106
+
107
+ describe "with active record strategy" do
108
+ it "should enable feature" do
109
+ within("tr[data-feature=world-domination] td[data-strategy=active-record]") do
110
+ click_on "on"
111
+ end
112
+
113
+ within("tr[data-feature=world-domination]") do
114
+ assert_equal "on", first("td.status").text
115
+ assert_equal "on", first("td[data-strategy=active-record] input.active[type=submit]").value
116
+ end
117
+ end
118
+
119
+ it "should disable feature" do
120
+ within("tr[data-feature=world-domination] td[data-strategy=active-record]") do
121
+ click_on "off"
122
+ end
123
+
124
+ within("tr[data-feature=world-domination]") do
125
+ assert_equal "off", first("td.status").text
126
+ assert_equal "off", first("td[data-strategy=active-record] input.active[type=submit]").value
127
+ end
128
+ end
129
+
130
+ it "should enable and clear feature" do
131
+ within("tr[data-feature=world-domination] td[data-strategy=active-record]") do
132
+ click_on "on"
133
+ end
134
+
135
+ within("tr[data-feature=world-domination] td[data-strategy=active-record]") do
136
+ click_on "clear"
137
+ end
138
+
139
+ within("tr[data-feature=world-domination]") do
140
+ assert_equal "off", first("td.status").text
141
+ refute has_selector?("td[data-strategy=active-record] input.active[type=submit]")
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ describe "with hidden strategy" do
148
+ before do
149
+ Flipflop::FeatureSet.current.instance_variable_set(:@strategies, {})
150
+ Module.new do
151
+ extend Flipflop::Configurable
152
+ strategy :query_string, hidden: true
153
+ end
154
+
155
+ visit "/flipflop"
156
+ end
157
+
158
+ it "should not show hidden strategy" do
159
+ assert_equal [], all("thead th").map(&:text)[3..-1]
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,96 @@
1
+ require "bundler/setup"
2
+ require "flipflop"
3
+
4
+ gem "minitest"
5
+ require "minitest/autorun"
6
+
7
+ require "action_controller"
8
+
9
+ # Who is setting this to true? :o
10
+ $VERBOSE = false
11
+
12
+ def create_request
13
+ env = Rack::MockRequest.env_for("/example")
14
+ request = ActionDispatch::TestRequest.new(env)
15
+ request.host = "example.com"
16
+
17
+ class << request
18
+ def cookie_jar
19
+ @cookie_jar ||= begin
20
+ method = ActionDispatch::Cookies::CookieJar.method(:build)
21
+ if method.arity == 2 # Rails 5.0
22
+ method.call(self, {})
23
+ else
24
+ method.call(self)
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ request
31
+ end
32
+
33
+ def reload_constant(name)
34
+ ActiveSupport::Dependencies.remove_constant(name.to_s)
35
+ path = ActiveSupport::Dependencies.search_for_file(name.to_s.underscore).sub!(/\.rb\z/, "")
36
+ ActiveSupport::Dependencies.loaded.delete(path)
37
+ Object.const_get(name)
38
+ end
39
+
40
+ class TestApp
41
+ class << self
42
+ def new
43
+ ActiveSupport::Dependencies.remove_constant("App")
44
+ super.tap do |current|
45
+ current.create!
46
+ current.load!
47
+ current.migrate!
48
+ reload_constant("Flipflop::Feature")
49
+ end
50
+ end
51
+ end
52
+
53
+ def create!
54
+ require "rails/generators"
55
+ require "rails/generators/rails/app/app_generator"
56
+ require "generators/flipflop/install/install_generator"
57
+
58
+ FileUtils.rm_rf(File.expand_path("../../tmp/app", __FILE__))
59
+ Dir.chdir(File.expand_path("../..", __FILE__))
60
+
61
+ Rails::Generators::AppGenerator.new(["tmp/app"],
62
+ quiet: true,
63
+ skip_active_job: true,
64
+ skip_bundle: true,
65
+ skip_gemfile: true,
66
+ skip_git: true,
67
+ skip_javascript: true,
68
+ skip_keeps: true,
69
+ skip_spring: true,
70
+ skip_test_unit: true,
71
+ skip_turbolinks: true,
72
+ ).invoke_all
73
+
74
+ Flipflop::InstallGenerator.new([],
75
+ quiet: true,
76
+ ).invoke_all
77
+ end
78
+
79
+ def load!
80
+ ENV["RAILS_ENV"] = "test"
81
+ require "rails"
82
+ require "flipflop/engine"
83
+ require File.expand_path("../../tmp/app/config/environment", __FILE__)
84
+ ActiveSupport::Dependencies.mechanism = :load
85
+ load(Rails.application.paths["config/features.rb"].existent.first)
86
+ require "capybara/rails"
87
+ end
88
+
89
+ def migrate!
90
+ ActiveRecord::Base.establish_connection
91
+
92
+ ActiveRecord::Tasks::DatabaseTasks.create_current
93
+ ActiveRecord::Migration.verbose = false
94
+ ActiveRecord::Migrator.migrate(Rails.application.paths["db/migrate"].to_a)
95
+ end
96
+ end
@@ -0,0 +1,104 @@
1
+ require File.expand_path("../../test_helper", __FILE__)
2
+
3
+ describe Flipflop::Configurable do
4
+ subject do
5
+ Flipflop::FeatureSet.current.reset!
6
+ Module.new do
7
+ extend Flipflop::Configurable
8
+ end
9
+ end
10
+
11
+ describe "feature" do
12
+ it "should append feature definition" do
13
+ subject.feature(:one, default: true)
14
+ subject.feature(:two, default: false)
15
+
16
+ assert_equal [:one, :two],
17
+ Flipflop::FeatureSet.current.features.map(&:key)
18
+ end
19
+
20
+ it "should append feature definition with default" do
21
+ subject.feature(:one, default: true)
22
+ subject.feature(:two, default: false)
23
+
24
+ assert_equal [true, false],
25
+ Flipflop::FeatureSet.current.features.map(&:default)
26
+ end
27
+ end
28
+
29
+ describe "strategy" do
30
+ it "should append strategy objects" do
31
+ strategy_class = Class.new(Flipflop::Strategies::AbstractStrategy)
32
+ strategies = [
33
+ strategy_class.new,
34
+ strategy_class.new,
35
+ ]
36
+
37
+ subject.strategy(strategies[0])
38
+ subject.strategy(strategies[1])
39
+
40
+ assert_equal strategies, Flipflop::FeatureSet.current.strategies
41
+ end
42
+
43
+ it "should append strategy classes" do
44
+ strategies = [
45
+ Class.new(Flipflop::Strategies::AbstractStrategy),
46
+ Class.new(Flipflop::Strategies::AbstractStrategy),
47
+ ]
48
+
49
+ subject.strategy(strategies[0])
50
+ subject.strategy(strategies[1])
51
+
52
+ assert_equal strategies, Flipflop::FeatureSet.current.strategies.map(&:class)
53
+ end
54
+
55
+ it "should append strategy classes with options" do
56
+ strategy_class = Class.new(Flipflop::Strategies::AbstractStrategy)
57
+
58
+ subject.strategy(strategy_class, name: "my strategy")
59
+ subject.strategy(strategy_class, name: "awesome strategy")
60
+
61
+ assert_equal ["my strategy", "awesome strategy"],
62
+ Flipflop::FeatureSet.current.strategies.map(&:name)
63
+ end
64
+
65
+ it "should append strategy symbols" do
66
+ subject.strategy(:cookie)
67
+ subject.strategy(:query_string)
68
+
69
+ assert_equal [
70
+ Flipflop::Strategies::CookieStrategy,
71
+ Flipflop::Strategies::QueryStringStrategy
72
+ ], Flipflop::FeatureSet.current.strategies.map(&:class)
73
+ end
74
+
75
+ it "should append strategy symbols with options" do
76
+ subject.strategy(:cookie, name: "my strategy")
77
+ subject.strategy(:query_string, name: "awesome strategy")
78
+
79
+ assert_equal ["my strategy", "awesome strategy"],
80
+ Flipflop::FeatureSet.current.strategies.map(&:name)
81
+ end
82
+
83
+ it "should append strategy lambda" do
84
+ subject.strategy { |feature| "hi!" }
85
+
86
+ assert_equal [Flipflop::Strategies::LambdaStrategy],
87
+ Flipflop::FeatureSet.current.strategies.map(&:class)
88
+ end
89
+
90
+ it "should append strategy lambda with name" do
91
+ subject.strategy(:my_strategy) { |feature| "hi!" }
92
+
93
+ assert_equal ["my_strategy"],
94
+ Flipflop::FeatureSet.current.strategies.map(&:name)
95
+ end
96
+
97
+ it "should append strategy lambda with name and options" do
98
+ subject.strategy("my strategy", description: "awesome") { |feature| "hi!" }
99
+
100
+ assert_equal ["awesome"],
101
+ Flipflop::FeatureSet.current.strategies.map(&:description)
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,142 @@
1
+ require File.expand_path("../../test_helper", __FILE__)
2
+
3
+ describe Flipflop::FeatureCache do
4
+ subject do
5
+ Flipflop::FeatureCache.current
6
+ end
7
+
8
+ after do
9
+ Flipflop::FeatureCache.current.disable!
10
+ end
11
+
12
+ describe "current" do
13
+ it "should return same instance" do
14
+ current = subject
15
+ assert_equal current, Flipflop::FeatureCache.current
16
+ end
17
+
18
+ it "should return new instance in different thread" do
19
+ current = subject
20
+ refute_equal current, Thread.new { Flipflop::FeatureCache.current }.value
21
+ end
22
+ end
23
+
24
+ describe "when enabled" do
25
+ before do
26
+ subject.enable!
27
+ end
28
+
29
+ describe "enabled" do
30
+ it "should return true" do
31
+ assert_equal true, subject.enabled?
32
+ end
33
+ end
34
+
35
+ describe "fetch" do
36
+ it "should store value by key" do
37
+ subject.fetch(:key) { 1 }
38
+ assert_equal 1, subject.fetch(:key) { 2 }
39
+ end
40
+
41
+ it "should not call block if cached" do
42
+ called = false
43
+ subject.fetch(:key) { 1 }
44
+ subject.fetch(:key) { called = true }
45
+ assert_equal false, called
46
+ end
47
+ end
48
+
49
+ describe "clear" do
50
+ it "should empty cache" do
51
+ subject.fetch(:key) { 1 }
52
+ subject.clear!
53
+ assert_equal 2, subject.fetch(:key) { 2 }
54
+ end
55
+ end
56
+ end
57
+
58
+ describe "when disabled" do
59
+ before do
60
+ subject.disable!
61
+ end
62
+
63
+ describe "enabled" do
64
+ it "should return false" do
65
+ assert_equal false, subject.enabled?
66
+ end
67
+ end
68
+
69
+ describe "fetch" do
70
+ it "should not store value" do
71
+ subject.fetch(:key) { 1 }
72
+ assert_equal 2, subject.fetch(:key) { 2 }
73
+ end
74
+
75
+ it "should always call block" do
76
+ called = false
77
+ subject.fetch(:key) { 1 }
78
+ subject.fetch(:key) { called = true }
79
+ assert_equal true, called
80
+ end
81
+ end
82
+ end
83
+
84
+ describe "enable" do
85
+ it "should not clear cache" do
86
+ subject.enable!
87
+ subject.fetch(:key) { 1 }
88
+ subject.enable!
89
+ assert_equal 1, subject.fetch(:key) { 2 }
90
+ end
91
+ end
92
+
93
+ describe "disable" do
94
+ it "should clear cache" do
95
+ subject.enable!
96
+ subject.fetch(:key) { 1 }
97
+ subject.disable!
98
+ subject.enable!
99
+ assert_equal 2, subject.fetch(:key) { 2 }
100
+ end
101
+ end
102
+
103
+ describe "middleware" do
104
+ subject do
105
+ app = ->(env) {
106
+ raise env["error"] if env["error"]
107
+ env["cache"] = Flipflop::FeatureCache.current.enabled?
108
+ return [200, {}, ["ok"]]
109
+ }
110
+ Flipflop::FeatureCache::Middleware.new(app)
111
+ end
112
+
113
+ it "should call app" do
114
+ response = subject.call({})
115
+ assert_equal "ok", response[2].to_a.join
116
+ end
117
+
118
+ it "should enable cache before request" do
119
+ response = subject.call(env = {})
120
+ response[2].try(:close)
121
+ assert_equal true, env["cache"]
122
+ end
123
+
124
+ it "should disable cache after request" do
125
+ response = subject.call({})
126
+ response[2].try(:close)
127
+ assert_equal false, Flipflop::FeatureCache.current.enabled?
128
+ end
129
+
130
+ it "should disable cache after error" do
131
+ subject.call({ "error" => "boo!" }) rescue nil
132
+ assert_equal false, Flipflop::FeatureCache.current.enabled?
133
+ end
134
+
135
+ it "should not change cache if already enabled" do
136
+ Flipflop::FeatureCache.current.enable!
137
+ response = subject.call({})
138
+ response[2].try(:close)
139
+ assert_equal true, Flipflop::FeatureCache.current.enabled?
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,42 @@
1
+ require File.expand_path("../../test_helper", __FILE__)
2
+
3
+ describe Flipflop::FeatureDefinition do
4
+ describe "with defaults" do
5
+ subject do
6
+ Flipflop::FeatureDefinition.new(:my_key)
7
+ end
8
+
9
+ it "should have specified key" do
10
+ assert_equal :my_key, subject.key
11
+ end
12
+
13
+ it "should have humanized description" do
14
+ assert_equal "My key.", subject.description
15
+ end
16
+
17
+ it "should default to false" do
18
+ assert_equal false, subject.default
19
+ end
20
+ end
21
+
22
+ describe "with options" do
23
+ subject do
24
+ Flipflop::FeatureDefinition.new(:my_key,
25
+ default: true,
26
+ description: "Awesome feature",
27
+ )
28
+ end
29
+
30
+ it "should have specified key" do
31
+ assert_equal :my_key, subject.key
32
+ end
33
+
34
+ it "should have specified description" do
35
+ assert_equal "Awesome feature", subject.description
36
+ end
37
+
38
+ it "should have specified default" do
39
+ assert_equal true, subject.default
40
+ end
41
+ end
42
+ end