Line 0
Link Here
|
0 |
- |
1 |
#!/usr/bin/env perl |
|
|
2 |
|
3 |
use Modern::Perl; |
4 |
|
5 |
use Test::More tests => 5; |
6 |
use Test::Mojo; |
7 |
use Data::Dumper; |
8 |
|
9 |
use JSON qw(encode_json); |
10 |
|
11 |
use t::lib::TestBuilder; |
12 |
use t::lib::Mocks; |
13 |
|
14 |
use Koha::Virtualshelves; |
15 |
use Koha::Database; |
16 |
|
17 |
my $schema = Koha::Database->new->schema; |
18 |
my $builder = t::lib::TestBuilder->new; |
19 |
|
20 |
my $t = Test::Mojo->new('Koha::REST::V1'); |
21 |
t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); |
22 |
|
23 |
subtest 'list() tests' => sub { |
24 |
plan tests => 7; |
25 |
|
26 |
$schema->storage->txn_begin; |
27 |
|
28 |
my $password = 'thePassword123'; |
29 |
|
30 |
# Create librarian with necessary permissions |
31 |
my $librarian = $builder->build_object( |
32 |
{ |
33 |
class => 'Koha::Patrons', |
34 |
value => { flags => 2**13 } |
35 |
} |
36 |
); |
37 |
my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); |
38 |
|
39 |
$librarian->set_password( { password => $password, skip_validation => 1 } ); |
40 |
$patron->set_password( { password => $password, skip_validation => 1 } ); |
41 |
|
42 |
my $librarian_userid = $librarian->userid; |
43 |
my $patron_userid = $patron->userid; |
44 |
|
45 |
# Create test lists owned by different users |
46 |
my $list_1 = $builder->build_object( |
47 |
{ |
48 |
class => 'Koha::Virtualshelves', |
49 |
value => { owner => $librarian->id, public => 1 } |
50 |
} |
51 |
); |
52 |
my $list_2 = $builder->build_object( |
53 |
{ |
54 |
class => 'Koha::Virtualshelves', |
55 |
value => { owner => $librarian->id, public => 0 } |
56 |
} |
57 |
); |
58 |
|
59 |
my $q = encode_json( { list_id => { -in => [ $list_1->id, $list_2->id, ] } } ); |
60 |
|
61 |
# Test unauthorized access |
62 |
$t->get_ok("/api/v1/lists?q=$q")->status_is( 401, "Anonymous users cannot access admin lists endpoint" ); |
63 |
|
64 |
$t->get_ok("//$patron_userid:$password@/api/v1/lists?q=$q") |
65 |
->status_is( 403, "Regular patrons cannot access admin lists endpoint" ); |
66 |
|
67 |
# Test authorized access - use to_api method which is already tested |
68 |
my $expected = [ $list_1->to_api, $list_2->to_api ]; |
69 |
|
70 |
$t->get_ok("//$librarian_userid:$password@/api/v1/lists?q=$q")->status_is(200)->json_is($expected); |
71 |
|
72 |
$schema->storage->txn_rollback; |
73 |
}; |
74 |
|
75 |
subtest 'get() tests' => sub { |
76 |
plan tests => 11; |
77 |
|
78 |
$schema->storage->txn_begin; |
79 |
|
80 |
my $password = 'thePassword123'; |
81 |
|
82 |
# Create librarian with necessary permissions |
83 |
my $librarian = $builder->build_object( |
84 |
{ |
85 |
class => 'Koha::Patrons', |
86 |
value => { flags => 2**13 } |
87 |
} |
88 |
); |
89 |
my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); |
90 |
my $another_patron = $builder->build_object( { class => 'Koha::Patrons' } ); |
91 |
|
92 |
$librarian->set_password( { password => $password, skip_validation => 1 } ); |
93 |
$patron->set_password( { password => $password, skip_validation => 1 } ); |
94 |
|
95 |
my $librarian_userid = $librarian->userid; |
96 |
my $patron_userid = $patron->userid; |
97 |
|
98 |
# Create test lists with different attributes |
99 |
my $public_list = $builder->build_object( |
100 |
{ |
101 |
class => 'Koha::Virtualshelves', |
102 |
value => { |
103 |
owner => $librarian->id, |
104 |
public => 1, |
105 |
allow_change_from_owner => 1 |
106 |
} |
107 |
} |
108 |
); |
109 |
|
110 |
my $private_list = $builder->build_object( |
111 |
{ |
112 |
class => 'Koha::Virtualshelves', |
113 |
value => { |
114 |
owner => $patron->id, |
115 |
public => 0, |
116 |
allow_change_from_owner => 0 |
117 |
} |
118 |
} |
119 |
); |
120 |
|
121 |
my $another_private_list = $builder->build_object( |
122 |
{ |
123 |
class => 'Koha::Virtualshelves', |
124 |
value => { |
125 |
owner => $another_patron->id, |
126 |
public => 0, |
127 |
allow_change_from_owner => 0 |
128 |
} |
129 |
} |
130 |
); |
131 |
|
132 |
# Test unauthorized access |
133 |
$t->get_ok( "/api/v1/lists/" . $public_list->id ) |
134 |
->status_is( 401, "Anonymous users cannot access admin lists endpoint" ); |
135 |
|
136 |
# Test access without permission |
137 |
$t->get_ok( "//$patron_userid:$password@/api/v1/lists/" . $public_list->id ) |
138 |
->status_is( 403, "Regular patrons cannot access admin lists endpoint" ); |
139 |
|
140 |
# Test authorized access to public list |
141 |
$t->get_ok( "//$librarian_userid:$password@/api/v1/lists/" . $public_list->id )->status_is(200) |
142 |
->json_is( $public_list->to_api ); |
143 |
|
144 |
# Test authorized access to another patron's private list |
145 |
$t->get_ok( "//$librarian_userid:$password@/api/v1/lists/" . $private_list->id )->status_is( |
146 |
403, |
147 |
"Librarian cannot access private lists they don't own" |
148 |
); |
149 |
|
150 |
# Test non-existent list |
151 |
$t->get_ok("//$librarian_userid:$password@/api/v1/lists/99999999")->status_is( 404, "List not found" ); |
152 |
|
153 |
$schema->storage->txn_rollback; |
154 |
}; |
155 |
|
156 |
subtest 'add() tests' => sub { |
157 |
plan tests => 16; |
158 |
|
159 |
$schema->storage->txn_begin; |
160 |
|
161 |
my $password = 'thePassword123'; |
162 |
|
163 |
# Create librarian with necessary permissions |
164 |
my $librarian = $builder->build_object( |
165 |
{ |
166 |
class => 'Koha::Patrons', |
167 |
value => { flags => 2**13 } |
168 |
} |
169 |
); |
170 |
my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); |
171 |
my $another_patron = $builder->build_object( { class => 'Koha::Patrons' } ); |
172 |
|
173 |
$librarian->set_password( { password => $password, skip_validation => 1 } ); |
174 |
$patron->set_password( { password => $password, skip_validation => 1 } ); |
175 |
|
176 |
my $librarian_userid = $librarian->userid; |
177 |
my $patron_userid = $patron->userid; |
178 |
|
179 |
# Test unauthorized access |
180 |
my $list_data = { |
181 |
name => "Test List", |
182 |
owner_id => $librarian->id, |
183 |
public => 1, |
184 |
allow_change_from_owner => 1, allow_change_from_others => 0, |
185 |
default_sort_field => 'title' |
186 |
}; |
187 |
|
188 |
$t->post_ok( "/api/v1/lists" => json => $list_data )->status_is( 401, "Anonymous users cannot create lists" ); |
189 |
|
190 |
$t->post_ok( "//$patron_userid:$password@/api/v1/lists" => json => $list_data ) |
191 |
->status_is( 403, "Regular patrons cannot create lists" ); |
192 |
|
193 |
# Test authorized creation - list for self |
194 |
$t->post_ok( "//$librarian_userid:$password@/api/v1/lists" => json => $list_data )->status_is(201)->header_like( |
195 |
Location => qr|^/api/v1/lists/\d+|, |
196 |
'Location header is correct' |
197 |
)->json_has( '/list_id', 'List ID is present in response' ) |
198 |
->json_has( '/name', 'List name is present in response' ); |
199 |
|
200 |
# Test authorized creation - list for another patron |
201 |
my $list_for_patron = { |
202 |
name => "Test List for Patron", |
203 |
owner_id => $another_patron->id, |
204 |
public => 0, |
205 |
allow_change_from_owner => 1, |
206 |
allow_change_from_others => 0, |
207 |
default_sort_field => 'title' |
208 |
}; |
209 |
|
210 |
$t->post_ok( "//$librarian_userid:$password@/api/v1/lists" => json => $list_for_patron )->status_is(201)->json_is( |
211 |
'/owner_id' => $another_patron->id, |
212 |
'List created with correct owner' |
213 |
)->json_is( '/name' => 'Test List for Patron', 'List name set correctly' ); |
214 |
|
215 |
# Test creating list with invalid owner_id |
216 |
my $list_invalid_owner = { |
217 |
name => "Test List", |
218 |
owner_id => 999999, # Non-existent patron id |
219 |
public => 1, |
220 |
allow_change_from_owner => 1, |
221 |
allow_change_from_others => 0, |
222 |
default_sort_field => 'title' |
223 |
}; |
224 |
|
225 |
$t->post_ok( "//$librarian_userid:$password@/api/v1/lists" => json => $list_invalid_owner )->status_is(400) |
226 |
->json_like( '/error' => qr/Invalid owner_id/ ); |
227 |
|
228 |
$schema->storage->txn_rollback; |
229 |
}; |
230 |
|
231 |
subtest 'update() tests' => sub { |
232 |
plan tests => 27; |
233 |
|
234 |
$schema->storage->txn_begin; |
235 |
|
236 |
my $password = 'thePassword123'; |
237 |
|
238 |
# Create librarian with necessary permissions |
239 |
my $librarian = $builder->build_object( |
240 |
{ |
241 |
class => 'Koha::Patrons', |
242 |
value => { flags => 2**13 } |
243 |
} |
244 |
); |
245 |
my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); |
246 |
my $another_patron = $builder->build_object( { class => 'Koha::Patrons' } ); |
247 |
|
248 |
$librarian->set_password( { password => $password, skip_validation => 1 } ); |
249 |
$patron->set_password( { password => $password, skip_validation => 1 } ); |
250 |
|
251 |
my $librarian_userid = $librarian->userid; |
252 |
my $patron_userid = $patron->userid; |
253 |
|
254 |
# Create test list |
255 |
my $list = $builder->build_object( |
256 |
{ |
257 |
class => 'Koha::Virtualshelves', |
258 |
value => { |
259 |
owner => $librarian->id, |
260 |
public => 1, |
261 |
allow_change_from_owner => 1, |
262 |
allow_change_from_others => 0 |
263 |
} |
264 |
} |
265 |
); |
266 |
|
267 |
# Test unauthorized access |
268 |
my $update_data = { |
269 |
name => "Updated List Name 1", |
270 |
public => 0, |
271 |
allow_change_from_owner => 1, |
272 |
allow_change_from_others => 0, |
273 |
default_sort_field => 'title' |
274 |
}; |
275 |
|
276 |
$t->put_ok( "/api/v1/lists/" . $list->id => json => $update_data ) |
277 |
->status_is( 401, "Anonymous users cannot update lists" ); |
278 |
|
279 |
$t->put_ok( "//$patron_userid:$password@/api/v1/lists/" . $list->id => json => $update_data ) |
280 |
->status_is( 403, "Regular patrons cannot update lists" ); |
281 |
|
282 |
# Test successful update |
283 |
$t->put_ok( "//$librarian_userid:$password@/api/v1/lists/" . $list->id => json => $update_data )->status_is(200) |
284 |
->json_is( |
285 |
'/name' => 'Updated List Name 1', |
286 |
'List name updated correctly' |
287 |
)->json_is( '/public' => 0, 'List privacy updated correctly' ); |
288 |
|
289 |
# Test update of non-existent list |
290 |
$t->put_ok( "//$librarian_userid:$password@/api/v1/lists/99999999" => json => $update_data ) |
291 |
->status_is( 404, "Attempting to update non-existent list returns 404" ); |
292 |
|
293 |
# Test partial update - changed to include all required fields |
294 |
my $partial_update = { |
295 |
name => "Updated List Name 2", |
296 |
public => 1, |
297 |
allow_change_from_owner => 1, |
298 |
allow_change_from_others => 0, |
299 |
default_sort_field => 'title' |
300 |
}; |
301 |
|
302 |
$t->put_ok( "//$librarian_userid:$password@/api/v1/lists/" . $list->id => json => $partial_update )->status_is(200) |
303 |
->json_is( '/name' => "Updated List Name 2", "Update successful" ); |
304 |
|
305 |
# Test updating another patron's list with librarian permissions |
306 |
my $patron_list = $builder->build_object( |
307 |
{ |
308 |
class => 'Koha::Virtualshelves', |
309 |
value => { |
310 |
owner => $patron->id, |
311 |
public => 1, |
312 |
allow_change_from_owner => 1, |
313 |
allow_change_from_others => 1, |
314 |
allow_change_from_staff => 1 |
315 |
} |
316 |
} |
317 |
); |
318 |
|
319 |
my $update_data_2 = { |
320 |
name => "Updated List Name 3", |
321 |
public => 0, |
322 |
allow_change_from_owner => 1, |
323 |
allow_change_from_others => 0, |
324 |
default_sort_field => 'title' |
325 |
}; |
326 |
|
327 |
$t->put_ok( "//$librarian_userid:$password@/api/v1/lists/" . $patron_list->id => json => $update_data_2 ) |
328 |
->status_is(200)->json_is( '/name' => 'Updated List Name 3', 'Librarian can update other patron\'s list' ); |
329 |
|
330 |
# Test librarian updating their own list |
331 |
my $librarian_list = $builder->build_object( |
332 |
{ |
333 |
class => 'Koha::Virtualshelves', |
334 |
value => { |
335 |
owner => $librarian->id, |
336 |
public => 1, |
337 |
allow_change_from_owner => 0 # Even librarian should respect this flag for their own lists |
338 |
} |
339 |
} |
340 |
); |
341 |
|
342 |
$t->put_ok( "//$librarian_userid:$password@/api/v1/lists/" . $librarian_list->id => json => $update_data ) |
343 |
->status_is( |
344 |
403, |
345 |
"Even librarians must respect allow_change_from_owner for their own lists" |
346 |
); |
347 |
|
348 |
# Test update with allow_change_from_staff permission |
349 |
my $list_staff = $builder->build_object( |
350 |
{ |
351 |
class => 'Koha::Virtualshelves', |
352 |
value => { |
353 |
owner => $patron->id, |
354 |
public => 1, |
355 |
allow_change_from_owner => 1, |
356 |
allow_change_from_others => 0, |
357 |
allow_change_from_staff => 1 |
358 |
} |
359 |
} |
360 |
); |
361 |
|
362 |
my $update_data_3 = { |
363 |
name => "Updated List Name 4", |
364 |
public => 0, |
365 |
allow_change_from_owner => 1, |
366 |
allow_change_from_others => 0, |
367 |
default_sort_field => 'title' |
368 |
}; |
369 |
|
370 |
$t->put_ok( "//$librarian_userid:$password@/api/v1/lists/" . $list_staff->id => json => $update_data_3 ) |
371 |
->status_is(200) |
372 |
->json_is( '/name' => 'Updated List Name 4', 'Staff can update list with allow_change_from_staff' ); |
373 |
|
374 |
# Test updating list with no permissions |
375 |
my $no_permission_list = $builder->build_object( |
376 |
{ |
377 |
class => 'Koha::Virtualshelves', |
378 |
value => { |
379 |
owner => $patron->id, |
380 |
public => 1, |
381 |
allow_change_from_owner => 1, |
382 |
allow_change_from_others => 0, |
383 |
allow_change_from_staff => 0 |
384 |
} |
385 |
} |
386 |
); |
387 |
|
388 |
my $update_data_4 = { |
389 |
name => "Updated List Name 5", |
390 |
public => 0, |
391 |
allow_change_from_owner => 1, |
392 |
allow_change_from_others => 0, |
393 |
default_sort_field => 'title' |
394 |
}; |
395 |
|
396 |
$t->put_ok( "//$librarian_userid:$password@/api/v1/lists/" . $no_permission_list->id => json => $update_data_4 ) |
397 |
->status_is(403)->json_is( '/error' => 'Cannot modify list without proper permissions' ); |
398 |
|
399 |
# Test update when both permission flags are false |
400 |
my $no_perm_list = $builder->build_object( |
401 |
{ |
402 |
class => 'Koha::Virtualshelves', |
403 |
value => { |
404 |
owner => $patron->id, |
405 |
public => 1, |
406 |
allow_change_from_owner => 1, |
407 |
allow_change_from_others => 0, |
408 |
allow_change_from_staff => 0 |
409 |
} |
410 |
} |
411 |
); |
412 |
|
413 |
$t->put_ok( "//$librarian_userid:$password@/api/v1/lists/" . $no_perm_list->id => json => $update_data ) |
414 |
->status_is(403)->json_is( '/error' => 'Cannot modify list without proper permissions' ); |
415 |
|
416 |
$schema->storage->txn_rollback; |
417 |
}; |
418 |
|
419 |
subtest 'delete() tests' => sub { |
420 |
plan tests => 16; |
421 |
|
422 |
$schema->storage->txn_begin; |
423 |
|
424 |
my $password = 'thePassword123'; |
425 |
|
426 |
# Create librarian with necessary permissions |
427 |
my $librarian = $builder->build_object( |
428 |
{ |
429 |
class => 'Koha::Patrons', |
430 |
value => { flags => 2**13 } |
431 |
} |
432 |
); |
433 |
my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); |
434 |
|
435 |
$librarian->set_password( { password => $password, skip_validation => 1 } ); |
436 |
$patron->set_password( { password => $password, skip_validation => 1 } ); |
437 |
|
438 |
my $librarian_userid = $librarian->userid; |
439 |
my $patron_userid = $patron->userid; |
440 |
|
441 |
# Create test list |
442 |
my $list = $builder->build_object( |
443 |
{ |
444 |
class => 'Koha::Virtualshelves', |
445 |
value => { owner => $librarian->id } |
446 |
} |
447 |
); |
448 |
|
449 |
# Test unauthorized access |
450 |
$t->delete_ok( "/api/v1/lists/" . $list->id )->status_is( 401, "Anonymous users cannot delete lists" ); |
451 |
|
452 |
$t->delete_ok( "//$patron_userid:$password@/api/v1/lists/" . $list->id ) |
453 |
->status_is( 403, "Regular patrons cannot delete lists" ); |
454 |
|
455 |
# Test successful delete |
456 |
$t->delete_ok( "//$librarian_userid:$password@/api/v1/lists/" . $list->id ) |
457 |
->status_is( 204, "List deleted successfully" ); |
458 |
|
459 |
# Test non-existent list |
460 |
$t->delete_ok("//$librarian_userid:$password@/api/v1/lists/99999999") |
461 |
->status_is( 404, "Attempting to delete non-existent list returns 404" ); |
462 |
|
463 |
# Test deleting another patron's list with librarian permissions |
464 |
my $patron_list = $builder->build_object( |
465 |
{ |
466 |
class => 'Koha::Virtualshelves', |
467 |
value => { owner => $patron->id } |
468 |
} |
469 |
); |
470 |
|
471 |
$t->delete_ok( "//$librarian_userid:$password@/api/v1/lists/" . $patron_list->id ) |
472 |
->status_is( 204, "Librarian can delete other patron's list" ); |
473 |
|
474 |
# Test deleting list with allow_change_from_staff permission |
475 |
my $list_staff = $builder->build_object( |
476 |
{ |
477 |
class => 'Koha::Virtualshelves', |
478 |
value => { |
479 |
owner => $patron->id, |
480 |
public => 1, |
481 |
allow_change_from_owner => 1, |
482 |
allow_change_from_others => 0, |
483 |
allow_change_from_staff => 1 |
484 |
} |
485 |
} |
486 |
); |
487 |
|
488 |
$t->delete_ok( "//$librarian_userid:$password@/api/v1/lists/" . $list_staff->id ) |
489 |
->status_is( 204, "Staff can delete list with allow_change_from_staff" ); |
490 |
|
491 |
# Test deleting list with allow_change_from_others permission |
492 |
my $list_others = $builder->build_object( |
493 |
{ |
494 |
class => 'Koha::Virtualshelves', |
495 |
value => { |
496 |
owner => $patron->id, |
497 |
public => 1, |
498 |
allow_change_from_owner => 1, |
499 |
allow_change_from_others => 1, |
500 |
allow_change_from_staff => 0 |
501 |
} |
502 |
} |
503 |
); |
504 |
|
505 |
$t->delete_ok( "//$librarian_userid:$password@/api/v1/lists/" . $list_others->id ) |
506 |
->status_is( 204, "Can delete list with allow_change_from_others" ); |
507 |
|
508 |
# Test deleting list with no permissions |
509 |
my $no_permission_list = $builder->build_object( |
510 |
{ |
511 |
class => 'Koha::Virtualshelves', |
512 |
value => { |
513 |
owner => $patron->id, |
514 |
public => 1, |
515 |
allow_change_from_owner => 1, |
516 |
allow_change_from_others => 0, |
517 |
allow_change_from_staff => 0 |
518 |
} |
519 |
} |
520 |
); |
521 |
|
522 |
$t->delete_ok( "//$librarian_userid:$password@/api/v1/lists/" . $no_permission_list->id ) |
523 |
->status_is( 403, "Cannot delete list without proper permissions" ); |
524 |
|
525 |
$schema->storage->txn_rollback; |
526 |
}; |
527 |
|
528 |
1; |