flipper-api 0.10.2 → 0.11.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/flipper-api.gemspec +12 -12
  3. data/lib/flipper/api/action.rb +12 -10
  4. data/lib/flipper/api/action_collection.rb +2 -2
  5. data/lib/flipper/api/error_response.rb +9 -8
  6. data/lib/flipper/api/json_params.rb +45 -0
  7. data/lib/flipper/api/middleware.rb +3 -28
  8. data/lib/flipper/api/v1/actions/actors_gate.rb +1 -9
  9. data/lib/flipper/api/v1/actions/boolean_gate.rb +1 -1
  10. data/lib/flipper/api/v1/actions/clear_feature.rb +21 -0
  11. data/lib/flipper/api/v1/actions/feature.rb +8 -16
  12. data/lib/flipper/api/v1/actions/features.rb +26 -8
  13. data/lib/flipper/api/v1/actions/groups_gate.rb +18 -7
  14. data/lib/flipper/api/v1/actions/percentage_of_actors_gate.rb +6 -24
  15. data/lib/flipper/api/v1/actions/percentage_of_time_gate.rb +6 -23
  16. data/lib/flipper/api/v1/decorators/feature.rb +3 -4
  17. data/lib/flipper/api.rb +11 -27
  18. data/lib/flipper/version.rb +1 -1
  19. data/spec/flipper/api/action_spec.rb +30 -36
  20. data/spec/flipper/api/json_params_spec.rb +81 -0
  21. data/spec/flipper/api/v1/actions/actors_gate_spec.rb +33 -22
  22. data/spec/flipper/api/v1/actions/boolean_gate_spec.rb +2 -2
  23. data/spec/flipper/api/v1/actions/clear_feature_spec.rb +27 -0
  24. data/spec/flipper/api/v1/actions/feature_spec.rb +28 -30
  25. data/spec/flipper/api/v1/actions/features_spec.rb +79 -44
  26. data/spec/flipper/api/v1/actions/groups_gate_spec.rb +92 -10
  27. data/spec/flipper/api/v1/actions/percentage_of_actors_gate_spec.rb +62 -24
  28. data/spec/flipper/api/v1/actions/percentage_of_time_gate_spec.rb +34 -15
  29. data/spec/flipper/api_spec.rb +55 -0
  30. metadata +15 -7
@@ -1,68 +1,66 @@
1
1
  require 'helper'
2
2
 
3
3
  RSpec.describe Flipper::Api::Action do
4
- let(:action_subclass) {
4
+ let(:action_subclass) do
5
5
  Class.new(described_class) do
6
6
  def noooope
7
- raise "should never run this"
7
+ raise 'should never run this'
8
8
  end
9
9
 
10
10
  def get
11
- [200, {}, "get"]
11
+ [200, {}, 'get']
12
12
  end
13
13
 
14
14
  def post
15
- [200, {}, "post"]
15
+ [200, {}, 'post']
16
16
  end
17
17
 
18
18
  def put
19
- [200, {}, "put"]
19
+ [200, {}, 'put']
20
20
  end
21
21
 
22
22
  def delete
23
- [200, {}, "delete"]
23
+ [200, {}, 'delete']
24
24
  end
25
25
  end
26
- }
26
+ end
27
27
 
28
28
  describe 'https verbs' do
29
-
30
29
  it "won't run method that isn't whitelisted" do
31
- fake_request = Struct.new(:request_method, :env, :session).new("NOOOOPE", {}, {})
30
+ fake_request = Struct.new(:request_method, :env, :session).new('NOOOOPE', {}, {})
32
31
  action = action_subclass.new(flipper, fake_request)
33
- expect {
32
+ expect do
34
33
  action.run
35
- }.to raise_error(Flipper::Api::RequestMethodNotSupported)
34
+ end.to raise_error(Flipper::Api::RequestMethodNotSupported)
36
35
  end
37
36
 
38
- it "will run get" do
39
- fake_request = Struct.new(:request_method, :env, :session).new("GET", {}, {})
37
+ it 'will run get' do
38
+ fake_request = Struct.new(:request_method, :env, :session).new('GET', {}, {})
40
39
  action = action_subclass.new(flipper, fake_request)
