Line 0
Link Here
|
0 |
- |
1 |
#!/usr/bin/env perl |
|
|
2 |
|
3 |
# This file is part of Koha. |
4 |
# |
5 |
# Koha is free software; you can redistribute it and/or modify it under the |
6 |
# terms of the GNU General Public License as published by the Free Software |
7 |
# Foundation; either version 3 of the License, or (at your option) any later |
8 |
# version. |
9 |
# |
10 |
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY |
11 |
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR |
12 |
# A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
13 |
# |
14 |
# You should have received a copy of the GNU General Public License along |
15 |
# with Koha; if not, write to the Free Software Foundation, Inc., |
16 |
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
17 |
|
18 |
use Modern::Perl; |
19 |
|
20 |
use Test::More tests => 2; |
21 |
use Test::Mojo; |
22 |
use Test::Warn; |
23 |
|
24 |
use t::lib::TestBuilder; |
25 |
use t::lib::Mocks; |
26 |
|
27 |
use C4::Auth; |
28 |
use Koha::Biblio::Availability; |
29 |
use Koha::Item::Availability; |
30 |
use Koha::Database; |
31 |
|
32 |
require t::db_dependent::Koha::Availability::Helpers; |
33 |
|
34 |
my $schema = Koha::Database->new->schema; |
35 |
my $builder = t::lib::TestBuilder->new; |
36 |
|
37 |
# FIXME: sessionStorage defaults to mysql, but it seems to break transaction handling |
38 |
# this affects the other REST api tests |
39 |
t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' ); |
40 |
|
41 |
my $remote_address = '127.0.0.1'; |
42 |
my $t = Test::Mojo->new('Koha::REST::V1'); |
43 |
|
44 |
subtest '/availability/biblio' => sub { |
45 |
plan tests => 2; |
46 |
|
47 |
subtest '/hold' => sub { |
48 |
plan tests => 30; |
49 |
|
50 |
$schema->storage->txn_begin; |
51 |
|
52 |
set_default_circulation_rules(); |
53 |
set_default_system_preferences(); |
54 |
|
55 |
my $item = build_a_test_item(); |
56 |
my $item2 = build_a_test_item( |
57 |
Koha::Biblios->find($item->biblionumber), |
58 |
Koha::Biblioitems->find($item->biblioitemnumber) |
59 |
); |
60 |
my ($patron, $session_id) = create_user_and_session(); |
61 |
$patron = Koha::Patrons->find($patron); |
62 |
my $route = '/api/v1/availability/biblio/hold'; |
63 |
my $tx = $t->ua->build_tx( GET => $route . '?biblionumber='.$item->biblionumber ); |
64 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
65 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
66 |
$t->request_ok($tx) |
67 |
->status_is(200) |
68 |
->json_has('/0/availability') |
69 |
->json_is('/0/availability/available' => Mojo::JSON->true) |
70 |
->json_is('/0/item_availabilities/0/availability/available' => Mojo::JSON->true) |
71 |
->json_is('/0/item_availabilities/1/availability/available' => Mojo::JSON->true); |
72 |
|
73 |
$tx = $t->ua->build_tx( GET => $route . '?biblionumber='.$item->biblionumber.'&limit_items=1' ); |
74 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
75 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
76 |
$t->request_ok($tx) |
77 |
->status_is(200) |
78 |
->json_has('/0/availability') |
79 |
->json_has('/0/item_availabilities/0') |
80 |
->json_hasnt('/0/item_availabilities/1'); |
81 |
|
82 |
$patron->gonenoaddress('1')->store; |
83 |
$item2->notforloan('1')->store; |
84 |
$tx = $t->ua->build_tx( GET => $route . '?biblionumber='.$item->biblionumber ); |
85 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
86 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
87 |
$t->request_ok($tx) |
88 |
->status_is(200) |
89 |
->json_has('/0/availability') |
90 |
->json_is('/0/availability/available' => Mojo::JSON->false) |
91 |
->json_is('/0/availability/unavailabilities/Patron::GoneNoAddress' => {}) |
92 |
->json_is('/0/item_availabilities/0/itemnumber' => $item->itemnumber) |
93 |
->json_is('/0/item_availabilities/0/availability/available' => Mojo::JSON->true) |
94 |
->json_is('/0/item_availabilities/1/itemnumber' => $item2->itemnumber) |
95 |
->json_is('/0/item_availabilities/1/availability/available' => Mojo::JSON->false) |
96 |
->json_is('/0/item_availabilities/1/availability/unavailabilities/Item::NotForLoan' => { |
97 |
code => "Not For Loan", |
98 |
status => 1, |
99 |
}); |
100 |
$patron->gonenoaddress('0')->store; |
101 |
$item2->notforloan('0')->store; |
102 |
|
103 |
my $patron2 = build_a_test_patron(); |
104 |
$tx = $t->ua->build_tx( GET => $route . '?biblionumber='.$item->biblionumber.'&borrowernumber='.$patron2->borrowernumber ); |
105 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
106 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
107 |
$t->request_ok($tx) |
108 |
->status_is(403); |
109 |
|
110 |
my $branch = Koha::Libraries->find( |
111 |
$builder->build({ source => 'Branch' })->{'branchcode'}); |
112 |
t::lib::Mocks::mock_preference('UseBranchTransferLimits', 1); |
113 |
t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'itemtype'); |
114 |
C4::Circulation::CreateBranchTransferLimit( |
115 |
$branch->branchcode, |
116 |
$item->holdingbranch, |
117 |
$item->effective_itemtype |
118 |
); |
119 |
|
120 |
$tx = $t->ua->build_tx( GET => $route . '?biblionumber='.$item->biblionumber.'&branchcode='.$branch->branchcode ); |
121 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
122 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
123 |
$t->request_ok($tx) |
124 |
->status_is(200) |
125 |
->json_has('/0/availability') |
126 |
->json_is('/0/availability/available' => Mojo::JSON->true) |
127 |
->json_is('/0/item_availabilities/0/availability/available' => Mojo::JSON->true) |
128 |
->json_is('/0/item_availabilities/1/availability/available' => Mojo::JSON->false) |
129 |
->json_is('/0/item_availabilities/1/availability/unavailabilities/Item::CannotBeTransferred' => { |
130 |
from_library => $item->holdingbranch, |
131 |
to_library => $branch->branchcode,}); |
132 |
|
133 |
$schema->storage->txn_rollback; |
134 |
}; |
135 |
|
136 |
subtest '/search' => sub { |
137 |
plan tests => 15; |
138 |
|
139 |
$schema->storage->txn_begin; |
140 |
|
141 |
set_default_circulation_rules(); |
142 |
set_default_system_preferences(); |
143 |
|
144 |
my $item = build_a_test_item(); |
145 |
my $item2 = build_a_test_item( |
146 |
Koha::Biblios->find($item->biblionumber), |
147 |
Koha::Biblioitems->find($item->biblioitemnumber) |
148 |
); |
149 |
my $route = '/api/v1/availability/biblio/search'; |
150 |
my $tx = $t->ua->build_tx( GET => $route . '?biblionumber='.$item->biblionumber ); |
151 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
152 |
$t->request_ok($tx) |
153 |
->status_is(200) |
154 |
->json_has('/0/availability') |
155 |
->json_is('/0/availability/available' => Mojo::JSON->true) |
156 |
->json_is('/0/item_availabilities/0/availability/available' => Mojo::JSON->true) |
157 |
->json_is('/0/item_availabilities/1/availability/available' => Mojo::JSON->true); |
158 |
|
159 |
$item2->notforloan('1')->store; |
160 |
$tx = $t->ua->build_tx( GET => $route . '?biblionumber='.$item->biblionumber ); |
161 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
162 |
$t->request_ok($tx) |
163 |
->status_is(200) |
164 |
->json_has('/0/availability') |
165 |
->json_is('/0/availability/available' => Mojo::JSON->true) |
166 |
->json_is('/0/item_availabilities/0/itemnumber' => $item->itemnumber) |
167 |
->json_is('/0/item_availabilities/0/availability/available' => Mojo::JSON->true) |
168 |
->json_is('/0/item_availabilities/1/itemnumber' => $item2->itemnumber) |
169 |
->json_is('/0/item_availabilities/1/availability/available' => Mojo::JSON->false) |
170 |
->json_is('/0/item_availabilities/1/availability/unavailabilities/Item::NotForLoan' => { |
171 |
code => "Not For Loan", |
172 |
status => 1, |
173 |
}); |
174 |
|
175 |
$schema->storage->txn_rollback; |
176 |
}; |
177 |
}; |
178 |
|
179 |
subtest '/availability/item' => sub { |
180 |
plan tests => 3; |
181 |
|
182 |
subtest '/hold' => sub { |
183 |
plan tests => 17; |
184 |
|
185 |
$schema->storage->txn_begin; |
186 |
|
187 |
set_default_circulation_rules(); |
188 |
set_default_system_preferences(); |
189 |
|
190 |
my $item = build_a_test_item(); |
191 |
my ($patron, $session_id) = create_user_and_session(); |
192 |
$patron = Koha::Patrons->find($patron); |
193 |
my $route = '/api/v1/availability/item/hold'; |
194 |
my $tx = $t->ua->build_tx( GET => $route . '?itemnumber='.$item->itemnumber ); |
195 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
196 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
197 |
$t->request_ok($tx) |
198 |
->status_is(200) |
199 |
->json_has('/0/availability') |
200 |
->json_is('/0/availability/available' => Mojo::JSON->true); |
201 |
|
202 |
$patron->gonenoaddress('1')->store; |
203 |
$item->notforloan('1')->store; |
204 |
$tx = $t->ua->build_tx( GET => $route . '?itemnumber='.$item->itemnumber ); |
205 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
206 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
207 |
$t->request_ok($tx) |
208 |
->status_is(200) |
209 |
->json_has('/0/availability') |
210 |
->json_is('/0/availability/available' => Mojo::JSON->false) |
211 |
->json_is('/0/itemnumber' => $item->itemnumber) |
212 |
->json_is('/0/availability/unavailabilities/Patron::GoneNoAddress' => {}) |
213 |
->json_is('/0/availability/unavailabilities/Item::NotForLoan' => { |
214 |
code => "Not For Loan", |
215 |
status => 1, |
216 |
}); |
217 |
$patron->gonenoaddress('0')->store; |
218 |
$item->notforloan('0')->store; |
219 |
|
220 |
my $patron2 = build_a_test_patron(); |
221 |
$tx = $t->ua->build_tx( GET => $route . '?itemnumber='.$item->itemnumber.'&borrowernumber='.$patron2->borrowernumber ); |
222 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
223 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
224 |
$t->request_ok($tx) |
225 |
->status_is(403); |
226 |
|
227 |
my $branch = Koha::Libraries->find( |
228 |
$builder->build({ source => 'Branch' })->{'branchcode'}); |
229 |
t::lib::Mocks::mock_preference('UseBranchTransferLimits', 1); |
230 |
t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'itemtype'); |
231 |
C4::Circulation::CreateBranchTransferLimit( |
232 |
$branch->branchcode, |
233 |
$item->holdingbranch, |
234 |
$item->effective_itemtype |
235 |
); |
236 |
|
237 |
$tx = $t->ua->build_tx( GET => $route . '?itemnumber='.$item->itemnumber.'&branchcode='.$branch->branchcode ); |
238 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
239 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
240 |
$t->request_ok($tx) |
241 |
->status_is(200) |
242 |
->json_is('/0/availability/available' => Mojo::JSON->false) |
243 |
->json_is('/0/availability/unavailabilities/Item::CannotBeTransferred' => { |
244 |
from_library => $item->holdingbranch, |
245 |
to_library => $branch->branchcode,}); |
246 |
|
247 |
$schema->storage->txn_rollback; |
248 |
}; |
249 |
|
250 |
subtest '/checkout' => sub { |
251 |
plan tests => 12; |
252 |
|
253 |
$schema->storage->txn_begin; |
254 |
|
255 |
set_default_circulation_rules(); |
256 |
set_default_system_preferences(); |
257 |
|
258 |
my $item = build_a_test_item(); |
259 |
my ($patron, $session_id) = create_user_and_session(); |
260 |
$patron = Koha::Patrons->find($patron); |
261 |
my $route = '/api/v1/availability/item/checkout'; |
262 |
my $tx = $t->ua->build_tx( GET => $route . '?itemnumber='.$item->itemnumber ); |
263 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
264 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
265 |
$t->request_ok($tx) |
266 |
->status_is(200) |
267 |
->json_has('/0/availability') |
268 |
->json_is('/0/availability/available' => Mojo::JSON->true); |
269 |
|
270 |
$item->notforloan('1')->store; |
271 |
$tx = $t->ua->build_tx( GET => $route . '?itemnumber='.$item->itemnumber ); |
272 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
273 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
274 |
$t->request_ok($tx) |
275 |
->status_is(200) |
276 |
->json_has('/0/availability') |
277 |
->json_is('/0/availability/available' => Mojo::JSON->false) |
278 |
->json_is('/0/itemnumber' => $item->itemnumber) |
279 |
->json_is('/0/availability/unavailabilities/Item::NotForLoan' => { |
280 |
code => "Not For Loan", |
281 |
status => 1, |
282 |
}); |
283 |
$item->notforloan('0')->store; |
284 |
|
285 |
my $patron2 = build_a_test_patron(); |
286 |
$tx = $t->ua->build_tx( GET => $route . '?itemnumber='.$item->itemnumber.'&borrowernumber='.$patron2->borrowernumber ); |
287 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
288 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
289 |
$t->request_ok($tx) |
290 |
->status_is(403); |
291 |
|
292 |
$schema->storage->txn_rollback; |
293 |
}; |
294 |
|
295 |
subtest '/search' => sub { |
296 |
plan tests => 15; |
297 |
|
298 |
$schema->storage->txn_begin; |
299 |
|
300 |
set_default_circulation_rules(); |
301 |
set_default_system_preferences(); |
302 |
|
303 |
my $item = build_a_test_item(); |
304 |
my $item2 = build_a_test_item( |
305 |
Koha::Biblios->find($item->biblionumber), |
306 |
Koha::Biblioitems->find($item->biblioitemnumber) |
307 |
); |
308 |
my $route = '/api/v1/availability/biblio/search'; |
309 |
my $tx = $t->ua->build_tx( GET => $route . '?biblionumber='.$item->biblionumber ); |
310 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
311 |
$t->request_ok($tx) |
312 |
->status_is(200) |
313 |
->json_has('/0/availability') |
314 |
->json_is('/0/availability/available' => Mojo::JSON->true) |
315 |
->json_is('/0/item_availabilities/0/availability/available' => Mojo::JSON->true) |
316 |
->json_is('/0/item_availabilities/1/availability/available' => Mojo::JSON->true); |
317 |
|
318 |
$item2->notforloan('1')->store; |
319 |
$tx = $t->ua->build_tx( GET => $route . '?biblionumber='.$item->biblionumber ); |
320 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
321 |
$t->request_ok($tx) |
322 |
->status_is(200) |
323 |
->json_has('/0/availability') |
324 |
->json_is('/0/availability/available' => Mojo::JSON->true) |
325 |
->json_is('/0/item_availabilities/0/itemnumber' => $item->itemnumber) |
326 |
->json_is('/0/item_availabilities/0/availability/available' => Mojo::JSON->true) |
327 |
->json_is('/0/item_availabilities/1/itemnumber' => $item2->itemnumber) |
328 |
->json_is('/0/item_availabilities/1/availability/available' => Mojo::JSON->false) |
329 |
->json_is('/0/item_availabilities/1/availability/unavailabilities/Item::NotForLoan' => { |
330 |
code => "Not For Loan", |
331 |
status => 1, |
332 |
}); |
333 |
|
334 |
$schema->storage->txn_rollback; |
335 |
}; |
336 |
}; |
337 |
|
338 |
=head3 |
339 |
subtest '' => sub { |
340 |
|
341 |
plan tests => 18; |
342 |
|
343 |
$schema->storage->txn_begin; |
344 |
|
345 |
Koha::Cities->search->delete; |
346 |
my ( $borrowernumber, $session_id ) = |
347 |
create_user_and_session( { authorized => 0 } ); |
348 |
|
349 |
## Authorized user tests |
350 |
# No cities, so empty array should be returned |
351 |
my $tx = $t->ua->build_tx( GET => '/api/v1/cities' ); |
352 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
353 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
354 |
$t->request_ok($tx)->status_is(200)->json_is( [] ); |
355 |
|
356 |
my $city_country = 'France'; |
357 |
my $city = $builder->build( |
358 |
{ source => 'City', value => { city_country => $city_country } } ); |
359 |
|
360 |
# One city created, should get returned |
361 |
$tx = $t->ua->build_tx( GET => '/api/v1/cities' ); |
362 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
363 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
364 |
$t->request_ok($tx)->status_is(200)->json_is( [$city] ); |
365 |
|
366 |
my $another_city = $builder->build( |
367 |
{ source => 'City', value => { city_country => $city_country } } ); |
368 |
my $city_with_another_country = $builder->build( { source => 'City' } ); |
369 |
|
370 |
# Two cities created, they should both be returned |
371 |
$tx = $t->ua->build_tx( GET => '/api/v1/cities' ); |
372 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
373 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
374 |
$t->request_ok($tx)->status_is(200) |
375 |
->json_is( [ $city, $another_city, $city_with_another_country ] ); |
376 |
|
377 |
# Filtering works, two cities sharing city_country |
378 |
$tx = |
379 |
$t->ua->build_tx( GET => "/api/v1/cities?city_country=" . $city_country ); |
380 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
381 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
382 |
my $result = |
383 |
$t->request_ok($tx)->status_is(200)->json_is( [ $city, $another_city ] ); |
384 |
|
385 |
$tx = $t->ua->build_tx( |
386 |
GET => "/api/v1/cities?city_name=" . $city->{city_name} ); |
387 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
388 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
389 |
$t->request_ok($tx)->status_is(200)->json_is( [$city] ); |
390 |
|
391 |
# Warn on unsupported query parameter |
392 |
$tx = $t->ua->build_tx( GET => '/api/v1/cities?city_blah=blah' ); |
393 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
394 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
395 |
$t->request_ok($tx)->status_is(400) |
396 |
->json_is( [{ path => '/query/city_blah', message => 'Malformed query string'}] ); |
397 |
|
398 |
$schema->storage->txn_rollback; |
399 |
}; |
400 |
|
401 |
subtest 'get() tests' => sub { |
402 |
|
403 |
plan tests => 6; |
404 |
|
405 |
$schema->storage->txn_begin; |
406 |
|
407 |
my $city = $builder->build( { source => 'City' } ); |
408 |
my ( $borrowernumber, $session_id ) = |
409 |
create_user_and_session( { authorized => 0 } ); |
410 |
|
411 |
my $tx = $t->ua->build_tx( GET => "/api/v1/cities/" . $city->{cityid} ); |
412 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
413 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
414 |
$t->request_ok($tx)->status_is(200)->json_is($city); |
415 |
|
416 |
my $non_existent_id = $city->{cityid} + 1; |
417 |
$tx = $t->ua->build_tx( GET => "/api/v1/cities/" . $non_existent_id ); |
418 |
$tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); |
419 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
420 |
$t->request_ok($tx)->status_is(404) |
421 |
->json_is( '/error' => 'City not found' ); |
422 |
|
423 |
$schema->storage->txn_rollback; |
424 |
}; |
425 |
|
426 |
subtest 'add() tests' => sub { |
427 |
|
428 |
plan tests => 17; |
429 |
|
430 |
$schema->storage->txn_begin; |
431 |
|
432 |
my ( $unauthorized_borrowernumber, $unauthorized_session_id ) = |
433 |
create_user_and_session( { authorized => 0 } ); |
434 |
my ( $authorized_borrowernumber, $authorized_session_id ) = |
435 |
create_user_and_session( { authorized => 1 } ); |
436 |
my $city = { |
437 |
city_name => "City Name", |
438 |
city_state => "City State", |
439 |
city_zipcode => "City Zipcode", |
440 |
city_country => "City Country" |
441 |
}; |
442 |
|
443 |
# Unauthorized attempt to write |
444 |
my $tx = $t->ua->build_tx( POST => "/api/v1/cities/" => json => $city ); |
445 |
$tx->req->cookies( |
446 |
{ name => 'CGISESSID', value => $unauthorized_session_id } ); |
447 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
448 |
$t->request_ok($tx)->status_is(403); |
449 |
|
450 |
# Authorized attempt to write invalid data |
451 |
my $city_with_invalid_field = { |
452 |
city_blah => "City Blah", |
453 |
city_state => "City State", |
454 |
city_zipcode => "City Zipcode", |
455 |
city_country => "City Country" |
456 |
}; |
457 |
|
458 |
$tx = $t->ua->build_tx( |
459 |
POST => "/api/v1/cities/" => json => $city_with_invalid_field ); |
460 |
$tx->req->cookies( |
461 |
{ name => 'CGISESSID', value => $authorized_session_id } ); |
462 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
463 |
$t->request_ok($tx)->status_is(400)->json_is( |
464 |
"/errors" => [ |
465 |
{ |
466 |
message => "Properties not allowed: city_blah.", |
467 |
path => "/body" |
468 |
} |
469 |
] |
470 |
); |
471 |
|
472 |
# Authorized attempt to write |
473 |
$tx = $t->ua->build_tx( POST => "/api/v1/cities/" => json => $city ); |
474 |
$tx->req->cookies( |
475 |
{ name => 'CGISESSID', value => $authorized_session_id } ); |
476 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
477 |
my $cityid = $t->request_ok($tx)->status_is(200) |
478 |
->json_is( '/city_name' => $city->{city_name} ) |
479 |
->json_is( '/city_state' => $city->{city_state} ) |
480 |
->json_is( '/city_zipcode' => $city->{city_zipcode} ) |
481 |
->json_is( '/city_country' => $city->{city_country} ) |
482 |
->tx->res->json->{cityid}; |
483 |
|
484 |
# Authorized attempt to create with null id |
485 |
$city->{cityid} = undef; |
486 |
$tx = $t->ua->build_tx( |
487 |
POST => "/api/v1/cities/" => json => $city ); |
488 |
$tx->req->cookies( |
489 |
{ name => 'CGISESSID', value => $authorized_session_id } ); |
490 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
491 |
$t->request_ok($tx)->status_is(400)->json_has('/errors'); |
492 |
|
493 |
# Authorized attempt to create with existing id |
494 |
$city->{cityid} = $cityid; |
495 |
$tx = $t->ua->build_tx( |
496 |
POST => "/api/v1/cities/" => json => $city ); |
497 |
$tx->req->cookies( |
498 |
{ name => 'CGISESSID', value => $authorized_session_id } ); |
499 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
500 |
$t->request_ok($tx)->status_is(400)->json_is( |
501 |
"/errors" => [ |
502 |
{ |
503 |
message => "Read-only.", |
504 |
path => "/body/cityid" |
505 |
} |
506 |
] |
507 |
); |
508 |
|
509 |
$schema->storage->txn_rollback; |
510 |
}; |
511 |
|
512 |
subtest 'update() tests' => sub { |
513 |
|
514 |
plan tests => 15; |
515 |
|
516 |
$schema->storage->txn_begin; |
517 |
|
518 |
my ( $unauthorized_borrowernumber, $unauthorized_session_id ) = |
519 |
create_user_and_session( { authorized => 0 } ); |
520 |
my ( $authorized_borrowernumber, $authorized_session_id ) = |
521 |
create_user_and_session( { authorized => 1 } ); |
522 |
|
523 |
my $city_id = $builder->build( { source => 'City' } )->{cityid}; |
524 |
|
525 |
# Unauthorized attempt to update |
526 |
my $tx = $t->ua->build_tx( PUT => "/api/v1/cities/$city_id" => json => |
527 |
{ city_name => 'New unauthorized name change' } ); |
528 |
$tx->req->cookies( |
529 |
{ name => 'CGISESSID', value => $unauthorized_session_id } ); |
530 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
531 |
$t->request_ok($tx)->status_is(403); |
532 |
|
533 |
# Attempt partial update on a PUT |
534 |
my $city_with_missing_field = { |
535 |
city_name => 'New name', |
536 |
city_state => 'New state', |
537 |
city_country => 'New country' |
538 |
}; |
539 |
|
540 |
$tx = $t->ua->build_tx( |
541 |
PUT => "/api/v1/cities/$city_id" => json => $city_with_missing_field ); |
542 |
$tx->req->cookies( |
543 |
{ name => 'CGISESSID', value => $authorized_session_id } ); |
544 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
545 |
$t->request_ok($tx)->status_is(400) |
546 |
->json_is( "/errors" => |
547 |
[ { message => "Missing property.", path => "/body/city_zipcode" } ] |
548 |
); |
549 |
|
550 |
# Full object update on PUT |
551 |
my $city_with_updated_field = { |
552 |
city_name => "London", |
553 |
city_state => "City State", |
554 |
city_zipcode => "City Zipcode", |
555 |
city_country => "City Country" |
556 |
}; |
557 |
|
558 |
$tx = $t->ua->build_tx( |
559 |
PUT => "/api/v1/cities/$city_id" => json => $city_with_updated_field ); |
560 |
$tx->req->cookies( |
561 |
{ name => 'CGISESSID', value => $authorized_session_id } ); |
562 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
563 |
$t->request_ok($tx)->status_is(200)->json_is( '/city_name' => 'London' ); |
564 |
|
565 |
# Authorized attempt to write invalid data |
566 |
my $city_with_invalid_field = { |
567 |
city_blah => "City Blah", |
568 |
city_state => "City State", |
569 |
city_zipcode => "City Zipcode", |
570 |
city_country => "City Country" |
571 |
}; |
572 |
|
573 |
$tx = $t->ua->build_tx( |
574 |
PUT => "/api/v1/cities/$city_id" => json => $city_with_invalid_field ); |
575 |
$tx->req->cookies( |
576 |
{ name => 'CGISESSID', value => $authorized_session_id } ); |
577 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
578 |
$t->request_ok($tx)->status_is(400)->json_is( |
579 |
"/errors" => [ |
580 |
{ |
581 |
message => "Properties not allowed: city_blah.", |
582 |
path => "/body" |
583 |
} |
584 |
] |
585 |
); |
586 |
|
587 |
my $non_existent_id = $city_id + 1; |
588 |
$tx = |
589 |
$t->ua->build_tx( PUT => "/api/v1/cities/$non_existent_id" => json => |
590 |
$city_with_updated_field ); |
591 |
$tx->req->cookies( |
592 |
{ name => 'CGISESSID', value => $authorized_session_id } ); |
593 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
594 |
$t->request_ok($tx)->status_is(404); |
595 |
|
596 |
$schema->storage->txn_rollback; |
597 |
|
598 |
# Wrong mathod (POST) |
599 |
$city_with_updated_field->{cityid} = 2; |
600 |
|
601 |
$tx = $t->ua->build_tx( |
602 |
POST => "/api/v1/cities/$city_id" => json => $city_with_updated_field ); |
603 |
$tx->req->cookies( |
604 |
{ name => 'CGISESSID', value => $authorized_session_id } ); |
605 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
606 |
$t->request_ok($tx)->status_is(404); |
607 |
}; |
608 |
|
609 |
subtest 'delete() tests' => sub { |
610 |
|
611 |
plan tests => 7; |
612 |
|
613 |
$schema->storage->txn_begin; |
614 |
|
615 |
my ( $unauthorized_borrowernumber, $unauthorized_session_id ) = |
616 |
create_user_and_session( { authorized => 0 } ); |
617 |
my ( $authorized_borrowernumber, $authorized_session_id ) = |
618 |
create_user_and_session( { authorized => 1 } ); |
619 |
|
620 |
my $city_id = $builder->build( { source => 'City' } )->{cityid}; |
621 |
|
622 |
# Unauthorized attempt to update |
623 |
my $tx = $t->ua->build_tx( DELETE => "/api/v1/cities/$city_id" ); |
624 |
$tx->req->cookies( |
625 |
{ name => 'CGISESSID', value => $unauthorized_session_id } ); |
626 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
627 |
$t->request_ok($tx)->status_is(403); |
628 |
|
629 |
$tx = $t->ua->build_tx( DELETE => "/api/v1/cities/$city_id" ); |
630 |
$tx->req->cookies( |
631 |
{ name => 'CGISESSID', value => $authorized_session_id } ); |
632 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
633 |
$t->request_ok($tx)->status_is(200)->content_is(''); |
634 |
|
635 |
$tx = $t->ua->build_tx( DELETE => "/api/v1/cities/$city_id" ); |
636 |
$tx->req->cookies( |
637 |
{ name => 'CGISESSID', value => $authorized_session_id } ); |
638 |
$tx->req->env( { REMOTE_ADDR => $remote_address } ); |
639 |
$t->request_ok($tx)->status_is(404); |
640 |
|
641 |
$schema->storage->txn_rollback; |
642 |
}; |
643 |
=cut |
644 |
sub create_user_and_session { |
645 |
|
646 |
my $args = shift; |
647 |
my $flags = ( $args->{authorized} ) ? $args->{authorized} : 0; |
648 |
my $dbh = C4::Context->dbh; |
649 |
|
650 |
my $user = $builder->build( |
651 |
{ |
652 |
source => 'Borrower', |
653 |
value => { |
654 |
flags => $flags, |
655 |
debarred => undef, |
656 |
debarredcomment => undef, |
657 |
lost => undef, |
658 |
gonenoaddress => undef, |
659 |
dateexpiry => output_pref({ dt => dt_from_string()->add_duration( # expires in 100 days |
660 |
DateTime::Duration->new(days => 100)), dateformat => 'iso', dateonly => 1 }), |
661 |
dateofbirth => '1950-10-10', |
662 |
} |
663 |
} |
664 |
); |
665 |
|
666 |
# Create a session for the authorized user |
667 |
my $session = C4::Auth::get_session(''); |
668 |
$session->param( 'number', $user->{borrowernumber} ); |
669 |
$session->param( 'id', $user->{userid} ); |
670 |
$session->param( 'ip', '127.0.0.1' ); |
671 |
$session->param( 'lasttime', time() ); |
672 |
$session->flush; |
673 |
|
674 |
if ( $args->{authorized} ) { |
675 |
$dbh->do( " |
676 |
INSERT INTO user_permissions (borrowernumber,module_bit,code) |
677 |
VALUES (?,3,'parameters_remaining_permissions')", undef, |
678 |
$user->{borrowernumber} ); |
679 |
} |
680 |
|
681 |
return ( $user->{borrowernumber}, $session->id ); |
682 |
} |
683 |
|
684 |
|
685 |
1; |