Bug 29510: Regression tests
[koha.git] / t / db_dependent / Koha / REST / Plugin / Objects.t
1 #!/usr/bin/perl
2
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18 use Modern::Perl;
19
20 use Koha::Acquisition::Orders;
21 use Koha::AuthorisedValueCategories;
22 use Koha::AuthorisedValues;
23 use Koha::Cities;
24 use Koha::Biblios;
25 use Koha::Patrons;
26
27 use Mojo::JSON qw(encode_json);
28
29 # Dummy app for testing the plugin
30 use Mojolicious::Lite;
31
32 app->log->level('error');
33
34 plugin 'Koha::REST::Plugin::Objects';
35 plugin 'Koha::REST::Plugin::Query';
36 plugin 'Koha::REST::Plugin::Pagination';
37
38 get '/cities' => sub {
39     my $c = shift;
40     $c->validation->output($c->req->params->to_hash);
41     $c->stash_embed( { spec => { parameters => [ { name => 'x-koha-embed', items => { enum => ['+strings'] } } ] } } );
42     my $cities = $c->objects->search(Koha::Cities->new);
43     $c->render( status => 200, json => $cities );
44 };
45
46 get '/cities/rs' => sub {
47     my $c = shift;
48     $c->validation->output( $c->req->params->to_hash );
49     $c->stash_embed;
50     my $cities = $c->objects->search_rs( Koha::Cities->new );
51
52     $c->render( status => 200, json => { count => $cities->count } );
53 };
54
55 get '/cities/:city_id' => sub {
56     my $c = shift;
57     my $id = $c->stash("city_id");
58     $c->stash_embed( { spec => { parameters => [ { name => 'x-koha-embed', items => { enum => ['+strings'] } } ] } } );
59     my $city = $c->objects->find(Koha::Cities->new, $id);
60     $c->render( status => 200, json => $city );
61 };
62
63 get '/orders' => sub {
64     my $c = shift;
65     $c->stash('koha.embed', ( { fund => {} } ) );
66     $c->validation->output($c->req->params->to_hash);
67     my $orders = $c->objects->search(Koha::Acquisition::Orders->new);
68     $c->render( status => 200, json => $orders );
69 };
70
71 get '/orders/:order_id' => sub {
72     my $c = shift;
73     $c->stash('koha.embed', ( { fund => {} } ) );
74     my $id = $c->stash("order_id");
75     my $order = $c->objects->find(Koha::Acquisition::Orders->new, $id);
76     $c->render( status => 200, json => $order );
77 };
78
79 get '/biblios' => sub {
80     my $c = shift;
81     my $output = $c->req->params->to_hash;
82     $output->{query} = $c->req->json if defined $c->req->json;
83     my $headers = $c->req->headers->to_hash;
84     $output->{'x-koha-query'} = $headers->{'x-koha-query'} if defined $headers->{'x-koha-query'};
85     $c->validation->output($output);
86     my $biblios_set = Koha::Biblios->new;
87     $c->stash("koha.embed", {
88         "suggestions" => {
89             children => {
90                 "suggester" => {}
91             }
92         }
93     });
94     my $biblios = $c->objects->search($biblios_set);
95     $c->render( status => 200, json => {count => scalar(@$biblios), biblios => $biblios} );
96 };
97
98 get '/libraries/:library_id_1/:library_id_2' => sub {
99
100     my $c = shift;
101
102     # Emulate a public route by stashing the is_public value
103     $c->stash( 'is_public' => 1 );
104
105     my $library_id_1 = $c->param('library_id_1');
106     my $library_id_2 = $c->param('library_id_2');
107
108     my $libraries_rs = Koha::Libraries->search(
109         { branchcode => [ $library_id_1, $library_id_2 ] },
110         { order_by   => 'branchname' }
111     );
112     my $libraries    = $c->objects->search( $libraries_rs );
113
114     $c->render(
115         status => 200,
116         json   => $libraries
117     );
118 };
119
120 get '/my_patrons' => sub {
121
122     my $c = shift;
123
124     my $patrons = $c->objects->search( scalar Koha::Patrons->search( {}, { order_by   => 'borrowernumber' }) );
125
126     $c->render(
127         status => 200,
128         json   => $patrons
129     );
130 };
131
132 get '/cities/:city_id/rs' => sub {
133     my $c = shift;
134     $c->validation->output( $c->req->params->to_hash );
135     $c->stash_embed;
136     my $city_id = $c->param('city_id');
137     my $city    = $c->objects->find_rs( Koha::Cities->new, $city_id );
138
139     $c->render( status => 200, json => { name => $city->city_name } );
140 };
141
142 get '/my_patrons/:patron_id' => sub {
143
144     my $c = shift;
145
146     my $patron_id = $c->param('patron_id');
147     my $patron    = $c->objects->find( scalar Koha::Patrons->new, $patron_id );
148
149     $c->render(
150         status => 200,
151         json   => $patron
152     );
153 };
154
155 # The tests
156 use Test::More tests => 18;
157 use Test::Mojo;
158
159 use t::lib::Mocks;
160 use t::lib::TestBuilder;
161 use Koha::Database;
162
163 my $schema  = Koha::Database->new()->schema();
164 my $builder = t::lib::TestBuilder->new;
165
166 subtest 'objects.search helper' => sub {
167
168     plan tests => 50;
169
170     $schema->storage->txn_begin;
171
172     # Remove existing cities to have more control on the search results
173     Koha::Cities->delete;
174
175     # Create three sample cities that match the query. This makes sure we
176     # always have a "next" link regardless of Mojolicious::Plugin::OpenAPI version.
177     $builder->build_object({
178         class => 'Koha::Cities',
179         value => {
180             city_name => 'Manuel'
181         }
182     });
183     $builder->build_object({
184         class => 'Koha::Cities',
185         value => {
186             city_name => 'Manuela'
187         }
188     });
189     $builder->build_object({
190         class => 'Koha::Cities',
191         value => {
192             city_name => 'Manuelab'
193         }
194     });
195
196     my $t = Test::Mojo->new;
197     $t->get_ok('/cities?name=manuel&_per_page=1&_page=1')
198         ->status_is(200)
199         ->header_like( 'Link' => qr/<http:\/\/.*[\?&]_page=2.*>; rel="next",/ )
200         ->json_has('/0')
201         ->json_hasnt('/1')
202         ->json_is('/0/name' => 'Manuel');
203
204     $builder->build_object({
205         class => 'Koha::Cities',
206         value => {
207             city_name => 'Emanuel'
208         }
209     });
210
211     # _match=starts_with
212     $t->get_ok('/cities?name=manuel&_per_page=4&_page=1&_match=starts_with')
213         ->status_is(200)
214         ->json_has('/0')
215         ->json_has('/1')
216         ->json_has('/2')
217         ->json_hasnt('/3')
218         ->json_is('/0/name' => 'Manuel')
219         ->json_is('/1/name' => 'Manuela')
220         ->json_is('/2/name' => 'Manuelab');
221
222     # _match=ends_with
223     $t->get_ok('/cities?name=manuel&_per_page=4&_page=1&_match=ends_with')
224         ->status_is(200)
225         ->json_has('/0')
226         ->json_has('/1')
227         ->json_hasnt('/2')
228         ->json_is('/0/name' => 'Manuel')
229         ->json_is('/1/name' => 'Emanuel');
230
231     # _match=exact
232     $t->get_ok('/cities?name=manuel&_per_page=4&_page=1&_match=exact')
233         ->status_is(200)
234         ->json_has('/0')
235         ->json_hasnt('/1')
236         ->json_is('/0/name' => 'Manuel');
237
238     # _match=contains
239     $t->get_ok('/cities?name=manuel&_per_page=4&_page=1&_match=contains')
240         ->status_is(200)
241         ->json_has('/0')
242         ->json_has('/1')
243         ->json_has('/2')
244         ->json_has('/3')
245         ->json_hasnt('/4')
246         ->json_is('/0/name' => 'Manuel')
247         ->json_is('/1/name' => 'Manuela')
248         ->json_is('/2/name' => 'Manuelab')
249         ->json_is('/3/name' => 'Emanuel');
250
251     # Add 20 more cities
252     for ( 1..20 ) {
253         $builder->build_object({ class => 'Koha::Cities' });
254     }
255
256     t::lib::Mocks::mock_preference('RESTdefaultPageSize', 20 );
257     $t->get_ok('/cities')
258       ->status_is(200);
259
260     my $response_count = scalar @{ $t->tx->res->json };
261     is( $response_count, 20, 'RESTdefaultPageSize is honoured by default (20)' );
262
263     t::lib::Mocks::mock_preference('RESTdefaultPageSize', 5 );
264     $t->get_ok('/cities')
265       ->status_is(200);
266
267     $response_count = scalar @{ $t->tx->res->json };
268     is( $response_count, 5, 'RESTdefaultPageSize is honoured by default (5)' );
269
270     $t->get_ok('/cities?_page=1&_per_page=-1')
271       ->status_is(200);
272
273     $response_count = scalar @{ $t->tx->res->json };
274     is( $response_count, 24, '_per_page=-1 means all resources' );
275
276     $t->get_ok('/cities?_page=100&_per_page=-1')
277       ->status_is(200);
278
279     $response_count = scalar @{ $t->tx->res->json };
280     is( $response_count, 24, 'When _per_page=-1 the page param is not considered' );
281
282     $schema->storage->txn_rollback;
283 };
284
285 subtest 'objects.search helper, sorting on mapped column' => sub {
286
287     plan tests => 42;
288
289     $schema->storage->txn_begin;
290
291     # Have complete control over the existing cities to ease testing
292     Koha::Cities->delete;
293
294     $builder->build_object({ class => 'Koha::Cities', value => { city_name => 'A', city_country => 'Argentina' } });
295     $builder->build_object({ class => 'Koha::Cities', value => { city_name => 'B', city_country => 'Argentina' } });
296     $builder->build_object({ class => 'Koha::Cities', value => { city_name => 'C', city_country => 'Argentina' } });
297     $builder->build_object({ class => 'Koha::Cities', value => { city_name => 'C', city_country => 'Belarus' } });
298
299     my $t = Test::Mojo->new;
300     # CSV-param
301     $t->get_ok('/cities?_order_by=%2Bname,-country')
302       ->status_is(200)
303       ->json_has('/0')
304       ->json_has('/1')
305       ->json_is('/0/name' => 'A')
306       ->json_is('/1/name' => 'B')
307       ->json_is('/2/name' => 'C')
308       ->json_is('/2/country' => 'Belarus')
309       ->json_is('/3/name' => 'C')
310       ->json_is('/3/country' => 'Argentina')
311       ->json_hasnt('/4');
312
313     # Multi-param: traditional
314     $t->get_ok('/cities?_order_by=%2Bname&_order_by=-country')
315       ->status_is(200)
316       ->json_has('/0')
317       ->json_has('/1')
318       ->json_is('/0/name' => 'A')
319       ->json_is('/1/name' => 'B')
320       ->json_is('/2/name' => 'C')
321       ->json_is('/2/country' => 'Belarus')
322       ->json_is('/3/name' => 'C')
323       ->json_is('/3/country' => 'Argentina')
324       ->json_hasnt('/4');
325
326     # Multi-param: PHP Style, Passes validation as above, subsequntly explodes
327     $t->get_ok('/cities?_order_by[]=%2Bname&_order_by[]=-country')
328       ->status_is(200)
329       ->json_has('/0')
330       ->json_has('/1')
331       ->json_is('/0/name' => 'A')
332       ->json_is('/1/name' => 'B')
333       ->json_is('/2/name' => 'C')
334       ->json_is('/2/country' => 'Belarus')
335       ->json_is('/3/name' => 'C')
336       ->json_is('/3/country' => 'Argentina')
337       ->json_hasnt('/4');
338
339     # Single-param
340     $t->get_ok('/cities?_order_by=-name')
341       ->status_is(200)
342       ->json_has('/0')
343       ->json_has('/1')
344       ->json_is('/0/name' => 'C')
345       ->json_is('/1/name' => 'C')
346       ->json_is('/2/name' => 'B')
347       ->json_is('/3/name' => 'A')
348       ->json_hasnt('/4');
349
350     $schema->storage->txn_rollback;
351 };
352
353 subtest 'objects.search helper, encoding' => sub {
354
355     plan tests => 5;
356
357     $schema->storage->txn_begin;
358
359     Koha::Cities->delete;
360
361     $builder->build_object({ class => 'Koha::Cities', value => { city_name => 'A', city_country => 'Argentina' } });
362     $builder->build_object({ class => 'Koha::Cities', value => { city_name => 'B', city_country => '❤Argentina❤' } });
363
364     my $t = Test::Mojo->new;
365     $t->get_ok('/cities?q={"country": "❤Argentina❤"}')
366       ->status_is(200)
367       ->json_has('/0')
368       ->json_hasnt('/1')
369       ->json_is('/0/name' => 'B');
370
371     $schema->storage->txn_rollback;
372 };
373
374 subtest 'objects.search helper, X-Total-Count and X-Base-Total-Count' => sub {
375
376     plan tests => 8;
377
378     $schema->storage->txn_begin;
379
380     Koha::Cities->delete;
381
382     my $long_city_name = 'Llanfairpwllgwyngyll';
383     for my $i ( 1 .. length($long_city_name) ) {
384         $builder->build_object(
385             {
386                 class => 'Koha::Cities',
387                 value => {
388                     city_name    => substr( $long_city_name, 0, $i ),
389                     city_country => 'Wales'
390                 }
391             }
392         );
393     }
394
395     my $t = Test::Mojo->new;
396     $t->get_ok('/cities?name=L&_per_page=10&_page=1&_match=starts_with')
397       ->status_is(200)
398       ->header_is( 'X-Total-Count' => 20, 'X-Total-Count header present' )
399       ->header_is( 'X-Base-Total-Count' => 20, 'X-Base-Total-Count header present' );
400
401     $t->get_ok('/cities?name=Llan&_per_page=10&_page=1&_match=starts_with')
402       ->status_is(200)
403       ->header_is( 'X-Total-Count' => 17, 'X-Total-Count header present' )
404       ->header_is('X-Base-Total-Count' => 20, 'X-Base-Total-Count header present' );
405
406     $schema->storage->txn_rollback;
407 };
408
409 subtest 'objects.search helper, embed' => sub {
410
411     plan tests => 2;
412
413     $schema->storage->txn_begin;
414
415     my $order = $builder->build_object({ class => 'Koha::Acquisition::Orders' });
416
417     my $t = Test::Mojo->new;
418     $t->get_ok('/orders?order_id=' . $order->ordernumber)
419       ->json_is('/0',$order->to_api({ embed => ( { fund => {} } ) }));
420
421     $schema->storage->txn_rollback;
422 };
423
424 subtest 'objects.search helper with query parameter' => sub {
425     plan tests => 4;
426
427     $schema->storage->txn_begin;
428
429     my $patron1 = $builder->build_object( { class => "Koha::Patrons" } );
430     my $patron2 = $builder->build_object( { class => "Koha::Patrons" } );
431     my $biblio1 = $builder->build_sample_biblio;
432     my $biblio2 = $builder->build_sample_biblio;
433     my $biblio3 = $builder->build_sample_biblio;
434     my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron1->borrowernumber, biblionumber => $biblio1->biblionumber} } );
435     my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio2->biblionumber} } );
436     my $suggestion3 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio3->biblionumber} } );
437
438     my $t = Test::Mojo->new;
439     $t->get_ok('/biblios' => json => {"suggestions.suggester.patron_id" => $patron1->borrowernumber })
440       ->json_is('/count' => 1, 'there should be 1 biblio with suggestions of patron 1');
441
442     $t->get_ok('/biblios' => json => {"suggestions.suggester.patron_id" => $patron2->borrowernumber })
443       ->json_is('/count' => 2, 'there should be 2 biblios with suggestions of patron 2');
444
445     $schema->storage->txn_rollback;
446 };
447
448 subtest 'objects.search helper with q parameter' => sub {
449     plan tests => 4;
450
451     $schema->storage->txn_begin;
452
453     my $patron1 = $builder->build_object( { class => "Koha::Patrons" } );
454     my $patron2 = $builder->build_object( { class => "Koha::Patrons" } );
455     my $biblio1 = $builder->build_sample_biblio;
456     my $biblio2 = $builder->build_sample_biblio;
457     my $biblio3 = $builder->build_sample_biblio;
458     my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron1->borrowernumber, biblionumber => $biblio1->biblionumber} } );
459     my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio2->biblionumber} } );
460     my $suggestion3 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio3->biblionumber} } );
461
462     my $t = Test::Mojo->new;
463     $t->get_ok('/biblios?q={"suggestions.suggester.patron_id": "'.$patron1->borrowernumber.'"}')
464       ->json_is('/count' => 1, 'there should be 1 biblio with suggestions of patron 1');
465
466     $t->get_ok('/biblios?q={"suggestions.suggester.patron_id": "'.$patron2->borrowernumber.'"}')
467       ->json_is('/count' => 2, 'there should be 2 biblios with suggestions of patron 2');
468
469     $schema->storage->txn_rollback;
470 };
471
472 subtest 'objects.search helper with x-koha-query header' => sub {
473     plan tests => 4;
474
475     $schema->storage->txn_begin;
476
477     my $patron1 = $builder->build_object( { class => "Koha::Patrons" } );
478     my $patron2 = $builder->build_object( { class => "Koha::Patrons" } );
479     my $biblio1 = $builder->build_sample_biblio;
480     my $biblio2 = $builder->build_sample_biblio;
481     my $biblio3 = $builder->build_sample_biblio;
482     my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron1->borrowernumber, biblionumber => $biblio1->biblionumber} } );
483     my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio2->biblionumber} } );
484     my $suggestion3 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio3->biblionumber} } );
485
486     my $t = Test::Mojo->new;
487     $t->get_ok('/biblios' => {'x-koha-query' => '{"suggestions.suggester.patron_id": "'.$patron1->borrowernumber.'"}'})
488       ->json_is('/count' => 1, 'there should be 1 biblio with suggestions of patron 1');
489
490     $t->get_ok('/biblios' => {'x-koha-query' => '{"suggestions.suggester.patron_id": "'.$patron2->borrowernumber.'"}'})
491       ->json_is('/count' => 2, 'there should be 2 biblios with suggestions of patron 2');
492
493     $schema->storage->txn_rollback;
494 };
495
496 subtest 'objects.search helper with all query methods' => sub {
497     plan tests => 6;
498
499     $schema->storage->txn_begin;
500
501     my $patron1 = $builder->build_object( { class => "Koha::Patrons" , value => {firstname=>'patron1'} } );
502     my $patron2 = $builder->build_object( { class => "Koha::Patrons" , value => {firstname=>'patron2'} } );
503     my $biblio1 = $builder->build_sample_biblio;
504     my $biblio2 = $builder->build_sample_biblio;
505     my $biblio3 = $builder->build_sample_biblio;
506     my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron1->borrowernumber, biblionumber => $biblio1->biblionumber} } );
507     my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio2->biblionumber} } );
508     my $suggestion3 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio3->biblionumber} } );
509
510     my $t = Test::Mojo->new;
511     $t->get_ok('/biblios?q={"suggestions.suggester.firstname": "'.$patron1->firstname.'"}' => {'x-koha-query' => '{"suggestions.suggester.patron_id": "'.$patron1->borrowernumber.'"}'} => json => {"suggestions.suggester.cardnumber" => $patron1->cardnumber})
512       ->json_is('/count' => 1, 'there should be 1 biblio with suggestions of patron 1');
513
514     $t->get_ok('/biblios?q={"suggestions.suggester.firstname": "'.$patron2->firstname.'"}' => {'x-koha-query' => '{"suggestions.suggester.patron_id": "'.$patron2->borrowernumber.'"}'} => json => {"suggestions.suggester.cardnumber" => $patron2->cardnumber})
515       ->json_is('/count' => 2, 'there should be 2 biblios with suggestions of patron 2');
516
517     $t->get_ok('/biblios?q={"suggestions.suggester.firstname": "'.$patron1->firstname.'"}' => {'x-koha-query' => '{"suggestions.suggester.patron_id": "'.$patron2->borrowernumber.'"}'} => json => {"suggestions.suggester.cardnumber" => $patron2->cardnumber})
518       ->json_is('/count' => 0, 'there shouldn\'t be biblios where suggester has patron1 fistname and patron2 id');
519
520     $schema->storage->txn_rollback;
521 };
522
523 subtest 'objects.search helper order by embedded columns' => sub {
524     plan tests => 3;
525
526     $schema->storage->txn_begin;
527
528     my $patron1 = $builder->build_object( { class => "Koha::Patrons" , value => {firstname=>'patron1'} } );
529     my $patron2 = $builder->build_object( { class => "Koha::Patrons" , value => {firstname=>'patron2'} } );
530     my $biblio1 = $builder->build_sample_biblio;
531     my $biblio2 = $builder->build_sample_biblio;
532     my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron1->borrowernumber, biblionumber => $biblio1->biblionumber} } );
533     my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio2->biblionumber} } );
534
535     my $t = Test::Mojo->new;
536     $t->get_ok('/biblios?_order_by=-suggestions.suggester.firstname' => json => [{"me.biblio_id" => $biblio1->biblionumber}, {"me.biblio_id" => $biblio2->biblionumber}])
537       ->json_is('/biblios/0/biblio_id' => $biblio2->biblionumber, 'Biblio 2 should be first')
538       ->json_is('/biblios/1/biblio_id' => $biblio1->biblionumber, 'Biblio 1 should be second');
539
540     $schema->storage->txn_rollback;
541 };
542
543 subtest 'objects.search_rs helper' => sub {
544     plan tests => 3;
545
546     $schema->storage->txn_begin;
547
548     # Remove existing cities to have more control on the search results
549     Koha::Cities->delete;
550
551  # Create three sample cities that match the query. This makes sure we
552  # always have a "next" link regardless of Mojolicious::Plugin::OpenAPI version.
553     $builder->build_object(
554         {
555             class => 'Koha::Cities',
556             value => {
557                 city_name => 'city1'
558             }
559         }
560     );
561     $builder->build_object(
562         {
563             class => 'Koha::Cities',
564             value => {
565                 city_name => 'city2'
566             }
567         }
568     );
569     $builder->build_object(
570         {
571             class => 'Koha::Cities',
572             value => {
573                 city_name => 'city3'
574             }
575         }
576     );
577
578     my $t = Test::Mojo->new;
579     $t->get_ok('/cities/rs')->status_is(200)->json_is( '/count' => 3 );
580
581     $schema->storage->txn_rollback;
582 };
583
584 subtest 'objects.find helper' => sub {
585
586     plan tests => 9;
587
588     my $t = Test::Mojo->new;
589
590     $schema->storage->txn_begin;
591
592     my $city_1 = $builder->build_object( { class => 'Koha::Cities' } );
593     my $city_2 = $builder->build_object( { class => 'Koha::Cities' } );
594
595     $t->get_ok( '/cities/' . $city_1->id )
596       ->status_is(200)
597       ->json_is( $city_1->to_api );
598
599     $t->get_ok( '/cities/' . $city_2->id )
600       ->status_is(200)
601       ->json_is( $city_2->to_api );
602
603     # Remove the city
604     my $city_2_id = $city_2->id;
605     $city_2->delete;
606     $t->get_ok( '/cities/' . $city_2_id )
607       ->status_is(200)
608       ->json_is( undef );
609
610     $schema->storage->txn_rollback;
611 };
612
613 subtest 'objects.find helper, embed' => sub {
614
615     plan tests => 2;
616
617     my $t = Test::Mojo->new;
618
619     $schema->storage->txn_begin;
620
621     my $order = $builder->build_object({ class => 'Koha::Acquisition::Orders' });
622
623     $t->get_ok( '/orders/' . $order->ordernumber )
624       ->json_is( $order->to_api( { embed => ( { fund => {} } ) } ) );
625
626     $schema->storage->txn_rollback;
627 };
628
629 subtest 'objects.search helper, public requests' => sub {
630
631     plan tests => 3;
632
633     $schema->storage->txn_begin;
634
635     my $library_1 = $builder->build_object({ class => 'Koha::Libraries', value => { branchname => 'A' } });
636     my $library_2 = $builder->build_object({ class => 'Koha::Libraries', value => { branchname => 'B' } });
637
638     my $t = Test::Mojo->new;
639
640     $t->get_ok( '/libraries/'.$library_1->id.'/'.$library_2->id )
641       ->json_is('/0' => $library_1->to_api({ public => 1 }), 'Public representation of $library_1 is retrieved')
642       ->json_is('/1' => $library_2->to_api({ public => 1 }), 'Public representation of $library_2 is retrieved');
643
644     $schema->storage->txn_rollback;
645 };
646
647 subtest 'objects.search helper, search_limited() tests' => sub {
648
649     plan tests => 9;
650
651     $schema->storage->txn_begin;
652
653     my $library_1 = $builder->build_object({ class => 'Koha::Libraries' });
654     my $library_2 = $builder->build_object({ class => 'Koha::Libraries' });
655
656     my $patron_1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $library_1->id } });
657     my $patron_2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $library_1->id } });
658     my $patron_3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $library_2->id } });
659
660     my @libraries_where_can_see_patrons = ( $library_1->id, $library_2->id );
661
662     my $t = Test::Mojo->new;
663
664     my $mocked_patron = Test::MockModule->new('Koha::Patron');
665     $mocked_patron->mock( 'libraries_where_can_see_patrons', sub
666         {
667             return @libraries_where_can_see_patrons;
668         }
669     );
670
671     my $patron = $builder->build_object(
672         {
673             class => 'Koha::Patrons',
674             value => { flags => 2**4 }    # borrowers flag = 4
675         }
676     );
677
678     t::lib::Mocks::mock_userenv({ patron => $patron });
679
680     $t->get_ok( "/my_patrons?q=" . encode_json( { library_id => [ $library_1->id, $library_2->id ] } ) )
681       ->status_is(200)
682       ->json_is( '/0/patron_id' => $patron_1->id )
683       ->json_is( '/1/patron_id' => $patron_2->id )
684       ->json_is( '/2/patron_id' => $patron_3->id );
685
686     @libraries_where_can_see_patrons = ( $library_2->id );
687
688     my $res = $t->get_ok( "/my_patrons?q=" . encode_json( { library_id => [ $library_1->id, $library_2->id ] } ) )
689       ->status_is(200)
690       ->json_is( '/0/patron_id' => $patron_3->id, 'Returns the only allowed patron' )
691       ->tx->res->json;
692
693     is( scalar @{$res}, 1, 'Only one patron returned' );
694
695     $schema->storage->txn_rollback;
696 };
697
698 subtest 'objects.find helper with expanded authorised values' => sub {
699
700     plan tests => 18;
701
702     $schema->storage->txn_begin;
703
704     my $t = Test::Mojo->new;
705
706     Koha::AuthorisedValues->search( { category => 'Countries' } )->delete;
707     Koha::AuthorisedValueCategories->search( { category_name => 'Countries' } )
708       ->delete;
709
710     my $cat = $builder->build_object(
711         {
712             class => 'Koha::AuthorisedValueCategories',
713             value => { category_name => 'Countries' }
714         }
715     );
716     my $fr = $builder->build_object(
717         {
718             class => 'Koha::AuthorisedValues',
719             value => {
720                 authorised_value => 'FR',
721                 lib              => 'France',
722                 category         => $cat->category_name
723             }
724         }
725     );
726     my $us = $builder->build_object(
727         {
728             class => 'Koha::AuthorisedValues',
729             value => {
730                 authorised_value => 'US',
731                 lib              => 'United States of America',
732                 category         => $cat->category_name
733             }
734         }
735     );
736     my $ar = $builder->build_object(
737         {
738             class => 'Koha::AuthorisedValues',
739             value => {
740                 authorised_value => 'AR',
741                 lib              => 'Argentina',
742                 category         => $cat->category_name
743             }
744         }
745     );
746
747     my $city_class = Test::MockModule->new('Koha::City');
748     $city_class->mock(
749         'strings_map',
750         sub {
751             my ($self, $params) = @_;
752             use Koha::AuthorisedValues;
753
754             my $av = Koha::AuthorisedValues->find(
755                 {
756                     authorised_value => $self->city_country,
757                     category         => 'Countries'
758                 }
759             );
760
761             return {
762                 city_country => {
763                     category => $av->category,
764                     str      => ( $params->{public} ) ? $av->lib_opac : $av->lib,
765                     type     => 'av',
766                 }
767             };
768         }
769     );
770
771     my $manuel = $builder->build_object(
772         {
773             class => 'Koha::Cities',
774             value => {
775                 city_name    => 'Manuel',
776                 city_country => 'AR'
777             }
778         }
779     );
780     my $manuela = $builder->build_object(
781         {
782             class => 'Koha::Cities',
783             value => {
784                 city_name    => 'Manuela',
785                 city_country => 'US'
786             }
787         }
788     );
789
790     $t->get_ok( '/cities/' . $manuel->id => { 'x-koha-embed' => '+strings' } )
791       ->status_is(200)->json_is( '/name' => 'Manuel' )
792       ->json_has('/_strings')
793       ->json_is( '/_strings/country/type'     => 'av' )
794       ->json_is( '/_strings/country/category' => $cat->category_name )
795       ->json_is( '/_strings/country/str'      => $ar->lib );
796
797     $t->get_ok( '/cities/' . $manuel->id => { 'x-koha-embed' => '' } )
798       ->status_is(200)->json_is( '/name' => 'Manuel' )
799       ->json_hasnt('/_strings');
800
801     $t->get_ok( '/cities/' . $manuela->id => { 'x-koha-embed' => '+strings' } )
802       ->status_is(200)->json_is( '/name' => 'Manuela' )
803       ->json_has('/_strings')
804       ->json_is( '/_strings/country/type'     => 'av' )
805       ->json_is( '/_strings/country/category' => $cat->category_name )
806       ->json_is( '/_strings/country/str'      => $us->lib );
807
808     $schema->storage->txn_rollback;
809 };
810
811 subtest 'objects.search helper with expanded authorised values' => sub {
812
813     plan tests => 24;
814
815     my $t = Test::Mojo->new;
816
817     $schema->storage->txn_begin;
818
819     Koha::AuthorisedValues->search( { category => 'Countries' } )->delete;
820     Koha::AuthorisedValueCategories->search( { category_name => 'Countries' } )
821       ->delete;
822
823     my $cat = $builder->build_object(
824         {
825             class => 'Koha::AuthorisedValueCategories',
826             value => { category_name => 'Countries' }
827         }
828     );
829     my $fr = $builder->build_object(
830         {
831             class => 'Koha::AuthorisedValues',
832             value => {
833                 authorised_value => 'FR',
834                 lib              => 'France',
835                 category         => $cat->category_name
836             }
837         }
838     );
839     my $us = $builder->build_object(
840         {
841             class => 'Koha::AuthorisedValues',
842             value => {
843                 authorised_value => 'US',
844                 lib              => 'United States of America',
845                 category         => $cat->category_name
846             }
847         }
848     );
849     my $ar = $builder->build_object(
850         {
851             class => 'Koha::AuthorisedValues',
852             value => {
853                 authorised_value => 'AR',
854                 lib              => 'Argentina',
855                 category         => $cat->category_name
856             }
857         }
858     );
859
860     my $city_class = Test::MockModule->new('Koha::City');
861     $city_class->mock(
862         'strings_map',
863         sub {
864             my ($self, $params) = @_;
865             use Koha::AuthorisedValues;
866
867             my $av = Koha::AuthorisedValues->find(
868                 {
869                     authorised_value => $self->city_country,
870                     category         => 'Countries'
871                 }
872             );
873
874             return {
875                 city_country => {
876                     category => $av->category,
877                     str      => ( $params->{public} ) ? $av->lib_opac : $av->lib,
878                     type     => 'av',
879                 }
880             };
881         }
882     );
883
884
885     $builder->build_object(
886         {
887             class => 'Koha::Cities',
888             value => {
889                 city_name    => 'Manuel',
890                 city_country => 'AR'
891             }
892         }
893     );
894     $builder->build_object(
895         {
896             class => 'Koha::Cities',
897             value => {
898                 city_name    => 'Manuela',
899                 city_country => 'US'
900             }
901         }
902     );
903
904     $t->get_ok( '/cities?name=manuel&_per_page=4&_page=1&_match=starts_with' =>
905           { 'x-koha-embed' => '+strings' } )->status_is(200)
906       ->json_has('/0')->json_has('/1')->json_hasnt('/2')
907       ->json_is( '/0/name' => 'Manuel' )
908       ->json_has('/0/_strings')
909       ->json_is( '/0/_strings/country/str'      => $ar->lib )
910       ->json_is( '/0/_strings/country/type'     => 'av' )
911       ->json_is( '/0/_strings/country/category' => $cat->category_name )
912       ->json_is( '/1/name' => 'Manuela' )
913       ->json_has('/1/_strings')
914       ->json_is( '/1/_strings/country/str' => $us->lib )
915       ->json_is( '/1/_strings/country/type'     => 'av' )
916       ->json_is( '/1/_strings/country/category' => $cat->category_name );
917
918     $t->get_ok( '/cities?name=manuel&_per_page=4&_page=1&_match=starts_with' )->status_is(200)
919       ->json_has('/0')->json_has('/1')->json_hasnt('/2')
920       ->json_is( '/0/name' => 'Manuel' )->json_hasnt('/0/_strings')
921       ->json_is( '/1/name' => 'Manuela' )->json_hasnt('/1/_strings');
922
923     $schema->storage->txn_rollback;
924 };
925
926 subtest 'objects.find_rs helper' => sub {
927     plan tests => 9;
928
929     $schema->storage->txn_begin;
930
931     # Remove existing cities to have more control on the search results
932     Koha::Cities->delete;
933
934  # Create three sample cities that match the query. This makes sure we
935  # always have a "next" link regardless of Mojolicious::Plugin::OpenAPI version.
936     my $city1 = $builder->build_object(
937         {
938             class => 'Koha::Cities',
939             value => {
940                 city_name => 'city1'
941             }
942         }
943     );
944     my $city2 = $builder->build_object(
945         {
946             class => 'Koha::Cities',
947             value => {
948                 city_name => 'city2'
949             }
950         }
951     );
952     my $city3 = $builder->build_object(
953         {
954             class => 'Koha::Cities',
955             value => {
956                 city_name => 'city3'
957             }
958         }
959     );
960
961     my $t = Test::Mojo->new;
962
963     $t->get_ok( '/cities/' . $city1->id . '/rs' )->status_is(200)
964       ->json_is( '/name' => 'city1' );
965
966     $t->get_ok( '/cities/' . $city2->id . '/rs' )->status_is(200)
967       ->json_is( '/name' => 'city2' );
968
969     $t->get_ok( '/cities/' . $city3->id . '/rs' )->status_is(200)
970       ->json_is( '/name' => 'city3' );
971
972     $schema->storage->txn_rollback;
973 };
974
975 subtest 'objects.find helper, search_limited() tests' => sub {
976
977     plan tests => 12;
978
979     $schema->storage->txn_begin;
980
981     my $library_1 = $builder->build_object( { class => 'Koha::Libraries' } );
982     my $library_2 = $builder->build_object( { class => 'Koha::Libraries' } );
983
984     my $patron_1 = $builder->build_object( { class => 'Koha::Patrons', value => { branchcode => $library_1->id } } );
985     my $patron_2 = $builder->build_object( { class => 'Koha::Patrons', value => { branchcode => $library_2->id } } );
986
987     my @libraries_where_can_see_patrons = ( $library_1->id, $library_2->id );
988
989     my $t = Test::Mojo->new;
990
991     my $mocked_patron = Test::MockModule->new('Koha::Patron');
992     $mocked_patron->mock(
993         'libraries_where_can_see_patrons',
994         sub {
995             return @libraries_where_can_see_patrons;
996         }
997     );
998
999     my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
1000
1001     t::lib::Mocks::mock_userenv( { patron => $patron } );
1002
1003     $t->get_ok( "/my_patrons/" . $patron_1->id )->status_is(200)->json_is( '/patron_id' => $patron_1->id );
1004
1005     $t->get_ok( "/my_patrons/" . $patron_2->id )->status_is(200)->json_is( '/patron_id' => $patron_2->id );
1006
1007     @libraries_where_can_see_patrons = ( $library_2->id );
1008
1009     $t->get_ok( "/my_patrons/" . $patron_1->id )->status_is(200)->json_is(undef);
1010
1011     $t->get_ok( "/my_patrons/" . $patron_2->id )->status_is(200)->json_is( '/patron_id' => $patron_2->id );
1012
1013     $schema->storage->txn_rollback;
1014 };