41
- expect(action.run).to eq([200, {}, "get"])
40
+ expect(action.run).to eq([200, {}, 'get'])
42
41
  end
43
42
 
44
- it "will run post" do
45
- fake_request = Struct.new(:request_method, :env, :session).new("POST", {}, {})
43
+ it 'will run post' do
44
+ fake_request = Struct.new(:request_method, :env, :session).new('POST', {}, {})
46
45
  action = action_subclass.new(flipper, fake_request)
47
- expect(action.run).to eq([200, {}, "post"])
46
+ expect(action.run).to eq([200, {}, 'post'])
48
47
  end
49
48
 
50
- it "will run put" do
51
- fake_request = Struct.new(:request_method, :env, :session).new("PUT", {}, {})
49
+ it 'will run put' do
50
+ fake_request = Struct.new(:request_method, :env, :session).new('PUT', {}, {})
52
51
  action = action_subclass.new(flipper, fake_request)
53
- expect(action.run).to eq([200, {}, "put"])
52
+ expect(action.run).to eq([200, {}, 'put'])
54
53
  end
55
54
 
56
- it "will run delete" do
57
- fake_request = Struct.new(:request_method, :env, :session).new("DELETE", {}, {})
55
+ it 'will run delete' do
56
+ fake_request = Struct.new(:request_method, :env, :session).new('DELETE', {}, {})
58
57
  action = action_subclass.new(flipper, fake_request)
59
- expect(action.run).to eq([200, {}, "delete"])
58
+ expect(action.run).to eq([200, {}, 'delete'])
60
59
  end
61
60
  end
62
61
 
63
62
  describe '#json_error_response' do
64
- describe ":feature_not_found" do
65
-
63
+ describe ':feature_not_found' do
66
64
  it 'locates and serializes error correctly' do
67
65
  action = action_subclass.new({}, {})
68
66
  response = catch(:halt) do
@@ -71,15 +69,12 @@ RSpec.describe Flipper::Api::Action do
71
69
  status, headers, body = response
72
70
  parsed_body = JSON.parse(body[0])
73
71
 
74
- expect(headers["Content-Type"]).to eq("application/json")
75
- expect(parsed_body["code"]).to eq(1)
76
- expect(parsed_body["message"]).to eq("Feature not found.")
77
- expect(parsed_body["more_info"]).to eq("https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference")
72
+ expect(headers['Content-Type']).to eq('application/json')
73
+ expect(parsed_body).to eql(api_not_found_response)
78
74
  end
79
75
  end
80
76
 
81
77
  describe ':group_not_registered' do
82
-
83
78
  it 'locates and serializes error correctly' do
84
79
  action = action_subclass.new({}, {})
85
80
  response = catch(:halt) do
@@ -88,19 +83,18 @@ RSpec.describe Flipper::Api::Action do
88
83
  status, headers, body = response
89
84
  parsed_body = JSON.parse(body[0])
90
85
 
91
- expect(headers["Content-Type"]).to eq("application/json")
92
- expect(parsed_body["code"]).to eq(2)
93
- expect(parsed_body["message"]).to eq("Group not registered.")
94
- expect(parsed_body["more_info"]).to eq("https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference")
86
+ expect(headers['Content-Type']).to eq('application/json')
87
+ expect(parsed_body['code']).to eq(2)
88
+ expect(parsed_body['message']).to eq('Group not registered.')
89
+ expect(parsed_body['more_info']).to eq(api_error_code_reference_url)
95
90
  end
96
91
  end
97
92
 
98
93
  describe 'invalid error key' do
99
-
100
94
  it 'raises descriptive error' do
101
95
  action = action_subclass.new({}, {})
102
96
  catch(:halt) do
103
- expect{ action.json_error_response(:invalid_error_key) }.to raise_error(KeyError)
97
+ expect { action.json_error_response(:invalid_error_key) }.to raise_error(KeyError)
104
98
  end
105
99
  end
106
100
  end
