rails_api_kit 1.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.
@@ -0,0 +1,335 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe UsersController, type: :request do
4
+ describe 'GET /users' do
5
+ let!(:user) { }
6
+ let(:params) do
7
+ {
8
+ page: { number: 'Nan', size: 'NaN' },
9
+ sort: '-created_at'
10
+ }
11
+ end
12
+
13
+ before do
14
+ get(users_path, params: params, headers: api_headers)
15
+ end
16
+
17
+ it do
18
+ expect(response_json['data'].size).to eq(0)
19
+ expect(response_json['meta'])
20
+ .to eq(
21
+ 'many' => true,
22
+ 'pagination' => {
23
+ 'current' => 1,
24
+ 'total_page' => 1,
25
+ 'total_count' => 0
26
+ }
27
+ )
28
+ end
29
+
30
+ def query_str(parms, page: nil)
31
+ parms = parms.deep_merge(page: { number: page }) if page
32
+
33
+ arg_params = parms.dup
34
+ if page == 1
35
+ arg_params.delete(:page)
36
+ end
37
+
38
+ "?#{CGI.unescape(arg_params.to_query)}"
39
+ end
40
+
41
+ context 'with users' do
42
+ let(:first_user) { create_user }
43
+ let(:second_user) { create_user }
44
+ let(:third_user) { create_user }
45
+ let(:users) { [ first_user, second_user, third_user ] }
46
+ let(:user) { users.last }
47
+
48
+ context 'returns users with pagination links' do
49
+ it do
50
+ expect(response).to have_http_status(:ok)
51
+ expect(response_json['data'].size).to eq(3)
52
+ expect(response_json['data'][0]['id']).to eql(third_user.id)
53
+ expect(response_json['data'][1]['id']).to eql(second_user.id)
54
+ expect(response_json['data'][2]['id']).to eql(first_user.id)
55
+
56
+ expect(response_json).to have_link('current')
57
+ expect(response_json).to have_link(:prev)
58
+ expect(response_json).to have_link(:next)
59
+ expect(response_json).to have_link(:first)
60
+ expect(response_json).to have_link(:last)
61
+
62
+ query = CGI.unescape(params.except(:page).to_query)
63
+ expect(URI.parse(response_json['links']['current']).query).to eq(query)
64
+ end
65
+
66
+ context 'on page 2 out of 3' do
67
+ let(:as_list) { }
68
+ let(:params) do
69
+ {
70
+ page: { number: 2, size: 1 },
71
+ sort: '-created_at',
72
+ as_list: as_list
73
+ }.reject { |_k, _v| _v.blank? }
74
+ end
75
+
76
+ context 'on an array of resources' do
77
+ let(:as_list) { true }
78
+
79
+ it 'should exclude' do
80
+ expect(response).to have_http_status(:ok)
81
+ expect(response_json['data'].size).to eq(1)
82
+ expect(response_json['data'][0]['id']).to eql(second_user.id)
83
+
84
+ expect(response_json['meta']['pagination']).to eq(
85
+ 'current' => 2,
86
+ 'total_count' => 3,
87
+ 'total_page' => 3
88
+ )
89
+ expect(response_json['links']).to eq(
90
+ 'current' => query_str(params),
91
+ 'first' => query_str(params, page: 1),
92
+ 'prev' => query_str(params, page: 1),
93
+ 'next' => query_str(params, page: 3),
94
+ 'last' => query_str(params, page: 3),
95
+ )
96
+ end
97
+ end
98
+
99
+ it do
100
+ expect(response).to have_http_status(:ok)
101
+ expect(response_json['data'].size).to eq(1)
102
+ expect(response_json['data'][0]['id']).to eql(second_user.id)
103
+
104
+ expect(response_json['meta']['pagination']).to eq(
105
+ 'current' => 2,
106
+ 'total_count' => 3,
107
+ 'total_page' => 3
108
+ )
109
+ expect(response_json['links']).to eq(
110
+ 'current' => query_str(params),
111
+ 'first' => query_str(params, page: 1),
112
+ 'prev' => query_str(params, page: 1),
113
+ 'next' => query_str(params, page: 3),
114
+ 'last' => query_str(params, page: 3),
115
+ )
116
+
117
+ expect(response_json).to have_link(:current)
118
+ expect(response_json).to have_link(:prev)
119
+ expect(response_json).to have_link(:first)
120
+ expect(response_json).to have_link(:next)
121
+ expect(response_json).to have_link(:last)
122
+
123
+ qry = CGI.unescape(params.to_query)
124
+ expect(URI.parse(response_json['links']['current']).query)
125
+ .to eq(qry)
126
+
127
+ qry = CGI.unescape(params.deep_merge(page: { number: 2 }).to_query)
128
+ expect(URI.parse(response_json['links']['current']).query)
129
+ .to eq(qry)
130
+
131
+ qry = CGI.unescape(params.except(:page).to_query)
132
+ expect(URI.parse(response_json['links']['prev']).query).to eq(qry)
133
+ expect(URI.parse(response_json['links']['first']).query).to eq(qry)
134
+
135
+ qry = CGI.unescape(params.deep_merge(page: { number: 3 }).to_query)
136
+ expect(URI.parse(response_json['links']['next']).query).to eq(qry)
137
+ expect(URI.parse(response_json['links']['last']).query).to eq(qry)
138
+ end
139
+ end
140
+
141
+ context 'on page 3 out of 3' do
142
+ let(:params) do
143
+ {
144
+ page: { number: 3, size: 1 }
145
+ }
146
+ end
147
+
148
+ it do
149
+ expect(response).to have_http_status(:ok)
150
+ expect(response_json['data'].size).to eq(1)
151
+
152
+ expect(response_json['meta']['pagination']).to eq(
153
+ 'current' => 3,
154
+ 'total_count' => 3,
155
+ 'total_page' => 3
156
+ )
157
+ expect(response_json['links']).to eq(
158
+ 'current' => query_str(params),
159
+ 'first' => query_str(params, page: 1),
160
+ 'prev' => query_str(params, page: 2),
161
+ 'next' => nil,
162
+ 'last' => query_str(params, page: 3),
163
+ )
164
+
165
+ expect(response_json).to have_link(:current)
166
+ expect(response_json).to have_link(:prev)
167
+ expect(response_json).to have_link(:first)
168
+ expect(response_json).to have_link(:next)
169
+ expect(response_json).to have_link(:last)
170
+
171
+ expect(URI.parse(response_json['links']['current']).query)
172
+ .to eq(CGI.unescape(params.to_query))
173
+
174
+ qry = CGI.unescape(params.deep_merge(page: { number: 2 }).to_query)
175
+ expect(URI.parse(response_json['links']['prev']).query).to eq(qry)
176
+
177
+ qry = CGI.unescape(params.except(:page).to_query)
178
+ expect(URI.parse(response_json['links']['first']).query).to eq(qry)
179
+ end
180
+ end
181
+
182
+
183
+ context 'on page empty' do
184
+ let(:params) do
185
+ {
186
+ page: { number: 3 }
187
+ }
188
+ end
189
+
190
+ it do
191
+ expect(response).to have_http_status(:ok)
192
+ expect(response_json['data'].size).to eq(0)
193
+ expect(response_json['data']).to eql([])
194
+
195
+ expect(response_json['meta']['pagination']).to eq(
196
+ 'current' => 3,
197
+ 'total_count' => 3,
198
+ 'total_page' => 1
199
+ )
200
+ expect(response_json['links']).to eq(
201
+ 'current' => query_str(params),
202
+ 'first' => query_str(params, page: 1),
203
+ 'prev' => query_str(params, page: 2),
204
+ 'next' => nil,
205
+ 'last' => '?',
206
+ )
207
+
208
+ expect(response_json).to have_link(:current)
209
+ expect(response_json).to have_link(:prev)
210
+ expect(response_json).to have_link(:first)
211
+ expect(response_json).to have_link(:next)
212
+ expect(response_json).to have_link(:last)
213
+
214
+ expect(URI.parse(response_json['links']['current']).query)
215
+ .to eq(CGI.unescape(params.to_query))
216
+
217
+ qry = CGI.unescape(params.deep_merge(page: { number: 2 }).to_query)
218
+ expect(URI.parse(response_json['links']['prev']).query).to eq(qry)
219
+
220
+ qry = CGI.unescape(params.except(:page).to_query)
221
+ expect(URI.parse(response_json['links']['first']).query).to eq(qry)
222
+ end
223
+ end
224
+
225
+ context 'on paging beyond the last page' do
226
+ let(:as_list) { }
227
+ let(:params) do
228
+ {
229
+ page: { number: 5, size: 1 },
230
+ as_list: as_list
231
+ }.reject { |_k, _v| _v.blank? }
232
+ end
233
+
234
+ context 'on an array of resources' do
235
+ let(:as_list) { true }
236
+
237
+ it do
238
+ expect(response).to have_http_status(:ok)
239
+ expect(response_json['data'].size).to eq(0)
240
+
241
+ expect(response_json['meta']['pagination']).to eq(
242
+ 'current' => 5,
243
+ 'total_count' => 3,
244
+ 'total_page' => 3
245
+ )
246
+ expect(response_json['links']).to eq(
247
+ 'current' => query_str(params),
248
+ 'first' => query_str(params, page: 1),
249
+ 'prev' => query_str(params, page: 4),
250
+ 'next' => nil,
251
+ 'last' => query_str(params, page: 3),
252
+ )
253
+ end
254
+ end
255
+
256
+ it do
257
+ expect(response).to have_http_status(:ok)
258
+ expect(response_json['data'].size).to eq(0)
259
+
260
+ expect(response_json['meta']['pagination']).to eq(
261
+ 'current' => 5,
262
+ 'total_count' => 3,
263
+ 'total_page' => 3
264
+ )
265
+ expect(response_json['links']).to eq(
266
+ 'current' => query_str(params),
267
+ 'first' => query_str(params, page: 1),
268
+ 'prev' => query_str(params, page: 4),
269
+ 'next' => nil,
270
+ 'last' => query_str(params, page: 3),
271
+ )
272
+
273
+ expect(response_json).to have_link(:current)
274
+ expect(response_json).to have_link(:prev)
275
+ expect(response_json).to have_link(:first)
276
+ expect(response_json).to have_link(:next)
277
+ expect(response_json).to have_link(:last)
278
+
279
+ expect(URI.parse(response_json['links']['current']).query)
280
+ .to eq(CGI.unescape(params.to_query))
281
+
282
+ qry = CGI.unescape(params.deep_merge(page: { number: 4 }).to_query)
283
+ expect(URI.parse(response_json['links']['prev']).query).to eq(qry)
284
+
285
+ qry = CGI.unescape(params.except(:page).to_query)
286
+ expect(URI.parse(response_json['links']['first']).query).to eq(qry)
287
+ end
288
+ end
289
+
290
+ context 'on page 1 out of 3' do
291
+ let(:params) do
292
+ {
293
+ page: { size: 1, number: 1 },
294
+ sort: '-created_at'
295
+ }
296
+ end
297
+
298
+ it do
299
+ expect(response).to have_http_status(:ok)
300
+ expect(response_json['data'].size).to eq(1)
301
+ expect(response_json['data'][0]['id']).to eql(third_user.id)
302
+
303
+ expect(response_json['meta']['pagination']).to eq(
304
+ 'current' => 1,
305
+ 'total_count' => 3,
306
+ 'total_page' => 3,
307
+ )
308
+ expect(response_json['links']).to eq(
309
+ 'current' => query_str(params, page: 1),
310
+ 'first' => query_str(params, page: 1),
311
+ 'prev' => nil,
312
+ 'next' => query_str(params, page: 2),
313
+ 'last' => query_str(params, page: 3),
314
+ )
315
+
316
+ expect(response_json).to have_link(:prev)
317
+ expect(response_json).to have_link(:first)
318
+ expect(response_json).to have_link(:next)
319
+ expect(response_json).to have_link(:current)
320
+ expect(response_json).to have_link(:last)
321
+
322
+ expect(URI.parse(response_json['links']['current']).query)
323
+ .to eq(CGI.unescape(params.except(:page).to_query))
324
+
325
+ qry = CGI.unescape(params.deep_merge(page: { number: 2 }).to_query)
326
+ expect(URI.parse(response_json['links']['next']).query).to eq(qry)
327
+
328
+ qry = CGI.unescape(params.deep_merge(page: { number: 3 }).to_query)
329
+ expect(URI.parse(response_json['links']['last']).query).to eq(qry)
330
+ end
331
+ end
332
+ end
333
+ end
334
+ end
335
+ end
@@ -0,0 +1,87 @@
1
+ require 'bundler/setup'
2
+ require 'simplecov'
3
+
4
+ SimpleCov.start do
5
+ add_group 'Lib', 'lib'
6
+ add_group 'Tests', 'spec'
7
+ end
8
+ SimpleCov.minimum_coverage 90
9
+
10
+ require 'dummy'
11
+ require 'ffaker'
12
+ require 'rspec/rails'
13
+ Dir[File.join(__dir__, 'support', '**', '*.rb')].sort.each { |file| require file }
14
+
15
+ RSpec.configure do |config|
16
+ config.include ApiKit::RSpec
17
+
18
+ config.use_transactional_fixtures = true
19
+ config.mock_with :rspec
20
+ config.filter_run_when_matching :focus
21
+ config.disable_monkey_patching!
22
+
23
+ config.expect_with :rspec do |c|
24
+ c.syntax = :expect
25
+ end
26
+
27
+ # Silence ActiveModelSerializers logs during tests
28
+ if defined?(ActiveModelSerializers)
29
+ ActiveModelSerializers.logger = Logger.new(nil)
30
+ end
31
+ end
32
+
33
+ module RSpecHelpers
34
+ include Dummy.routes.url_helpers
35
+
36
+ # Helper to return valid API headers
37
+ #
38
+ # @return [Hash] the relevant content type &co
39
+ def api_headers
40
+ { 'Content-Type': Mime[:json].to_s }
41
+ end
42
+
43
+ # Parses and returns a deserialized JSON
44
+ #
45
+ # @return [Hash]
46
+ def response_json
47
+ JSON.parse(response.body)
48
+ end
49
+
50
+ # Creates an user
51
+ #
52
+ # @return [User]
53
+ def create_user
54
+ User.create!(
55
+ first_name: FFaker::Name.first_name,
56
+ last_name: FFaker::Name.last_name
57
+ )
58
+ end
59
+
60
+ # Creates a note
61
+ #
62
+ # @return [Note]
63
+ def create_note(user = nil)
64
+ Note.create!(
65
+ title: FFaker::Company.name,
66
+ quantity: rand(10),
67
+ user: (user || create_user)
68
+ )
69
+ end
70
+ end
71
+
72
+ module Rails4RequestMethods
73
+ [ :get, :post, :put, :delete ].each do |method_name|
74
+ define_method(method_name) do |path, named_args|
75
+ super(
76
+ path,
77
+ named_args.delete(:params),
78
+ named_args.delete(:headers)
79
+ )
80
+ end
81
+ end
82
+ end
83
+
84
+ RSpec.configure do |config|
85
+ config.include RSpecHelpers, type: :request
86
+ config.include RSpecHelpers, type: :controller
87
+ end
@@ -0,0 +1,41 @@
1
+ module ApiKit
2
+ module RSpec
3
+ module_function
4
+
5
+ def as_indifferent_hash(doc)
6
+ return doc unless ::RSpec.configuration.apikit_indifferent_hash
7
+
8
+ if doc.respond_to?(:with_indifferent_access)
9
+ return doc.with_indifferent_access
10
+ end
11
+
12
+ JSON.parse(JSON.generate(doc))
13
+ end
14
+ end
15
+ end
16
+
17
+ ::RSpec.configure do |config|
18
+ config.add_setting :apikit_indifferent_hash, default: true
19
+ end
20
+
21
+ ::RSpec::Matchers.define :have_link do |link|
22
+ match do |actual|
23
+ actual = ApiKit::RSpec.as_indifferent_hash(actual)
24
+ actual.key?('links') && actual['links'].key?(link.to_s) &&
25
+ (!@val_set || actual['links'][link.to_s] == @val)
26
+ end
27
+
28
+ chain :with_value do |val|
29
+ @val_set = true
30
+ @val = val
31
+ end
32
+ end
33
+
34
+ ::RSpec::Matchers.define :have_links do |*links|
35
+ match do |actual|
36
+ actual = ApiKit::RSpec.as_indifferent_hash(actual)
37
+ return false unless actual.key?('links')
38
+
39
+ links.all? { |link| actual['links'].key?(link.to_s) }
40
+ end
41
+ end