Bug 18292: Remove return 1 statements in tests
[koha.git] / t / db_dependent / api / v1 / cities.t
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 => 5;
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::Cities;
29 use Koha::Database;
30
31 my $schema  = Koha::Database->new->schema;
32 my $builder = t::lib::TestBuilder->new;
33
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' );
37
38 my $remote_address = '127.0.0.1';
39 my $t              = Test::Mojo->new('Koha::REST::V1');
40
41 subtest 'list() tests' => sub {
42
43     plan tests => 18;
44
45     $schema->storage->txn_begin;
46
47     Koha::Cities->search->delete;
48     my ( $borrowernumber, $session_id ) =
49       create_user_and_session( { authorized => 0 } );
50
51     ## Authorized user tests
52     # No cities, so empty array should be returned
53     my $tx = $t->ua->build_tx( GET => '/api/v1/cities' );
54     $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
55     $tx->req->env( { REMOTE_ADDR => $remote_address } );
56     $t->request_ok($tx)->status_is(200)->json_is( [] );
57
58     my $city_country = 'France';
59     my $city         = $builder->build(
60         { source => 'City', value => { city_country => $city_country } } );
61
62     # One city created, should get returned
63     $tx = $t->ua->build_tx( GET => '/api/v1/cities' );
64     $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
65     $tx->req->env( { REMOTE_ADDR => $remote_address } );
66     $t->request_ok($tx)->status_is(200)->json_is( [$city] );
67
68     my $another_city = $builder->build(
69         { source => 'City', value => { city_country => $city_country } } );
70     my $city_with_another_country = $builder->build( { source => 'City' } );
71
72     # Two cities created, they should both be returned
73     $tx = $t->ua->build_tx( GET => '/api/v1/cities' );
74     $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
75     $tx->req->env( { REMOTE_ADDR => $remote_address } );
76     $t->request_ok($tx)->status_is(200)
77       ->json_is( [ $city, $another_city, $city_with_another_country ] );
78
79     # Filtering works, two cities sharing city_country
80     $tx =
81       $t->ua->build_tx( GET => "/api/v1/cities?city_country=" . $city_country );
82     $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
83     $tx->req->env( { REMOTE_ADDR => $remote_address } );
84     my $result =
85       $t->request_ok($tx)->status_is(200)->json_is( [ $city, $another_city ] );
86
87     $tx = $t->ua->build_tx(
88         GET => "/api/v1/cities?city_name=" . $city->{city_name} );
89     $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
90     $tx->req->env( { REMOTE_ADDR => $remote_address } );
91     $t->request_ok($tx)->status_is(200)->json_is( [$city] );
92
93     # Warn on unsupported query parameter
94     $tx = $t->ua->build_tx( GET => '/api/v1/cities?city_blah=blah' );
95     $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
96     $tx->req->env( { REMOTE_ADDR => $remote_address } );
97     $t->request_ok($tx)->status_is(400)
98       ->json_is( [{ path => '/query/city_blah', message => 'Malformed query string'}] );
99
100     $schema->storage->txn_rollback;
101 };
102
103 subtest 'get() tests' => sub {
104
105     plan tests => 6;
106
107     $schema->storage->txn_begin;
108
109     my $city = $builder->build( { source => 'City' } );
110     my ( $borrowernumber, $session_id ) =
111       create_user_and_session( { authorized => 0 } );
112
113     my $tx = $t->ua->build_tx( GET => "/api/v1/cities/" . $city->{cityid} );
114     $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
115     $tx->req->env( { REMOTE_ADDR => $remote_address } );
116     $t->request_ok($tx)->status_is(200)->json_is($city);
117
118     my $non_existent_id = $city->{cityid} + 1;
119     $tx = $t->ua->build_tx( GET => "/api/v1/cities/" . $non_existent_id );
120     $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
121     $tx->req->env( { REMOTE_ADDR => $remote_address } );
122     $t->request_ok($tx)->status_is(404)
123       ->json_is( '/error' => 'City not found' );
124
125     $schema->storage->txn_rollback;
126 };
127
128 subtest 'add() tests' => sub {
129
130     plan tests => 17;
131
132     $schema->storage->txn_begin;
133
134     my ( $unauthorized_borrowernumber, $unauthorized_session_id ) =
135       create_user_and_session( { authorized => 0 } );
136     my ( $authorized_borrowernumber, $authorized_session_id ) =
137       create_user_and_session( { authorized => 1 } );
138     my $city = {
139         city_name    => "City Name",
140         city_state   => "City State",
141         city_zipcode => "City Zipcode",
142         city_country => "City Country"
143     };
144
145     # Unauthorized attempt to write
146     my $tx = $t->ua->build_tx( POST => "/api/v1/cities/" => json => $city );
147     $tx->req->cookies(
148         { name => 'CGISESSID', value => $unauthorized_session_id } );
149     $tx->req->env( { REMOTE_ADDR => $remote_address } );
150     $t->request_ok($tx)->status_is(403);
151
152     # Authorized attempt to write invalid data
153     my $city_with_invalid_field = {
154         city_blah    => "City Blah",
155         city_state   => "City State",
156         city_zipcode => "City Zipcode",
157         city_country => "City Country"
158     };
159
160     $tx = $t->ua->build_tx(
161         POST => "/api/v1/cities/" => json => $city_with_invalid_field );
162     $tx->req->cookies(
163         { name => 'CGISESSID', value => $authorized_session_id } );
164     $tx->req->env( { REMOTE_ADDR => $remote_address } );
165     $t->request_ok($tx)->status_is(400)->json_is(
166         "/errors" => [
167             {
168                 message => "Properties not allowed: city_blah.",
169                 path    => "/body"
170             }
171         ]
172     );
173
174     # Authorized attempt to write
175     $tx = $t->ua->build_tx( POST => "/api/v1/cities/" => json => $city );
176     $tx->req->cookies(
177         { name => 'CGISESSID', value => $authorized_session_id } );
178     $tx->req->env( { REMOTE_ADDR => $remote_address } );
179     my $cityid = $t->request_ok($tx)->status_is(200)
180       ->json_is( '/city_name'    => $city->{city_name} )
181       ->json_is( '/city_state'   => $city->{city_state} )
182       ->json_is( '/city_zipcode' => $city->{city_zipcode} )
183       ->json_is( '/city_country' => $city->{city_country} )
184       ->tx->res->json->{cityid};
185
186     # Authorized attempt to create with null id
187     $city->{cityid} = undef;
188     $tx = $t->ua->build_tx(
189         POST => "/api/v1/cities/" => json => $city );
190     $tx->req->cookies(
191         { name => 'CGISESSID', value => $authorized_session_id } );
192     $tx->req->env( { REMOTE_ADDR => $remote_address } );
193     $t->request_ok($tx)->status_is(400)->json_has('/errors');
194
195     # Authorized attempt to create with existing id
196     $city->{cityid} = $cityid;
197     $tx = $t->ua->build_tx(
198         POST => "/api/v1/cities/" => json => $city );
199     $tx->req->cookies(
200         { name => 'CGISESSID', value => $authorized_session_id } );
201     $tx->req->env( { REMOTE_ADDR => $remote_address } );
202     $t->request_ok($tx)->status_is(400)->json_is(
203         "/errors" => [
204             {
205                 message => "Read-only.",
206                 path    => "/body/cityid"
207             }
208         ]
209     );
210
211     $schema->storage->txn_rollback;
212 };
213
214 subtest 'update() tests' => sub {
215
216     plan tests => 15;
217
218     $schema->storage->txn_begin;
219
220     my ( $unauthorized_borrowernumber, $unauthorized_session_id ) =
221       create_user_and_session( { authorized => 0 } );
222     my ( $authorized_borrowernumber, $authorized_session_id ) =
223       create_user_and_session( { authorized => 1 } );
224
225     my $city_id = $builder->build( { source => 'City' } )->{cityid};
226
227     # Unauthorized attempt to update
228     my $tx = $t->ua->build_tx( PUT => "/api/v1/cities/$city_id" => json =>
229           { city_name => 'New unauthorized name change' } );
230     $tx->req->cookies(
231         { name => 'CGISESSID', value => $unauthorized_session_id } );
232     $tx->req->env( { REMOTE_ADDR => $remote_address } );
233     $t->request_ok($tx)->status_is(403);
234
235     # Attempt partial update on a PUT
236     my $city_with_missing_field = {
237         city_name    => 'New name',
238         city_state   => 'New state',
239         city_country => 'New country'
240     };
241
242     $tx = $t->ua->build_tx(
243         PUT => "/api/v1/cities/$city_id" => json => $city_with_missing_field );
244     $tx->req->cookies(
245         { name => 'CGISESSID', value => $authorized_session_id } );
246     $tx->req->env( { REMOTE_ADDR => $remote_address } );
247     $t->request_ok($tx)->status_is(400)
248       ->json_is( "/errors" =>
249           [ { message => "Missing property.", path => "/body/city_zipcode" } ]
250       );
251
252     # Full object update on PUT
253     my $city_with_updated_field = {
254         city_name    => "London",
255         city_state   => "City State",
256         city_zipcode => "City Zipcode",
257         city_country => "City Country"
258     };
259
260     $tx = $t->ua->build_tx(
261         PUT => "/api/v1/cities/$city_id" => json => $city_with_updated_field );
262     $tx->req->cookies(
263         { name => 'CGISESSID', value => $authorized_session_id } );
264     $tx->req->env( { REMOTE_ADDR => $remote_address } );
265     $t->request_ok($tx)->status_is(200)->json_is( '/city_name' => 'London' );
266
267     # Authorized attempt to write invalid data
268     my $city_with_invalid_field = {
269         city_blah    => "City Blah",
270         city_state   => "City State",
271         city_zipcode => "City Zipcode",
272         city_country => "City Country"
273     };
274
275     $tx = $t->ua->build_tx(
276         PUT => "/api/v1/cities/$city_id" => json => $city_with_invalid_field );
277     $tx->req->cookies(
278         { name => 'CGISESSID', value => $authorized_session_id } );
279     $tx->req->env( { REMOTE_ADDR => $remote_address } );
280     $t->request_ok($tx)->status_is(400)->json_is(
281         "/errors" => [
282             {
283                 message => "Properties not allowed: city_blah.",
284                 path    => "/body"
285             }
286         ]
287     );
288
289     my $non_existent_id = $city_id + 1;
290     $tx =
291       $t->ua->build_tx( PUT => "/api/v1/cities/$non_existent_id" => json =>
292           $city_with_updated_field );
293     $tx->req->cookies(
294         { name => 'CGISESSID', value => $authorized_session_id } );
295     $tx->req->env( { REMOTE_ADDR => $remote_address } );
296     $t->request_ok($tx)->status_is(404);
297
298     $schema->storage->txn_rollback;
299
300     # Wrong mathod (POST)
301     $city_with_updated_field->{cityid} = 2;
302
303     $tx = $t->ua->build_tx(
304         POST => "/api/v1/cities/$city_id" => json => $city_with_updated_field );
305     $tx->req->cookies(
306         { name => 'CGISESSID', value => $authorized_session_id } );
307     $tx->req->env( { REMOTE_ADDR => $remote_address } );
308     $t->request_ok($tx)->status_is(404);
309 };
310
311 subtest 'delete() tests' => sub {
312
313     plan tests => 7;
314
315     $schema->storage->txn_begin;
316
317     my ( $unauthorized_borrowernumber, $unauthorized_session_id ) =
318       create_user_and_session( { authorized => 0 } );
319     my ( $authorized_borrowernumber, $authorized_session_id ) =
320       create_user_and_session( { authorized => 1 } );
321
322     my $city_id = $builder->build( { source => 'City' } )->{cityid};
323
324     # Unauthorized attempt to update
325     my $tx = $t->ua->build_tx( DELETE => "/api/v1/cities/$city_id" );
326     $tx->req->cookies(
327         { name => 'CGISESSID', value => $unauthorized_session_id } );
328     $tx->req->env( { REMOTE_ADDR => $remote_address } );
329     $t->request_ok($tx)->status_is(403);
330
331     $tx = $t->ua->build_tx( DELETE => "/api/v1/cities/$city_id" );
332     $tx->req->cookies(
333         { name => 'CGISESSID', value => $authorized_session_id } );
334     $tx->req->env( { REMOTE_ADDR => $remote_address } );
335     $t->request_ok($tx)->status_is(200)->content_is('');
336
337     $tx = $t->ua->build_tx( DELETE => "/api/v1/cities/$city_id" );
338     $tx->req->cookies(
339         { name => 'CGISESSID', value => $authorized_session_id } );
340     $tx->req->env( { REMOTE_ADDR => $remote_address } );
341     $t->request_ok($tx)->status_is(404);
342
343     $schema->storage->txn_rollback;
344 };
345
346 sub create_user_and_session {
347
348     my $args  = shift;
349     my $flags = ( $args->{authorized} ) ? $args->{authorized} : 0;
350     my $dbh   = C4::Context->dbh;
351
352     my $user = $builder->build(
353         {
354             source => 'Borrower',
355             value  => {
356                 flags => $flags
357             }
358         }
359     );
360
361     # Create a session for the authorized user
362     my $session = C4::Auth::get_session('');
363     $session->param( 'number',   $user->{borrowernumber} );
364     $session->param( 'id',       $user->{userid} );
365     $session->param( 'ip',       '127.0.0.1' );
366     $session->param( 'lasttime', time() );
367     $session->flush;
368
369     if ( $args->{authorized} ) {
370         $dbh->do( "
371             INSERT INTO user_permissions (borrowernumber,module_bit,code)
372             VALUES (?,3,'parameters_remaining_permissions')", undef,
373             $user->{borrowernumber} );
374     }
375
376     return ( $user->{borrowernumber}, $session->id );
377 }
378