@@ -0,0 +1,81 @@
1
+ require 'helper'
2
+
3
+ RSpec.describe Flipper::Api::JsonParams do
4
+ let(:app) do
5
+ app = lambda do |env|
6
+ request = Rack::Request.new(env)
7
+ [200, { 'Content-Type' => 'application/json' }, [JSON.generate(request.params)]]
8
+ end
9
+ builder = Rack::Builder.new
10
+ builder.use described_class
11
+ builder.run app
12
+ builder
13
+ end
14
+
15
+ describe 'json post request' do
16
+ it 'adds request body to params' do
17
+ response = post '/',
18
+ JSON.generate(flipper_id: 'user:2'),
19
+ 'CONTENT_TYPE' => 'application/json'
20
+
21
+ params = JSON.parse(response.body)
22
+ expect(params).to eq('flipper_id' => 'user:2')
23
+ end
24
+
25
+ it 'handles request bodies with multiple params' do
26
+ response = post '/',
27
+ JSON.generate(flipper_id: 'user:2', language: 'ruby'),
28
+ 'CONTENT_TYPE' => 'application/json'
29
+
30
+ params = JSON.parse(response.body)
31
+ expect(params).to eq('flipper_id' => 'user:2', 'language' => 'ruby')
32
+ end
33
+
34
+ it 'handles request bodies and single query string params' do
35
+ response = post '/?language=ruby',
36
+ JSON.generate(flipper_id: 'user:2'),
37
+ 'CONTENT_TYPE' => 'application/json'
38
+
39
+ params = JSON.parse(response.body)
40
+ expect(params).to eq('flipper_id' => 'user:2', 'language' => 'ruby')
41
+ end
42
+
43
+ it 'handles request bodies and multiple query string params' do
44
+ response = post '/?language=ruby&framework=rails',
45
+ JSON.generate(flipper_id: 'user:2'),
46
+ 'CONTENT_TYPE' => 'application/json'
47
+
48
+ params = JSON.parse(response.body)
49
+ expect(params).to eq('flipper_id' => 'user:2', 'language' => 'ruby', 'framework' => 'rails')
50
+ end
51
+
52
+ it 'favors request body params' do
53
+ response = post '/?language=javascript',
54
+ JSON.generate(flipper_id: 'user:2', language: 'ruby'),
55
+ 'CONTENT_TYPE' => 'application/json'
56
+
57
+ params = JSON.parse(response.body)
58
+ expect(params).to eq('flipper_id' => 'user:2', 'language' => 'ruby')
59
+ end
60
+ end
61
+
62
+ describe 'url-encoded request' do
63
+ it 'handles params the same as a json request' do
64
+ response = post '/', flipper_id: 'user:2'
65
+ params = JSON.parse(response.body)
66
+ expect(params).to eq('flipper_id' => 'user:2')
67
+ end
68
+
69
+ it 'handles single query string params' do
70
+ response = post '/?language=ruby', flipper_id: 'user:2'
71
+ params = JSON.parse(response.body)
72
+ expect(params).to eq('flipper_id' => 'user:2', 'language' => 'ruby')
73
+ end
74
+
75
+ it 'handles multiple query string params' do
76
+ response = post '/?language=ruby&framework=rails', flipper_id: 'user:2'
77
+ params = JSON.parse(response.body)
78
+ expect(params).to eq('flipper_id' => 'user:2', 'language' => 'ruby', 'framework' => 'rails')
79
+ end
80
+ end
81
+ end
@@ -2,13 +2,12 @@ require 'helper'
2
2
 
3
3
  RSpec.describe Flipper::Api::V1::Actions::ActorsGate do
4
4
  let(:app) { build_api(flipper) }
5
+ let(:actor) { Flipper::Api::Actor.new('1') }
5
6
 
6
7
  describe 'enable' do
7
- let(:actor) { Flipper::Api::Actor.new("1") }
8
-
9
8
  before do
10
9
  flipper[:my_feature].disable_actor(actor)
11
- post '/api/v1/features/my_feature/actors', { flipper_id: actor.flipper_id }
10
+ post '/features/my_feature/actors', flipper_id: actor.flipper_id
12
11
  end
13
12
 
14
13
  it 'enables feature for actor' do
@@ -19,16 +18,16 @@ RSpec.describe Flipper::Api::V1::Actions::ActorsGate do
19
18
 
20
19
  it 'returns decorated feature with actor enabled' do
