jsonapi.rb 1.6.0 → 1.7.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,65 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe UsersController, type: :request do
4
+ describe 'GET /users' do
5
+ let!(:user) { }
6
+ let(:params) { }
7
+
8
+ before do
9
+ get(users_path, params: params, headers: jsonapi_headers)
10
+ end
11
+
12
+ context 'with users' do
13
+ let(:first_user) { create_user }
14
+ let(:second_user) { create_user }
15
+ let(:third_user) { create_note.user }
16
+ let(:users) { [first_user, second_user, third_user] }
17
+ let(:user) { users.last }
18
+ let(:note) { third_user.notes.first }
19
+
20
+ context 'returns customers and dasherized first name' do
21
+ let(:params) do
22
+ { upcase: :yes, fields: { unknown: nil } }
23
+ end
24
+
25
+ it do
26
+ expect(response).to have_http_status(:ok)
27
+ expect(response_json['data'].size).to eq(users.size)
28
+
29
+ response_json['data'].each do |item|
30
+ user = users.detect { |u| u.id == item['id'].to_i }
31
+ expect(item).to have_attribute('first_name')
32
+ .with_value(user.first_name.upcase)
33
+ end
34
+ end
35
+ end
36
+
37
+ context 'returns customers included and sparse fields' do
38
+ let(:params) do
39
+ {
40
+ include: 'notes',
41
+ fields: { note: 'title,updated_at' }
42
+ }
43
+ end
44
+
45
+ it do
46
+ expect(response).to have_http_status(:ok)
47
+ expect(response_json['data'].last)
48
+ .to have_relationship(:notes)
49
+ .with_data([
50
+ { 'id' => note.id.to_s, 'type' => 'note' }
51
+ ])
52
+ expect(response_json['included']).to include(
53
+ 'id' => note.id.to_s,
54
+ 'type' => 'note',
55
+ 'relationships' => {},
56
+ 'attributes' => {
57
+ 'title' => note.title,
58
+ 'updated_at' => note.updated_at.as_json
59
+ }
60
+ )
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,101 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe UsersController, type: :request do
4
+ describe '#extract_attributes_and_predicate' do
5
+ context 'mixed attributes (and/or)' do
6
+ it 'extracts ANDs' do
7
+ attributes, predicates = JSONAPI::Filtering
8
+ .extract_attributes_and_predicates('attr1_and_attr2_eq')
9
+ expect(attributes).to eq(['attr1', 'attr2'])
10
+ expect(predicates.size).to eq(1)
11
+ expect(predicates[0].name).to eq('eq')
12
+ end
13
+ end
14
+
15
+ context 'mixed predicates' do
16
+ it 'extracts in order' do
17
+ attributes, predicates = JSONAPI::Filtering
18
+ .extract_attributes_and_predicates('attr1_sum_eq')
19
+ expect(attributes).to eq(['attr1'])
20
+ expect(predicates.size).to eq(2)
21
+ expect(predicates[0].name).to eq('sum')
22
+ expect(predicates[1].name).to eq('eq')
23
+ end
24
+ end
25
+ end
26
+
27
+ describe 'GET /users' do
28
+ let!(:user) { }
29
+ let(:params) { }
30
+
31
+ before do
32
+ get(users_path, params: params, headers: jsonapi_headers)
33
+ end
34
+
35
+ context 'with users' do
36
+ let(:first_user) { create_user }
37
+ let(:second_user) { create_note.user }
38
+ let(:third_user) { create_user }
39
+ let(:users) { [first_user, second_user, third_user] }
40
+ let(:user) { users.last }
41
+
42
+ context 'returns filtered users' do
43
+ let(:params) do
44
+ {
45
+ filter: {
46
+ last_name_or_first_name_cont_any: (
47
+ "#{third_user.first_name[0..5]}%,#{self.class.name}"
48
+ )
49
+ }
50
+ }
51
+ end
52
+
53
+ it do
54
+ expect(response).to have_http_status(:ok)
55
+ expect(response_json['data'].size).to eq(1)
56
+ expect(response_json['data'][0]).to have_id(third_user.id.to_s)
57
+ end
58
+
59
+ context 'with a comma' do
60
+ let(:params) do
61
+ third_user.update(first_name: third_user.first_name + ',')
62
+
63
+ {
64
+ filter: { first_name_eq: third_user.first_name }
65
+ }
66
+ end
67
+
68
+ it do
69
+ expect(response).to have_http_status(:ok)
70
+ expect(response_json['data'].size).to eq(1)
71
+ expect(response_json['data'][0]).to have_id(third_user.id.to_s)
72
+ end
73
+ end
74
+ end
75
+
76
+ context 'returns sorted users by notes quantity sum' do
77
+ let(:params) do
78
+ { sort: '-notes_quantity_sum' }
79
+ end
80
+
81
+ it do
82
+ expect(response).to have_http_status(:ok)
83
+ expect(response_json['data'].size).to eq(3)
84
+ expect(response_json['data'][0]).to have_id(second_user.id.to_s)
85
+ end
86
+ end
87
+
88
+ context 'returns sorted users by notes' do
89
+ let(:params) do
90
+ { sort: '-notes_created_at' }
91
+ end
92
+
93
+ it do
94
+ expect(response).to have_http_status(:ok)
95
+ expect(response_json['data'].size).to eq(3)
96
+ expect(response_json['data'][0]).to have_id(second_user.id.to_s)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,246 @@
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: jsonapi_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
+ 'records' => 0
25
+ }
26
+ )
27
+ end
28
+
29
+ context 'with users' do
30
+ let(:first_user) { create_user }
31
+ let(:second_user) { create_user }
32
+ let(:third_user) { create_user }
33
+ let(:users) { [first_user, second_user, third_user] }
34
+ let(:user) { users.last }
35
+
36
+ context 'returns users with pagination links' do
37
+ it do
38
+ expect(response).to have_http_status(:ok)
39
+ expect(response_json['data'].size).to eq(3)
40
+ expect(response_json['data'][0]).to have_id(third_user.id.to_s)
41
+ expect(response_json['data'][1]).to have_id(second_user.id.to_s)
42
+ expect(response_json['data'][2]).to have_id(first_user.id.to_s)
43
+
44
+ expect(response_json).to have_link('self')
45
+ expect(response_json).not_to have_link(:prev)
46
+ expect(response_json).not_to have_link(:next)
47
+ expect(response_json).not_to have_link(:first)
48
+ expect(response_json).not_to have_link(:last)
49
+
50
+ expect(URI.parse(response_json['links']['self']).query)
51
+ .to eq(CGI.unescape(params.to_query))
52
+ end
53
+
54
+ context 'on page 2 out of 3' do
55
+ let(:as_list) { }
56
+ let(:params) do
57
+ {
58
+ page: { number: 2, size: 1 },
59
+ sort: '-created_at',
60
+ as_list: as_list
61
+ }.reject { |_k, _v| _v.blank? }
62
+ end
63
+
64
+ context 'on an array of resources' do
65
+ let(:as_list) { true }
66
+
67
+ it do
68
+ expect(response).to have_http_status(:ok)
69
+ expect(response_json['data'].size).to eq(1)
70
+ expect(response_json['data'][0]).to have_id(second_user.id.to_s)
71
+
72
+ expect(response_json['meta']['pagination']).to eq(
73
+ 'current' => 2,
74
+ 'first' => 1,
75
+ 'prev' => 1,
76
+ 'next' => 3,
77
+ 'last' => 3,
78
+ 'records' => 3
79
+ )
80
+ end
81
+ end
82
+
83
+ it do
84
+ expect(response).to have_http_status(:ok)
85
+ expect(response_json['data'].size).to eq(1)
86
+ expect(response_json['data'][0]).to have_id(second_user.id.to_s)
87
+
88
+ expect(response_json['meta']['pagination']).to eq(
89
+ 'current' => 2,
90
+ 'first' => 1,
91
+ 'prev' => 1,
92
+ 'next' => 3,
93
+ 'last' => 3,
94
+ 'records' => 3
95
+ )
96
+
97
+ expect(response_json).to have_link(:self)
98
+ expect(response_json).to have_link(:prev)
99
+ expect(response_json).to have_link(:first)
100
+ expect(response_json).to have_link(:next)
101
+ expect(response_json).to have_link(:last)
102
+
103
+ qry = CGI.unescape(params.to_query)
104
+ expect(URI.parse(response_json['links']['self']).query).to eq(qry)
105
+
106
+ qry = CGI.unescape(params.deep_merge(page: { number: 2 }).to_query)
107
+ expect(URI.parse(response_json['links']['self']).query).to eq(qry)
108
+
109
+ qry = CGI.unescape(params.deep_merge(page: { number: 1 }).to_query)
110
+ expect(URI.parse(response_json['links']['prev']).query).to eq(qry)
111
+ expect(URI.parse(response_json['links']['first']).query).to eq(qry)
112
+
113
+ qry = CGI.unescape(params.deep_merge(page: { number: 3 }).to_query)
114
+ expect(URI.parse(response_json['links']['next']).query).to eq(qry)
115
+ expect(URI.parse(response_json['links']['last']).query).to eq(qry)
116
+ end
117
+ end
118
+
119
+ context 'on page 3 out of 3' do
120
+ let(:params) do
121
+ {
122
+ page: { number: 3, size: 1 }
123
+ }
124
+ end
125
+
126
+ it do
127
+ expect(response).to have_http_status(:ok)
128
+ expect(response_json['data'].size).to eq(1)
129
+
130
+ expect(response_json['meta']['pagination']).to eq(
131
+ 'current' => 3,
132
+ 'first' => 1,
133
+ 'prev' => 2,
134
+ 'records' => 3
135
+ )
136
+
137
+ expect(response_json).to have_link(:self)
138
+ expect(response_json).to have_link(:prev)
139
+ expect(response_json).to have_link(:first)
140
+ expect(response_json).not_to have_link(:next)
141
+ expect(response_json).not_to have_link(:last)
142
+
143
+ expect(URI.parse(response_json['links']['self']).query)
144
+ .to eq(CGI.unescape(params.to_query))
145
+
146
+ qry = CGI.unescape(params.deep_merge(page: { number: 2 }).to_query)
147
+ expect(URI.parse(response_json['links']['prev']).query).to eq(qry)
148
+
149
+ qry = CGI.unescape(params.deep_merge(page: { number: 1 }).to_query)
150
+ expect(URI.parse(response_json['links']['first']).query).to eq(qry)
151
+ end
152
+ end
153
+
154
+ context 'on paging beyond the last page' do
155
+ let(:as_list) { }
156
+ let(:params) do
157
+ {
158
+ page: { number: 5, size: 1 },
159
+ as_list: as_list
160
+ }.reject { |_k, _v| _v.blank? }
161
+ end
162
+
163
+ context 'on an array of resources' do
164
+ let(:as_list) { true }
165
+
166
+ it do
167
+ expect(response).to have_http_status(:ok)
168
+ expect(response_json['data'].size).to eq(0)
169
+
170
+ expect(response_json['meta']['pagination']).to eq(
171
+ 'current' => 5,
172
+ 'first' => 1,
173
+ 'prev' => 4,
174
+ 'records' => 3
175
+ )
176
+ end
177
+ end
178
+
179
+ it do
180
+ expect(response).to have_http_status(:ok)
181
+ expect(response_json['data'].size).to eq(0)
182
+
183
+ expect(response_json['meta']['pagination']).to eq(
184
+ 'current' => 5,
185
+ 'first' => 1,
186
+ 'prev' => 4,
187
+ 'records' => 3
188
+ )
189
+
190
+ expect(response_json).to have_link(:self)
191
+ expect(response_json).to have_link(:prev)
192
+ expect(response_json).to have_link(:first)
193
+ expect(response_json).not_to have_link(:next)
194
+ expect(response_json).not_to have_link(:last)
195
+
196
+ expect(URI.parse(response_json['links']['self']).query)
197
+ .to eq(CGI.unescape(params.to_query))
198
+
199
+ qry = CGI.unescape(params.deep_merge(page: { number: 4 }).to_query)
200
+ expect(URI.parse(response_json['links']['prev']).query).to eq(qry)
201
+
202
+ qry = CGI.unescape(params.deep_merge(page: { number: 1 }).to_query)
203
+ expect(URI.parse(response_json['links']['first']).query).to eq(qry)
204
+ end
205
+ end
206
+
207
+ context 'on page 1 out of 3' do
208
+ let(:params) do
209
+ {
210
+ page: { size: 1 },
211
+ sort: '-created_at'
212
+ }
213
+ end
214
+
215
+ it do
216
+ expect(response).to have_http_status(:ok)
217
+ expect(response_json['data'].size).to eq(1)
218
+ expect(response_json['data'][0]).to have_id(third_user.id.to_s)
219
+
220
+ expect(response_json['meta']['pagination']).to eq(
221
+ 'current' => 1,
222
+ 'next' => 2,
223
+ 'last' => 3,
224
+ 'records' => 3
225
+ )
226
+
227
+ expect(response_json).not_to have_link(:prev)
228
+ expect(response_json).not_to have_link(:first)
229
+ expect(response_json).to have_link(:next)
230
+ expect(response_json).to have_link(:self)
231
+ expect(response_json).to have_link(:last)
232
+
233
+ expect(URI.parse(response_json['links']['self']).query)
234
+ .to eq(CGI.unescape(params.to_query))
235
+
236
+ qry = CGI.unescape(params.deep_merge(page: { number: 2 }).to_query)
237
+ expect(URI.parse(response_json['links']['next']).query).to eq(qry)
238
+
239
+ qry = CGI.unescape(params.deep_merge(page: { number: 3 }).to_query)
240
+ expect(URI.parse(response_json['links']['last']).query).to eq(qry)
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
246
+ 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
+ require 'jsonapi/rspec'
14
+
15
+ RSpec.configure do |config|
16
+ config.include JSONAPI::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
+ end
27
+
28
+ module RSpecHelpers
29
+ include Dummy.routes.url_helpers
30
+
31
+ # Helper to return JSONAPI valid headers
32
+ #
33
+ # @return [Hash] the relevant content type &co
34
+ def jsonapi_headers
35
+ { 'Content-Type': Mime[:jsonapi].to_s }
36
+ end
37
+
38
+ # Parses and returns a deserialized JSON
39
+ #
40
+ # @return [Hash]
41
+ def response_json
42
+ JSON.parse(response.body)
43
+ end
44
+
45
+ # Creates an user
46
+ #
47
+ # @return [User]
48
+ def create_user
49
+ User.create!(
50
+ first_name: FFaker::Name.first_name,
51
+ last_name: FFaker::Name.last_name
52
+ )
53
+ end
54
+
55
+ # Creates a note
56
+ #
57
+ # @return [Note]
58
+ def create_note(user = nil)
59
+ Note.create!(
60
+ title: FFaker::Company.name,
61
+ quantity: rand(10),
62
+ user: (user || create_user)
63
+ )
64
+ end
65
+ end
66
+
67
+ module Rails4RequestMethods
68
+ [:get, :post, :put, :delete].each do |method_name|
69
+ define_method(method_name) do |path, named_args|
70
+ super(
71
+ path,
72
+ named_args.delete(:params),
73
+ named_args.delete(:headers)
74
+ )
75
+ end
76
+ end
77
+ end
78
+
79
+ RSpec.configure do |config|
80
+ config.include RSpecHelpers, type: :request
81
+ config.include RSpecHelpers, type: :controller
82
+
83
+ if ::Rails::VERSION::MAJOR == 4
84
+ config.include Rails4RequestMethods, type: :request
85
+ config.include Rails4RequestMethods, type: :controller
86
+ end
87
+ end