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
@@ -10,58 +10,88 @@ RSpec.describe Flipper::Api::V1::Actions::Features do
10
10
  before do
11
11
  flipper[:my_feature].enable
12
12
  flipper[:my_feature].enable(admin)
13
- get 'api/v1/features'
13
+ get '/features'
14
14
  end
15
15
 
16
16
  it 'responds with correct attributes' do
17
17
  expected_response = {
18
- "features" => [
18
+ 'features' => [
19
19
  {
20
- "key" => "my_feature",
21
- "state" => "on",
22
- "gates" => [
20
+ 'key' => 'my_feature',
21
+ 'state' => 'on',
22
+ 'gates' => [
23
23
  {
24
- "key"=> "boolean",
25
- "name"=> "boolean",
26
- "value" => true
24
+ 'key' => 'boolean',
25
+ 'name' => 'boolean',
26
+ 'value' => 'true',
27
27
  },
28
28
  {
29
- "key" => "groups",
30
- "name" => "group",
31
- "value" => [],
29
+ 'key' => 'groups',
30
+ 'name' => 'group',
31
+ 'value' => [],
32
32
  },
33
33
  {
34
- "key" => "actors",
35
- "name" => "actor",
36
- "value" => ["10"],
34
+ 'key' => 'actors',
35
+ 'name' => 'actor',
36
+ 'value' => ['10'],
37
37
  },
38
38
  {
39
- "key" => "percentage_of_actors",
40
- "name" => "percentage_of_actors",
41
- "value" => 0,
39
+ 'key' => 'percentage_of_actors',
40
+ 'name' => 'percentage_of_actors',
41
+ 'value' => nil,
42
42
  },
43
43
  {
44
- "key"=> "percentage_of_time",
45
- "name"=> "percentage_of_time",
46
- "value"=> 0,
44
+ 'key' => 'percentage_of_time',
45
+ 'name' => 'percentage_of_time',
46
+ 'value' => nil,
47
47
  },
48
48
  ],
49
49
  },
50
- ]
50
+ ],
51
51
  }
52
52
  expect(last_response.status).to eq(200)
53
53
  expect(json_response).to eq(expected_response)
54
54
  end
55
55
  end
56
56
 
57
+ context 'with keys specified' do
58
+ before do
59
+ flipper[:audit_log].enable
60
+ flipper[:issues].enable
61
+ flipper[:search].enable
62
+ flipper[:stats].disable
63
+ get '/features', 'keys' => 'search,stats'
64
+ end
65
+
66
+ it 'responds with correct attributes' do
67
+ expect(last_response.status).to eq(200)
68
+ keys = json_response.fetch('features').map { |feature| feature.fetch('key') }.sort
69
+ expect(keys).to eq(%w(search stats))
70
+ end
71
+ end
72
+
73
+ context 'with keys that are not existing features' do
74
+ before do
75
+ flipper[:search].disable
76
+ flipper[:stats].disable
77
+ get '/features', 'keys' => 'search,stats,not_a_feature,another_feature_that_does_not_exist'
78
+ end
79
+
80
+ it 'only returns features that exist' do
81
+ expect(last_response.status).to eq(200)
82
+ keys = json_response.fetch('features').map { |feature| feature.fetch('key') }.sort
83
+ expect(keys).to eq(%w(search stats))
84
+ end
85
+ end
86
+
57
87
  context 'with no flipper features' do
58
88
  before do
59
- get 'api/v1/features'
89
+ get '/features'
60
90
  end
61
91
 
62
92
  it 'returns empty array for features key' do
63
93
  expected_response = {
64
- "features" => []
94
+ 'features' => [],
65
95
  }
66
96
  expect(last_response.status).to eq(200)
67
97
  expect(json_response).to eq(expected_response)
@@ -72,7 +102,7 @@ RSpec.describe Flipper::Api::V1::Actions::Features do
72
102
  describe 'post' do
73
103
  context 'succesful request' do
74
104
  before do
75
- post 'api/v1/features', { name: 'my_feature' }
105
+ post '/features', name: 'my_feature'
76
106
  end
77
107
 
78
108
  it 'responds 200 ' do
@@ -82,33 +112,33 @@ RSpec.describe Flipper::Api::V1::Actions::Features do
82
112
  it 'returns decorated feature' do
83
113
  expected_response = {
84
114
 
85
- "key" => "my_feature",
86
- "state" => "off",
87
- "gates" => [
115
+ 'key' => 'my_feature',
116
+ 'state' => 'off',
117
+ 'gates' => [
88
118
  {
89
- "key"=> "boolean",
90
- "name"=> "boolean",
91
- "value" => false,
119
+ 'key' => 'boolean',
120
+ 'name' => 'boolean',
121
+ 'value' => nil,
92
122
  },
93
123
  {
94
- "key" => "groups",
95
- "name" => "group",
96
- "value" => [],
124
+ 'key' => 'groups',
125
+ 'name' => 'group',
126
+ 'value' => [],
97
127
  },
98
128
  {
99
- "key" => "actors",
100
- "name" => "actor",
101
- "value" => [],
129
+ 'key' => 'actors',
130
+ 'name' => 'actor',
131
+ 'value' => [],
102
132
  },
103
133
  {
104
- "key" => "percentage_of_actors",
105
- "name" => "percentage_of_actors",
106
- "value" => 0,
134
+ 'key' => 'percentage_of_actors',
135
+ 'name' => 'percentage_of_actors',
136
+ 'value' => nil,
107
137
  },
108
138
  {
109
- "key"=> "percentage_of_time",
110
- "name"=> "percentage_of_time",
111
- "value"=> 0,
139
+ 'key' => 'percentage_of_time',
140
+ 'name' => 'percentage_of_time',
141
+ 'value' => nil,
112
142
  },
113
143
  ],
114
144
  }
@@ -126,7 +156,7 @@ RSpec.describe Flipper::Api::V1::Actions::Features do
126
156
 
127
157
  context 'bad request' do
128
158
  before do
129
- post 'api/v1/features'
159
+ post '/features'
130
160
  end
131
161
 
132
162
  it 'returns correct status code' do
@@ -134,7 +164,12 @@ RSpec.describe Flipper::Api::V1::Actions::Features do
134
164
  end
135
165
 
136
166
  it 'returns formatted error' do
137
- expect(json_response).to eq({ 'code' => 5, 'message' => 'Required parameter name is missing.', 'more_info' => 'https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference' })
167
+ expected = {
168
+ 'code' => 5,
169
+ 'message' => 'Required parameter name is missing.',
170
+ 'more_info' => api_error_code_reference_url,
171
+ }
172
+ expect(json_response).to eq(expected)
138
173
  end
139
174
  end
140
175
  end
@@ -9,7 +9,7 @@ RSpec.describe Flipper::Api::V1::Actions::GroupsGate do
9
9
  Flipper.register(:admins) do |actor|
10
10
  actor.respond_to?(:admin?) && actor.admin?
11
11
  end
12
- post '/api/v1/features/my_feature/groups', { name: 'admins' }
12
+ post '/features/my_feature/groups', name: 'admins'
13
13
  end
14
14
 
15
15
  it 'enables feature for group' do
@@ -26,6 +26,29 @@ RSpec.describe Flipper::Api::V1::Actions::GroupsGate do
26
26
  end
27
27
  end
28
28
 
29
+ describe 'enable without name params' do
30
+ before do
31
+ flipper[:my_feature].disable
32
+ Flipper.register(:admins) do |actor|
33
+ actor.respond_to?(:admin?) && actor.admin?
34
+ end
35
+ post '/features/my_feature/groups'
36
+ end
37
+
38
+ it 'returns correct status code' do
39
+ expect(last_response.status).to eq(422)
40
+ end
41
+
42
+ it 'returns formatted error' do
43
+ expected = {
44
+ 'code' => 5,
45
+ 'message' => 'Required parameter name is missing.',
46
+ 'more_info' => api_error_code_reference_url,
47
+ }
48
+ expect(json_response).to eq(expected)
49
+ end
50
+ end
51
+
29
52
  describe 'disable' do
30
53
  before do
31
54
  flipper[:my_feature].disable
@@ -33,7 +56,7 @@ RSpec.describe Flipper::Api::V1::Actions::GroupsGate do
33
56
  actor.respond_to?(:admin?) && actor.admin?
34
57
  end
35
58
  flipper[:my_feature].enable_group(:admins)
36
- delete '/api/v1/features/my_feature/groups', { name: 'admins' }
59
+ delete '/features/my_feature/groups', name: 'admins'
37
60
  end
38
61
 
39
62
  it 'disables feature for group' do
@@ -50,26 +73,85 @@ RSpec.describe Flipper::Api::V1::Actions::GroupsGate do
50
73
  end
51
74
  end
52
75
 
53
- describe 'non-existent feature' do
76
+ describe 'disable for non-existent feature' do
54
77
  before do
55
- delete '/api/v1/features/my_feature/groups', { name: 'admins' }
78
+ Flipper.register(:admins) do |actor|
79
+ actor.respond_to?(:admin?) && actor.admin?
80
+ end
81
+ delete '/features/my_feature/groups', name: 'admins'
56
82
  end
57
83
 
58
- it '404s with correct error response when feature does not exist' do
59
- expect(last_response.status).to eq(404)
60
- 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' })
84
+ it 'disables feature for group' do
85
+ person = double
86
+ allow(person).to receive(:flipper_id).and_return(1)
87
+ allow(person).to receive(:admin?).and_return(true)
88
+ expect(last_response.status).to eq(200)
89
+ expect(flipper[:my_feature].enabled?(person)).to be_falsey
90
+ end
91
+
92
+ it 'returns decorated feature with group disabled' do
93
+ group_gate = json_response['gates'].find { |m| m['name'] == 'group' }
94
+ expect(group_gate['value']).to eq([])
61
95
  end
62
96
  end
63
97
 
64
- describe 'group not registered' do
98
+ describe 'disable for group not registered' do
65
99
  before do
66
100
  flipper[:my_feature].disable
67
- delete '/api/v1/features/my_feature/groups', { name: 'admins' }
101
+ delete '/features/my_feature/groups', name: 'admins'
68
102
  end
69
103
 
70
104
  it '404s with correct error response when group not registered' do
71
105
  expect(last_response.status).to eq(404)
72
- expect(json_response).to eq({ 'code' => 2, 'message' => 'Group not registered.', 'more_info' => 'https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference' })
106
+ expected = {
107
+ 'code' => 2,
108
+ 'message' => 'Group not registered.',
109
+ 'more_info' => api_error_code_reference_url,
110
+ }
111
+ expect(json_response).to eq(expected)
112
+ end
113
+ end
114
+
115
+ describe 'enable for group not registered when allow_unregistered_groups is true' do
116
+ before do
117
+ Flipper.unregister_groups
118
+ flipper[:my_feature].disable
119
+ post '/features/my_feature/groups', name: 'admins', allow_unregistered_groups: 'true'
120
+ end
121
+
122
+ it 'responds successfully' do
123
+ expect(last_response.status).to eq(200)
124
+ end
125
+
126
+ it 'returns decorated feature with group in groups set' do
127
+ group_gate = json_response['gates'].find { |m| m['name'] == 'group' }
128
+ expect(group_gate['value']).to eq(['admins'])
129
+ end
130
+
131
+ it 'enables group' do
132
+ expect(flipper[:my_feature].groups_value).to eq(Set["admins"])
133
+ end
134
+ end
135
+
136
+ describe 'disable for group not registered when allow_unregistered_groups is true' do
137
+ before do
138
+ Flipper.unregister_groups
139
+ flipper[:my_feature].disable
140
+ flipper[:my_feature].enable_group(:admins)
141
+ delete '/features/my_feature/groups', name: 'admins', allow_unregistered_groups: 'true'
142
+ end
143
+
144
+ it 'responds successfully' do
145
+ expect(last_response.status).to eq(200)
146
+ end
147
+
148
+ it 'returns decorated feature with group not in groups set' do
149
+ group_gate = json_response['gates'].find { |m| m['name'] == 'group' }
150
+ expect(group_gate['value']).to eq([])
151
+ end
152
+
153
+ it 'disables group' do
154
+ expect(flipper[:my_feature].groups_value).to be_empty
73
155
  end
74
156
  end
75
157
  end
@@ -4,26 +4,45 @@ RSpec.describe Flipper::Api::V1::Actions::PercentageOfActorsGate do
4
4
  let(:app) { build_api(flipper) }
5
5
 
6
6
  describe 'enable' do
7
- before do
8
- flipper[:my_feature].disable
9
- post '/api/v1/features/my_feature/percentage_of_actors', { percentage: '10' }
7
+ context 'url-encoded request' do
8
+ before do
9
+ flipper[:my_feature].disable
10
+ post '/features/my_feature/percentage_of_actors', percentage: '10'
11
+ end
12
+
13
+ it 'enables gate for feature' do
14
+ expect(flipper[:my_feature].enabled_gate_names).to include(:percentage_of_actors)
15
+ end
16
+
17
+ it 'returns decorated feature with gate enabled for 10 percent of actors' do
18
+ gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' }
19
+ expect(gate['value']).to eq('10')
20
+ end
10
21
  end
11
22
 
12
- it 'enables gate for feature' do
13
- expect(flipper[:my_feature].enabled_gate_names).to include(:percentage_of_actors)
14
- end
15
-
16
- it 'returns decorated feature with gate enabled for 10 percent of actors' do
17
- gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' }
18
- expect(gate['value']).to eq(10)
23
+ context 'json request' do
24
+ before do
25
+ flipper[:my_feature].disable
26
+ post '/features/my_feature/percentage_of_actors',
27
+ JSON.generate(percentage: '10'),
28
+ 'CONTENT_TYPE' => 'application/json'
29
+ end
30
+
31
+ it 'enables gate for feature' do
32
+ expect(flipper[:my_feature].enabled_gate_names).to include(:percentage_of_actors)
33
+ end
34
+
35
+ it 'returns decorated feature with gate enabled for 10 percent of actors' do
36
+ gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' }
37
+ expect(gate['value']).to eq('10')
38
+ end
19
39
  end
20
-
21
40
  end
22
41
 
23
- describe 'disable' do
42
+ describe 'disable without percentage' do
24
43
  before do
25
44
  flipper[:my_feature].enable_percentage_of_actors(10)
26
- delete '/api/v1/features/my_feature/percentage_of_actors'
45
+ delete '/features/my_feature/percentage_of_actors'
27
46
  end
28
47
 
29
48
  it 'disables gate for feature' do
@@ -32,54 +51,73 @@ RSpec.describe Flipper::Api::V1::Actions::PercentageOfActorsGate do
32
51
 
33
52
  it 'returns decorated feature with gate disabled' do
34
53
  gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' }
35
- expect(gate['value']).to eq(0)
54
+ expect(gate['value']).to eq('0')
55
+ end
56
+ end
57
+
58
+ describe 'disable with percentage' do
59
+ before do
60
+ flipper[:my_feature].enable_percentage_of_actors(10)
61
+ delete '/features/my_feature/percentage_of_actors',
62
+ JSON.generate(percentage: '5'),
63
+ 'CONTENT_TYPE' => 'application/json'
64
+ end
65
+
66
+ it 'returns decorated feature with gate value set to 0 regardless of percentage requested' do
67
+ gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' }
68
+ expect(gate['value']).to eq('0')
36
69
  end
37
70
  end
38
71
 
39
72
  describe 'non-existent feature' do
40
73
  before do
41
- delete '/api/v1/features/my_feature/percentage_of_actors'
74
+ delete '/features/my_feature/percentage_of_actors'
75
+ end
76
+
77
+ it 'disables gate for feature' do
78
+ expect(flipper[:my_feature].enabled_gates).to be_empty
42
79
  end
43
80
 
44
- it '404s with correct error response when feature does not exist' do
45
- expect(last_response.status).to eq(404)
46
- 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' })
81
+ it 'returns decorated feature with gate disabled' do
82
+ expect(last_response.status).to eq(200)
83
+ gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' }
84
+ expect(gate['value']).to eq('0')
47
85
  end
48
86
  end
49
87
 
50
88
  describe 'out of range parameter percentage parameter' do
51
89
  before do
52
90
  flipper[:my_feature].disable
53
- post '/api/v1/features/my_feature/percentage_of_actors', { percentage: '300' }
91
+ post '/features/my_feature/percentage_of_actors', percentage: '300'
54
92
  end
55
93
 
56
94
  it '422s with correct error response when percentage parameter is invalid' do
57
95
  expect(last_response.status).to eq(422)
58
- expect(json_response).to eq({ 'code' => 3, 'message' => 'Percentage must be a positive number less than or equal to 100.', 'more_info' => 'https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference' })
96
+ expect(json_response).to eq(api_positive_percentage_error_response)
59
97
  end
60
98
  end
61
99
 
62
100
  describe 'percentage parameter not an integer' do
63
101
  before do
64
102
  flipper[:my_feature].disable
65
- post '/api/v1/features/my_feature/percentage_of_actors', { percentage: 'foo' }
103
+ post '/features/my_feature/percentage_of_actors', percentage: 'foo'
66
104
  end
67
105
 
68
106
  it '422s with correct error response when percentage parameter is invalid' do
69
107
  expect(last_response.status).to eq(422)
70
- expect(json_response).to eq({ 'code' => 3, 'message' => 'Percentage must be a positive number less than or equal to 100.', 'more_info' => 'https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference' })
108
+ expect(json_response).to eq(api_positive_percentage_error_response)
71
109
  end
72
110
  end
73
111
 
74
112
  describe 'missing percentage parameter' do
75
113
  before do
76
114
  flipper[:my_feature].disable
77
- post '/api/v1/features/my_feature/percentage_of_actors'
115
+ post '/features/my_feature/percentage_of_actors'
78
116
  end
79
117
 
80
118
  it '422s with correct error response when percentage parameter is missing' do
81
119
  expect(last_response.status).to eq(422)
82
- expect(json_response).to eq({ 'code' => 3, 'message' => 'Percentage must be a positive number less than or equal to 100.', 'more_info' => 'https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference' })
120
+ expect(json_response).to eq(api_positive_percentage_error_response)
83
121
  end
84
122
  end
85
123
  end
@@ -6,7 +6,7 @@ RSpec.describe Flipper::Api::V1::Actions::PercentageOfTimeGate do
6
6
  describe 'enable' do
7
7
  before do
8
8
  flipper[:my_feature].disable
9
- post '/api/v1/features/my_feature/percentage_of_time', { percentage: '10' }
9
+ post '/features/my_feature/percentage_of_time', percentage: '10'
10
10
  end
11
11
 
12
12
  it 'enables gate for feature' do
@@ -15,14 +15,14 @@ RSpec.describe Flipper::Api::V1::Actions::PercentageOfTimeGate do
15
15
 
16
16
  it 'returns decorated feature with gate enabled for 5% of time' do
17
17
  gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_time' }
18
- expect(gate['value']).to eq(10)
18
+ expect(gate['value']).to eq('10')
19
19
  end
20
20
  end
21
21
 
22
- describe 'disable' do
22
+ describe 'disable without percentage' do
23
23
  before do
24
24
  flipper[:my_feature].enable_percentage_of_time(10)
25
- delete '/api/v1/features/my_feature/percentage_of_time'
25
+ delete '/features/my_feature/percentage_of_time'
26
26
  end
27
27
 
28
28
  it 'disables gate for feature' do
@@ -31,54 +31,73 @@ RSpec.describe Flipper::Api::V1::Actions::PercentageOfTimeGate do
31
31
 
32
32
  it 'returns decorated feature with gate disabled' do
33
33
  gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_time' }
34
- expect(gate['value']).to eq(0)
34
+ expect(gate['value']).to eq('0')
35
+ end
36
+ end
37
+
38
+ describe 'disable with percentage' do
39
+ before do
40
+ flipper[:my_feature].enable_percentage_of_time(10)
41
+ delete '/features/my_feature/percentage_of_time',
42
+ JSON.generate(percentage: '5'),
43
+ 'CONTENT_TYPE' => 'application/json'
44
+ end
45
+
46
+ it 'returns decorated feature with gate value set to 0 regardless of percentage requested' do
47
+ expect(last_response.status).to eq(200)
48
+ gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_time' }
49
+ expect(gate['value']).to eq('0')
35
50
  end
36
51
  end
37
52
 
38
53
  describe 'non-existent feature' do
39
54
  before do
40
- delete '/api/v1/features/my_feature/percentage_of_time'
55
+ delete '/features/my_feature/percentage_of_time'
41
56
  end
42
57
 
43
- it '404s with correct error response when feature does not exist' do
44
- expect(last_response.status).to eq(404)
45
- 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' })
58
+ it 'disables gate for feature' do
59
+ expect(flipper[:my_feature].enabled_gates).to be_empty
60
+ end
61
+
62
+ it 'returns decorated feature with gate disabled' do
63
+ gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_time' }
64
+ expect(gate['value']).to eq('0')
46
65
  end
47
66
  end
48
67
 
49
68
  describe 'out of range parameter percentage parameter' do
50
69
  before do
51
70
  flipper[:my_feature].disable
52
- post '/api/v1/features/my_feature/percentage_of_time', { percentage: '300' }
71
+ post '/features/my_feature/percentage_of_time', percentage: '300'
53
72
  end
54
73
 
55
74
  it '422s with correct error response when percentage parameter is invalid' do
56
75
  expect(last_response.status).to eq(422)
57
- expect(json_response).to eq({ 'code' => 3, 'message' => 'Percentage must be a positive number less than or equal to 100.', 'more_info' => 'https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference' })
76
+ expect(json_response).to eq(api_positive_percentage_error_response)
58
77
  end
59
78
  end
60
79
 
61
80
  describe 'percentage parameter not an integer' do
62
81
  before do
63
82
  flipper[:my_feature].disable
64
- post '/api/v1/features/my_feature/percentage_of_time', { percentage: 'foo' }
83
+ post '/features/my_feature/percentage_of_time', percentage: 'foo'
65
84
  end
66
85
 
67
86
  it '422s with correct error response when percentage parameter is invalid' do
68
87
  expect(last_response.status).to eq(422)
69
- expect(json_response).to eq({ 'code' => 3, 'message' => 'Percentage must be a positive number less than or equal to 100.', 'more_info' => 'https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference' })
88
+ expect(json_response).to eq(api_positive_percentage_error_response)
70
89
  end
71
90
  end
72
91
 
73
92
  describe 'missing percentage parameter' do
74
93
  before do
75
94
  flipper[:my_feature].disable
76
- post '/api/v1/features/my_feature/percentage_of_time'
95
+ post '/features/my_feature/percentage_of_time'
77
96
  end
78
97
 
79
98
  it '422s with correct error response when percentage parameter is missing' do
80
99
  expect(last_response.status).to eq(422)
81
- expect(json_response).to eq({ 'code' => 3, 'message' => 'Percentage must be a positive number less than or equal to 100.', 'more_info' => 'https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference' })
100
+ expect(json_response).to eq(api_positive_percentage_error_response)
82
101
  end
83
102
  end
84
103
  end
@@ -0,0 +1,55 @@
1
+ require 'helper'
2
+
3
+ RSpec.describe Flipper::Api do
4
+ context 'when initialized with flipper instance and flipper instance in env' do
5
+ let(:app) { build_api(flipper) }
6
+
7
+ it 'uses env instance over initialized instance' do
8
+ flipper[:built_a].enable
9
+ flipper[:built_b].disable
10
+
11
+ env_flipper = build_flipper
12
+ env_flipper[:env_a].enable
13
+ env_flipper[:env_b].disable
14
+
15
+ params = {}
16
+ env = {
17
+ 'flipper' => env_flipper,
18
+ }
19
+ get '/features', params, env
20
+
21
+ expect(last_response.status).to eq(200)
22
+ feature_names = json_response.fetch('features').map { |feature| feature.fetch('key') }
23
+ expect(feature_names).to eq(%w(env_a env_b))
24
+ end
25
+ end
26
+
27
+ context 'when initialized without flipper instance but flipper instance in env' do
28
+ let(:app) { described_class.app }
29
+
30
+ it 'uses env instance' do
31
+ flipper[:a].enable
32
+ flipper[:b].disable
33
+
34
+ params = {}
35
+ env = {
36
+ 'flipper' => flipper,
37
+ }
38
+ get '/features', params, env
39
+
40
+ expect(last_response.status).to eq(200)
41
+ feature_names = json_response.fetch('features').map { |feature| feature.fetch('key') }
42
+ expect(feature_names).to eq(%w(a b))
43
+ end
44
+ end
45
+
46
+ context "when request does not match any api routes" do
47
+ let(:app) { build_api(flipper) }
48
+
49
+ it "returns 404" do
50
+ get '/gibberish'
51
+ expect(last_response.status).to eq(404)
52
+ expect(json_response).to eq({})
53
+ end
54
+ end
55
+ end