21
20
  gate = json_response['gates'].find { |gate| gate['key'] == 'actors' }
22
- expect(gate['value']).to eq(["1"])
21
+ expect(gate['value']).to eq(['1'])
23
22
  end
24
23
  end
25
24
 
26
25
  describe 'disable' do
27
- let(:actor) { Flipper::Api::Actor.new("1") }
26
+ let(:actor) { Flipper::Api::Actor.new('1') }
28
27
 
29
28
  before do
30
29
  flipper[:my_feature].enable_actor(actor)
31
- delete '/api/v1/features/my_feature/actors', { flipper_id: actor.flipper_id }
30
+ delete '/features/my_feature/actors', flipper_id: actor.flipper_id
32
31
  end
33
32
 
34
33
  it 'disables feature' do
@@ -46,70 +45,82 @@ RSpec.describe Flipper::Api::V1::Actions::ActorsGate do
46
45
  describe 'enable missing flipper_id parameter' do
47
46
  before do
48
47
  flipper[:my_feature].enable
49
- post '/api/v1/features/my_feature/actors'
48
+ post '/features/my_feature/actors'
50
49
  end
51
50
 
52
51
  it 'returns correct error response' do
53
52
  expect(last_response.status).to eq(422)
54
- expect(json_response).to eq({ 'code' => 4, 'message' => 'Required parameter flipper_id is missing.', 'more_info' => 'https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference' })
53
+ expect(json_response).to eq(api_flipper_id_is_missing_response)
55
54
  end
56
55
  end
57
56
 
58
57
  describe 'disable missing flipper_id parameter' do
59
58
  before do
60
59
  flipper[:my_feature].enable
61
- delete '/api/v1/features/my_feature/actors'
60
+ delete '/features/my_feature/actors'
62
61
  end
63
62
 
64
63
  it 'returns correct error response' do
65
64
  expect(last_response.status).to eq(422)
66
- expect(json_response).to eq({ 'code' => 4, 'message' => 'Required parameter flipper_id is missing.', 'more_info' => 'https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference' })
65
+ expect(json_response).to eq(api_flipper_id_is_missing_response)
67
66
  end
68
67
  end
69
68
 
70
69
  describe 'enable nil flipper_id parameter' do
71
70
  before do
72
71
  flipper[:my_feature].enable
73
- post '/api/v1/features/my_feature/actors', { flipper_id: nil }
72
+ post '/features/my_feature/actors', flipper_id: nil
74
73
  end
75
74
 
76
75
  it 'returns correct error response' do
77
76
  expect(last_response.status).to eq(422)
78
- expect(json_response).to eq({ 'code' => 4, 'message' => 'Required parameter flipper_id is missing.', 'more_info' => 'https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference' })
77
+ expect(json_response).to eq(api_flipper_id_is_missing_response)
79
78
  end
80
79
  end
81
80
 
82
81
  describe 'disable nil flipper_id parameter' do
83
82
  before do
84
83
  flipper[:my_feature].enable
85
- delete '/api/v1/features/my_feature/actors', { flipper_id: nil }
84
+ delete '/features/my_feature/actors', flipper_id: nil
86
85
  end
87
86
 
88
87
  it 'returns correct error response' do
89
88
  expect(last_response.status).to eq(422)
90
- expect(json_response).to eq({ 'code' => 4, 'message' => 'Required parameter flipper_id is missing.', 'more_info' => 'https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference' })
89
+ expect(json_response).to eq(api_flipper_id_is_missing_response)
91
90
  end
92
91
  end
93
92
 
94
93
  describe 'enable missing feature' do
95
94
  before do
96
- post '/api/v1/features/my_feature/actors'
95
+ post '/features/my_feature/actors', flipper_id: actor.flipper_id
97
96
  end
98
97
 
99
- it 'returns correct error response' do
100
- expect(last_response.status).to eq(404)
101
- expect(json_response).to eq({ 'code' => 1, 'message' => 'Feature not found.', 'more_info' => 'https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference' })
98
+ it 'enables feature for actor' do
99
+ expect(last_response.status).to eq(200)
100
+ expect(flipper[:my_feature].enabled?(actor)).to be_truthy
101
+ expect(flipper[:my_feature].enabled_gate_names).to eq([:actor])
102
+ end
103
+
104
+ it 'returns decorated feature with actor enabled' do
105
+ gate = json_response['gates'].find { |gate| gate['key'] == 'actors' }
106
+ expect(gate['value']).to eq(['1'])
102
107
  end
