3 # This file is part of Koha.
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
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.
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.
20 use Test::More tests => 5;
24 use t::lib::TestBuilder;
31 my $schema = Koha::Database->new->schema;
32 my $builder = t::lib::TestBuilder->new;
34 # FIXME: sessionStorage defaults to mysql, but it seems to break transaction handling
35 # this affects the other REST api tests
36 t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' );
38 my $remote_address = '127.0.0.1';
39 my $t = Test::Mojo->new('Koha::REST::V1');
41 subtest 'list() tests' => sub {
44 $schema->storage->txn_begin;
46 unauthorized_access_tests('GET', undef, undef);
48 subtest 'librarian access tests' => sub {
51 my ($borrowernumber, $sessionid) = create_user_and_session({
53 my $patron = Koha::Patrons->find($borrowernumber);
54 Koha::Patrons->search({
55 borrowernumber => { '!=' => $borrowernumber},
56 cardnumber => { LIKE => $patron->cardnumber . "%" }
58 Koha::Patrons->search({
59 borrowernumber => { '!=' => $borrowernumber},
60 address2 => { LIKE => $patron->address2 . "%" }
63 my $tx = $t->ua->build_tx(GET => '/api/v1/patrons');
64 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
65 $tx->req->env({REMOTE_ADDR => '127.0.0.1'});
69 $tx = $t->ua->build_tx(GET => '/api/v1/patrons?cardnumber='.
71 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
72 $tx->req->env({REMOTE_ADDR => '127.0.0.1'});
75 ->json_is('/0/cardnumber' => $patron->cardnumber);
77 $tx = $t->ua->build_tx(GET => '/api/v1/patrons?address2='.
79 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
80 $tx->req->env({REMOTE_ADDR => '127.0.0.1'});
83 ->json_is('/0/address2' => $patron->address2);
86 $schema->storage->txn_rollback;
89 subtest 'get() tests' => sub {
92 $schema->storage->txn_begin;
94 unauthorized_access_tests('GET', -1, undef);
96 subtest 'access own object tests' => sub {
99 my ($patronid, $patronsessionid) = create_user_and_session({
102 # Access patron's own data even though they have no borrowers flag
103 my $tx = $t->ua->build_tx(GET => "/api/v1/patrons/$patronid");
104 $tx->req->cookies({name => 'CGISESSID', value => $patronsessionid});
105 $tx->req->env({REMOTE_ADDR => '127.0.0.1'});
109 my $guarantee = $builder->build({
110 source => 'Borrower',
112 guarantorid => $patronid,
116 # Access guarantee's data even though guarantor has no borrowers flag
117 my $guaranteenumber = $guarantee->{borrowernumber};
118 $tx = $t->ua->build_tx(GET => "/api/v1/patrons/$guaranteenumber");
119 $tx->req->cookies({name => 'CGISESSID', value => $patronsessionid});
120 $tx->req->env({REMOTE_ADDR => '127.0.0.1'});
125 subtest 'librarian access tests' => sub {
128 my ($patron_id) = create_user_and_session({
130 my $patron = Koha::Patrons->find($patron_id);
131 my ($borrowernumber, $sessionid) = create_user_and_session({
133 my $tx = $t->ua->build_tx(GET => "/api/v1/patrons/$patron_id");
134 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
137 ->json_is('/borrowernumber' => $patron_id)
138 ->json_is('/surname' => $patron->surname)
139 ->json_is('/lost' => Mojo::JSON->false );
142 $schema->storage->txn_rollback;
145 subtest 'add() tests' => sub {
148 $schema->storage->txn_begin;
150 my $categorycode = $builder->build({ source => 'Category' })->{categorycode};
151 my $branchcode = $builder->build({ source => 'Branch' })->{branchcode};
154 branchcode => $branchcode,
155 cardnumber => $branchcode.$categorycode,
156 categorycode => $categorycode,
158 surname => "TestUser",
159 userid => $branchcode.$categorycode,
162 unauthorized_access_tests('POST', undef, $newpatron);
164 subtest 'librarian access tests' => sub {
167 my ($borrowernumber, $sessionid) = create_user_and_session({
170 $newpatron->{branchcode} = "nonexistent"; # Test invalid branchcode
171 my $tx = $t->ua->build_tx(POST => "/api/v1/patrons" =>json => $newpatron);
172 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
175 ->json_is('/error' => "Given branchcode does not exist");
176 $newpatron->{branchcode} = $branchcode;
178 $newpatron->{categorycode} = "nonexistent"; # Test invalid patron category
179 $tx = $t->ua->build_tx(POST => "/api/v1/patrons" => json => $newpatron);
180 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
183 ->json_is('/error' => "Given categorycode does not exist");
184 $newpatron->{categorycode} = $categorycode;
186 $newpatron->{falseproperty} = "Non existent property";
187 $tx = $t->ua->build_tx(POST => "/api/v1/patrons" => json => $newpatron);
188 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
191 delete $newpatron->{falseproperty};
193 $tx = $t->ua->build_tx(POST => "/api/v1/patrons" => json => $newpatron);
194 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
196 ->status_is(201, 'Patron created successfully')
197 ->json_has('/borrowernumber', 'got a borrowernumber')
198 ->json_is('/cardnumber', $newpatron->{ cardnumber })
199 ->json_is('/surname' => $newpatron->{ surname })
200 ->json_is('/firstname' => $newpatron->{ firstname });
201 $newpatron->{borrowernumber} = $tx->res->json->{borrowernumber};
203 $tx = $t->ua->build_tx(POST => "/api/v1/patrons" => json => $newpatron);
204 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
207 ->json_has('/error', 'Fails when trying to POST duplicate'.
208 ' cardnumber or userid')
209 ->json_has('/conflict', {
210 userid => $newpatron->{ userid },
211 cardnumber => $newpatron->{ cardnumber }
216 $schema->storage->txn_rollback;
219 subtest 'edit() tests' => sub {
222 $schema->storage->txn_begin;
224 unauthorized_access_tests('PUT', 123, {email => 'nobody@example.com'});
226 subtest 'patron modifying own data' => sub {
229 my ($borrowernumber, $sessionid) = create_user_and_session({
231 my $patron = Koha::Patrons->find($borrowernumber)->TO_JSON;
233 t::lib::Mocks::mock_preference("OPACPatronDetails", 0);
234 my $tx = $t->ua->build_tx(PUT => "/api/v1/patrons/" .
235 $patron->{borrowernumber} => json => $patron);
236 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
238 ->status_is(403, 'OPACPatronDetails off - modifications not allowed.');
240 t::lib::Mocks::mock_preference("OPACPatronDetails", 1);
241 $tx = $t->ua->build_tx(PUT => "/api/v1/patrons/" .
242 $patron->{borrowernumber} => json => $patron);
243 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
245 ->status_is(204, 'Updating myself with my current data');
247 $patron->{'firstname'} = "noob";
248 $tx = $t->ua->build_tx(PUT => "/api/v1/patrons/" .
249 $patron->{borrowernumber} => json => $patron);
250 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
252 ->status_is(202, 'Updating myself with my current data');
255 Koha::Patron::Modifications->find({
256 borrowernumber => $patron->{borrowernumber},
259 is(Koha::Patrons->find({
260 borrowernumber => $patron->{borrowernumber}})->firstname,
261 "noob", "Changes approved");
264 subtest 'librarian access tests' => sub {
267 t::lib::Mocks::mock_preference('minPasswordLength', 1);
268 my ($borrowernumber, $sessionid) = create_user_and_session({
270 my ($borrowernumber2, undef) = create_user_and_session({
272 my $patron = Koha::Patrons->find($borrowernumber2);
273 my $newpatron = Koha::Patrons->find($borrowernumber2)->TO_JSON;
275 my $tx = $t->ua->build_tx(PUT => "/api/v1/patrons/-1" =>
277 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
280 ->json_has('/error', 'Fails when trying to PUT nonexistent patron');
282 $newpatron->{categorycode} = 'nonexistent';
283 $tx = $t->ua->build_tx(PUT => "/api/v1/patrons/" .
284 $newpatron->{borrowernumber} => json => $newpatron
286 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
289 ->json_is('/error' => "Given categorycode does not exist");
290 $newpatron->{categorycode} = $patron->categorycode;
292 $newpatron->{branchcode} = 'nonexistent';
293 $tx = $t->ua->build_tx(PUT => "/api/v1/patrons/" .
294 $newpatron->{borrowernumber} => json => $newpatron
296 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
299 ->json_is('/error' => "Given branchcode does not exist");
300 $newpatron->{branchcode} = $patron->branchcode;
302 $newpatron->{falseproperty} = "Non existent property";
303 $tx = $t->ua->build_tx(PUT => "/api/v1/patrons/" .
304 $newpatron->{borrowernumber} => json => $newpatron);
305 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
308 ->json_is('/errors/0/message' =>
309 'Properties not allowed: falseproperty.');
310 delete $newpatron->{falseproperty};
312 $tx = $t->ua->build_tx(PUT => "/api/v1/patrons/" .
313 $borrowernumber => json => $newpatron);
314 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
317 ->json_has('/error' => "Fails when trying to update to an existing"
318 ."cardnumber or userid")
319 ->json_has('/conflict', {
320 cardnumber => $newpatron->{ cardnumber },
321 userid => $newpatron->{ userid }
325 $newpatron->{ cardnumber } = $borrowernumber.$borrowernumber2;
326 $newpatron->{ userid } = "user".$borrowernumber.$borrowernumber2;
327 $newpatron->{ surname } = "user".$borrowernumber.$borrowernumber2;
329 $tx = $t->ua->build_tx(PUT => "/api/v1/patrons/" .
330 $newpatron->{borrowernumber} => json => $newpatron);
331 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
333 ->status_is(200, 'Patron updated successfully')
334 ->json_has($newpatron);
335 is(Koha::Patrons->find($newpatron->{borrowernumber})->cardnumber,
336 $newpatron->{ cardnumber }, 'Patron is really updated!');
339 $schema->storage->txn_rollback;
342 subtest 'delete() tests' => sub {
345 $schema->storage->txn_begin;
347 unauthorized_access_tests('DELETE', 123, undef);
349 subtest 'librarian access test' => sub {
352 my ($borrowernumber, $sessionid) = create_user_and_session({
354 my ($borrowernumber2, $sessionid2) = create_user_and_session({
357 my $tx = $t->ua->build_tx(DELETE => "/api/v1/patrons/-1");
358 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
360 ->status_is(404, 'Patron not found');
362 $tx = $t->ua->build_tx(DELETE => "/api/v1/patrons/$borrowernumber2");
363 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
365 ->status_is(200, 'Patron deleted successfully');
368 $schema->storage->txn_rollback;
371 # Centralized tests for 401s and 403s assuming the endpoint requires
372 # borrowers flag for access
373 sub unauthorized_access_tests {
374 my ($verb, $patronid, $json) = @_;
376 my $endpoint = '/api/v1/patrons';
377 $endpoint .= ($patronid) ? "/$patronid" : '';
379 subtest 'unauthorized access tests' => sub {
382 my $tx = $t->ua->build_tx($verb => $endpoint => json => $json);
386 my ($borrowernumber, $sessionid) = create_user_and_session({
389 $tx = $t->ua->build_tx($verb => $endpoint => json => $json);
390 $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
393 ->json_is('/required_permissions', {"borrowers" => "1"});
397 sub create_user_and_session {
400 my $flags = ( $args->{authorized} ) ? 16 : 0;
401 my $dbh = C4::Context->dbh;
403 my $user = $builder->build(
405 source => 'Borrower',
410 email => 'nobody@example.com',
411 emailpro => 'nobody@example.com',
412 B_email => 'nobody@example.com',
417 # Create a session for the authorized user
418 my $session = C4::Auth::get_session('');
419 $session->param( 'number', $user->{borrowernumber} );
420 $session->param( 'id', $user->{userid} );
421 $session->param( 'ip', '127.0.0.1' );
422 $session->param( 'lasttime', time() );
425 return ( $user->{borrowernumber}, $session->id );