103
108
  end
104
109
 
105
110
  describe 'disable missing feature' do
106
111
  before do
107
- delete '/api/v1/features/my_feature/actors'
112
+ delete '/features/my_feature/actors', flipper_id: actor.flipper_id
108
113
  end
109
114
 
110
- it 'returns correct error response' do
111
- expect(last_response.status).to eq(404)
112
- expect(json_response).to eq({ 'code' => 1, 'message' => 'Feature not found.', 'more_info' => 'https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference' })
115
+ it 'disables feature' do
116
+ expect(last_response.status).to eq(200)
117
+ expect(flipper[:my_feature].enabled?(actor)).to be_falsy
118
+ expect(flipper[:my_feature].enabled_gate_names).to be_empty
119
+ end
120
+
121
+ it 'returns decorated feature with boolean gate disabled' do
122
+ gate = json_response['gates'].find { |gate| gate['key'] == 'actors' }
123
+ expect(gate['value']).to be_empty
113
124
  end
114
125
  end
115
126
  end
@@ -6,7 +6,7 @@ RSpec.describe Flipper::Api::V1::Actions::BooleanGate do
6
6
  describe 'enable' do
7
7
  before do
8
8
  flipper[:my_feature].disable
9
- post '/api/v1/features/my_feature/boolean'
9
+ post '/features/my_feature/boolean'
10
10
  end
11
11
 
12
12
  it 'enables feature' do
@@ -23,7 +23,7 @@ RSpec.describe Flipper::Api::V1::Actions::BooleanGate do
23
23
  describe 'disable' do
24
24
  before do
25
25
  flipper[:my_feature].enable
26
- delete '/api/v1/features/my_feature/boolean'
26
+ delete '/features/my_feature/boolean'
27
27
  end
28
28
 
29
29
  it 'disables feature' do
@@ -0,0 +1,27 @@
1
+ require 'helper'
2
+
3
+ RSpec.describe Flipper::Api::V1::Actions::ClearFeature do
4
+ let(:app) { build_api(flipper) }
5
+
6
+ describe 'clear' do
7
+ before do
8
+ Flipper.register(:admins) {}
9
+ actor_class = Struct.new(:flipper_id)
10
+ actor22 = actor_class.new('22')
11
+
12
+ feature = flipper[:my_feature]
13
+ feature.enable flipper.boolean
14
+ feature.enable flipper.group(:admins)
15
+ feature.enable flipper.actor(actor22)
16
+ feature.enable flipper.actors(25)
17
+ feature.enable flipper.time(45)
18
+
19
+ delete '/features/my_feature/clear'
20
+ end
21
+
22
+ it 'clears feature' do
23
+ expect(last_response.status).to eq(204)
24
+ expect(flipper[:my_feature].off?).to be_truthy
25
+ end
26
+ end
27
+ end
@@ -7,10 +7,9 @@ RSpec.describe Flipper::Api::V1::Actions::Feature do
7
7
 
8
8
  describe 'get' do
9
9
  context 'enabled feature' do
10
-
11
10
  before do
12
11
  flipper[:my_feature].enable
13
- get 'api/v1/features/my_feature'
12
+ get '/features/my_feature'
14
13
  end
15
14
 
16
15
  it 'responds with correct attributes' do
@@ -21,7 +20,7 @@ RSpec.describe Flipper::Api::V1::Actions::Feature do
21
20
  {
22
21
  'key' => 'boolean',
23
22
  'name' => 'boolean',
24
- 'value' => true,
23
+ 'value' => 'true',
25
24
  },
26
25
  {
27
26
  'key' => 'groups',
@@ -36,14 +35,14 @@ RSpec.describe Flipper::Api::V1::Actions::Feature do
36
35
  {
37
36
  'key' => 'percentage_of_actors',
38
37
  'name' => 'percentage_of_actors',
39
- 'value' => 0,
38
+ 'value' => nil,
40
39
  },
41
40
  {
42
41
  'key' => 'percentage_of_time',
43
42
  'name' => 'percentage_of_time',
44
- 'value' => 0,
45
- }
46
- ]
43
+ 'value' => nil,
44
+ },
45
+ ],
47
46
  }
48
47
 
49
48
  expect(last_response.status).to eq(200)
@@ -54,7 +53,7 @@ RSpec.describe Flipper::Api::V1::Actions::Feature do
54
53
  context 'disabled feature' do
55
54
  before do
56
55
  flipper[:my_feature].disable
57
- get 'api/v1/features/my_feature'
56
+ get '/features/my_feature'
58
57
  end
59
58
 
60
59
  it 'responds with correct attributes' do
@@ -65,12 +64,12 @@ RSpec.describe Flipper::Api::V1::Actions::Feature do
65
64
  {
66
65
  'key' => 'boolean',
67
66
  'name' => 'boolean',
68
- 'value' => false,
67
+ 'value' => nil,
69
68
  },
70
69
  {
71
- 'key'=> 'groups',
72
- 'name'=> 'group',
73
- 'value'=> [],
70
+ 'key' => 'groups',
71
+ 'name' => 'group',
72
+ 'value' => [],
74
73
  },
75
74
  {
76
75
  'key' => 'actors',
@@ -80,14 +79,14 @@ RSpec.describe Flipper::Api::V1::Actions::Feature do
80
79
  {
81
80
  'key' => 'percentage_of_actors',
82
81
  'name' => 'percentage_of_actors',
83
- 'value'=> 0,
82
+ 'value' => nil,
84
83
  },
85
84
  {
86
85
  'key' => 'percentage_of_time',
87
86
  'name' => 'percentage_of_time',
88
- 'value' => 0,
89
- }
90
- ]
87
+ 'value' => nil,
88
+ },
89
+ ],
91
90
  }
92
91
 
93
92
  expect(last_response.status).to eq(200)
@@ -97,15 +96,17 @@ RSpec.describe Flipper::Api::V1::Actions::Feature do
97
96
 
98
97
  context 'feature does not exist' do
99
98
  before do
100
- get 'api/v1/features/not_a_feature'
99
+ get '/features/not_a_feature'
101
100
  end
102
101
 
103
- it 'returns 404' do
102
+ it '404s' do
104
103
  expect(last_response.status).to eq(404)
105
- end
106
-
107
- it 'returns formatted error response body' do
108
- expect(json_response).to eq({ "code" => 1, "message" => "Feature not found.", "more_info" => "https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference" })
104
+ expected = {
105
+ 'code' => 1,
106
+ 'message' => 'Feature not found.',
107
+ 'more_info' => api_error_code_reference_url,
108
+ }
109
+ expect(json_response).to eq(expected)
109
110
  end
110
111
  end
111
112
  end
@@ -115,7 +116,7 @@ RSpec.describe Flipper::Api::V1::Actions::Feature do
115
116
  it 'deletes feature' do
116
117
  flipper[:my_feature].enable
117
118
  expect(flipper.features.map(&:key)).to include('my_feature')
118
- delete 'api/v1/features/my_feature'
119
+ delete '/features/my_feature'
119
120
  expect(last_response.status).to eq(204)
120
121
  expect(flipper.features.map(&:key)).not_to include('my_feature')
121
122
  end
@@ -123,15 +124,12 @@ RSpec.describe Flipper::Api::V1::Actions::Feature do
123
124
 
124
125
  context 'feature not found' do
125
126
  before do
126
- delete 'api/v1/features/my_feature'
127
+ delete '/features/my_feature'
127
128
  end
128
129
 
129
- it 'returns 404' do
130
- expect(last_response.status).to eq(404)
131
- end
132
-
133
- it 'returns formatted error response body' do
134
- expect(json_response).to eq({ "code" => 1, "message" => "Feature not found.", "more_info" => "https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference" })
130
+ it 'responds with 204' do
131
+ expect(last_response.status).to eq(204)
132
+ expect(flipper.features.map(&:key)).not_to include('my_feature')
135
133
  end
136
134
  end
137
135
  end