View | Details | Raw Unified | Return to bug 38050
Collapse All | Expand All

(-)a/t/db_dependent/api/v1/public/lists.t (-53 / +378 lines)
Lines 18-24 Link Here
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::NoWarnings;
20
use Test::NoWarnings;
21
use Test::More tests => 2;
21
use Test::More tests => 6;
22
use Test::Mojo;
22
use Test::Mojo;
23
23
24
use JSON qw(encode_json);
24
use JSON qw(encode_json);
Lines 52-124 subtest 'list_public() tests' => sub { Link Here
52
    $patron_2->set_password( { password => $password, skip_validation => 1 } );
52
    $patron_2->set_password( { password => $password, skip_validation => 1 } );
53
    my $patron_2_userid = $patron_2->userid;
53
    my $patron_2_userid = $patron_2->userid;
54
54
55
    my $list_1 =
55
    my $list_1 = $builder->build_object(
56
        $builder->build_object( { class => 'Koha::Virtualshelves', value => { owner => $patron_1->id, public => 1 } } );
57
    my $list_2 =
58
        $builder->build_object( { class => 'Koha::Virtualshelves', value => { owner => $patron_1->id, public => 0 } } );
59
    my $list_3 =
60
        $builder->build_object( { class => 'Koha::Virtualshelves', value => { owner => $patron_2->id, public => 1 } } );
61
    my $list_4 =
62
        $builder->build_object( { class => 'Koha::Virtualshelves', value => { owner => $patron_2->id, public => 0 } } );
63
64
    my $q = encode_json(
65
        {
56
        {
66
            list_id => {
57
            class => 'Koha::Virtualshelves',
67
                -in => [
58
            value => { owner => $patron_1->id, public => 1 }
68
                    $list_1->id,
59
        }
69
                    $list_2->id,
60
    );
70
                    $list_3->id,
61
    my $list_2 = $builder->build_object(
71
                    $list_4->id,
62
        {
72
                ]
63
            class => 'Koha::Virtualshelves',
73
            }
64
            value => { owner => $patron_1->id, public => 0 }
65
        }
66
    );
67
    my $list_3 = $builder->build_object(
68
        {
69
            class => 'Koha::Virtualshelves',
70
            value => { owner => $patron_2->id, public => 1 }
74
        }
71
        }
75
    );
72
    );
73
    my $list_4 = $builder->build_object(
74
        {
75
            class => 'Koha::Virtualshelves',
76
            value => { owner => $patron_2->id, public => 0 }
77
        }
78
    );
79
80
    my $q = encode_json( { list_id => { -in => [ $list_1->id, $list_2->id, $list_3->id, $list_4->id, ] } } );
76
81
77
    # anonymous
82
    # anonymous
78
    $t->get_ok("/api/v1/public/lists?q=$q")->status_is( 200, "Anonymous users can only fetch public lists" )
83
    $t->get_ok("/api/v1/public/lists?q=$q")->status_is( 200, "Anonymous users can only fetch public lists" )->json_is(
79
        ->json_is( [ $list_1->to_api( { public => 1 } ), $list_3->to_api( { public => 1 } ) ] );
84
        [
85
            $list_1->to_api( { public => 1 } ),
86
            $list_3->to_api( { public => 1 } )
87
        ]
88
    );
80
89
81
    $t->get_ok("/api/v1/public/lists?q=$q&only_public=1")
90
    $t->get_ok("/api/v1/public/lists?q=$q&only_public=1")
82
        ->status_is( 200, "Anonymous users can only fetch public lists" )
91
        ->status_is( 200, "Anonymous users can only fetch public lists" )->json_is(
83
        ->json_is( [ $list_1->to_api( { public => 1 } ), $list_3->to_api( { public => 1 } ) ] );
92
        [
93
            $list_1->to_api( { public => 1 } ),
94
            $list_3->to_api( { public => 1 } )
95
        ]
96
        );
84
97
85
    $t->get_ok("/api/v1/public/lists?q=$q&only_mine=1")
98
    $t->get_ok("/api/v1/public/lists?q=$q&only_mine=1")->status_is(
86
        ->status_is( 400, "Passing only_mine on an anonymous session generates a 400 code" )
99
        400,
87
        ->json_is( '/error_code' => q{only_mine_forbidden} );
100
        "Passing only_mine on an anonymous session generates a 400 code"
101
    )->json_is( '/error_code' => q{only_mine_forbidden} );
88
102
89
    $t->get_ok("//$patron_1_userid:$password@/api/v1/public/lists?q=$q&only_mine=1")
103
    $t->get_ok("//$patron_1_userid:$password@/api/v1/public/lists?q=$q&only_mine=1")->status_is(
90
        ->status_is( 200, "Passing only_mine with a logged in user makes it return only their lists" )
104
        200,
91
        ->json_is( [ $list_1->to_api( { public => 1 } ), $list_2->to_api( { public => 1 } ) ] );
105
        "Passing only_mine with a logged in user makes it return only their lists"
106
    )->json_is(
107
        [
108
            $list_1->to_api( { public => 1 } ),
109
            $list_2->to_api( { public => 1 } )
110
        ]
111
    );
92
112
93
    $t->get_ok("//$patron_2_userid:$password@/api/v1/public/lists?q=$q&only_mine=1")
113
    $t->get_ok("//$patron_2_userid:$password@/api/v1/public/lists?q=$q&only_mine=1")->status_is(
94
        ->status_is( 200, "Passing only_mine with a logged in user makes it return only their lists" )
114
        200,
95
        ->json_is( [ $list_3->to_api( { public => 1 } ), $list_4->to_api( { public => 1 } ) ] );
115
        "Passing only_mine with a logged in user makes it return only their lists"
116
    )->json_is(
117
        [
118
            $list_3->to_api( { public => 1 } ),
119
            $list_4->to_api( { public => 1 } )
120
        ]
121
    );
96
122
97
    # only public
123
    # only public
98
    $t->get_ok("//$patron_1_userid:$password@/api/v1/public/lists?q=$q&only_public=1")
124
    $t->get_ok("//$patron_1_userid:$password@/api/v1/public/lists?q=$q&only_public=1")->status_is(
99
        ->status_is( 200, "Passing only_public with a logged in user makes it return only public lists" )
125
        200,
100
        ->json_is( [ $list_1->to_api( { public => 1 } ), $list_3->to_api( { public => 1 } ) ] );
126
        "Passing only_public with a logged in user makes it return only public lists"
101
127
    )->json_is(
102
    $t->get_ok("//$patron_2_userid:$password@/api/v1/public/lists?q=$q&only_public=1")
128
        [
103
        ->status_is( 200, "Passing only_public with a logged in user makes it return only public lists" )
129
            $list_1->to_api( { public => 1 } ),
104
        ->json_is( [ $list_1->to_api( { public => 1 } ), $list_3->to_api( { public => 1 } ) ] );
130
            $list_3->to_api( { public => 1 } )
105
131
        ]
106
    $t->get_ok("//$patron_1_userid:$password@/api/v1/public/lists?q=$q")
132
    );
107
        ->status_is( 200, "Not filtering with only_mine or only_public makes it return all accessible lists" )
133
108
        ->json_is(
134
    $t->get_ok("//$patron_2_userid:$password@/api/v1/public/lists?q=$q&only_public=1")->status_is(
109
        [ $list_1->to_api( { public => 1 } ), $list_2->to_api( { public => 1 } ), $list_3->to_api( { public => 1 } ) ]
135
        200,
136
        "Passing only_public with a logged in user makes it return only public lists"
137
    )->json_is(
138
        [
139
            $list_1->to_api( { public => 1 } ),
140
            $list_3->to_api( { public => 1 } )
141
        ]
142
    );
143
144
    $t->get_ok("//$patron_1_userid:$password@/api/v1/public/lists?q=$q")->status_is(
145
        200,
146
        "Not filtering with only_mine or only_public makes it return all accessible lists"
147
    )->json_is(
148
        [
149
            $list_1->to_api( { public => 1 } ),
150
            $list_2->to_api( { public => 1 } ),
151
            $list_3->to_api( { public => 1 } )
152
        ]
153
    );
154
155
    $t->get_ok("//$patron_2_userid:$password@/api/v1/public/lists?q=$q")->status_is(
156
        200,
157
        "Not filtering with only_mine or only_public makes it return all accessible lists"
158
    )->json_is(
159
        [
160
            $list_1->to_api( { public => 1 } ),
161
            $list_3->to_api( { public => 1 } ),
162
            $list_4->to_api( { public => 1 } )
163
        ]
164
    );
165
166
    # conflicting params
167
    $t->get_ok("//$patron_1_userid:$password@/api/v1/public/lists?q=$q&only_public=1&only_mine=1")->status_is(
168
        200,
169
        "Passing only_public with a logged in user makes it return only public lists"
170
    )->json_is( [ $list_1->to_api( { public => 1 } ) ] );
171
172
    $schema->storage->txn_rollback;
173
};
174
175
subtest 'public_get() tests' => sub {
176
    plan tests => 12;
177
178
    $schema->storage->txn_begin;
179
180
    my $password = 'thePassword123';
181
    my $patron_1 = $builder->build_object( { class => 'Koha::Patrons' } );
182
    my $patron_2 = $builder->build_object( { class => 'Koha::Patrons' } );
183
184
    $patron_1->set_password( { password => $password, skip_validation => 1 } );
185
    $patron_2->set_password( { password => $password, skip_validation => 1 } );
186
187
    my $patron_1_userid = $patron_1->userid;
188
    my $patron_2_userid = $patron_2->userid;
189
190
    # Create test lists
191
    my $public_list = $builder->build_object(
192
        {
193
            class => 'Koha::Virtualshelves',
194
            value => {
195
                owner                   => $patron_1->id,
196
                public                  => 1,
197
                allow_change_from_owner => 1
198
            }
199
        }
200
    );
201
202
    my $private_list = $builder->build_object(
203
        {
204
            class => 'Koha::Virtualshelves',
205
            value => {
206
                owner                   => $patron_1->id,
207
                public                  => 0,
208
                allow_change_from_owner => 0
209
            }
210
        }
211
    );
212
213
    # Test anonymous access to public list
214
    $t->get_ok( "/api/v1/public/lists/" . $public_list->id )->status_is(200)->json_is( $public_list->to_api );
215
216
    # Test anonymous access to private list
217
    $t->get_ok( "/api/v1/public/lists/" . $private_list->id )
218
        ->status_is( 403, 'Anonymous user cannot access private list' );
219
220
    # Test authenticated access - owner can see their private list
221
    $t->get_ok( "//$patron_1_userid:$password@/api/v1/public/lists/" . $private_list->id )->status_is(200)
222
        ->json_is( $private_list->to_api );
223
224
    # Test non-owner access to private list
225
    $t->get_ok( "//$patron_2_userid:$password@/api/v1/public/lists/" . $private_list->id )
226
        ->status_is( 403, 'Non-owner cannot access private list' );
227
228
    # Test non-existent list
229
    $t->get_ok("/api/v1/public/lists/99999999")->status_is( 404, "List not found" );
230
231
    $schema->storage->txn_rollback;
232
};
233
234
subtest 'public_add() tests' => sub {
235
    plan tests => 9;    # Reduced test count since we're removing one assertion
236
237
    $schema->storage->txn_begin;
238
239
    my $password       = 'thePassword123';
240
    my $patron         = $builder->build_object( { class => 'Koha::Patrons' } );
241
    my $another_patron = $builder->build_object( { class => 'Koha::Patrons' } );
242
    $patron->set_password( { password => $password, skip_validation => 1 } );
243
    my $patron_userid = $patron->userid;
244
245
    my $list_data = {
246
        name                     => "Test List",
247
        public                   => 1,
248
        allow_change_from_owner  => 1,
249
        allow_change_from_others => 0,
250
        default_sort_field       => 'title'
251
    };
252
253
    # Test anonymous attempt
254
    $t->post_ok( "/api/v1/public/lists" => json => $list_data )->status_is( 401, 'Anonymous user cannot create list' );
255
256
    # Test authenticated user can create list
257
    $t->post_ok( "//$patron_userid:$password@/api/v1/public/lists" => json => $list_data )->status_is(201)
258
        ->json_has( '/list_id', 'List ID is present in response' )
259
        ->json_has( '/name',    'List name is present in response' )->json_is(
260
        '/owner_id' => $patron->id,
261
        'List created with logged in user as owner'
110
        );
262
        );
111
263
112
    $t->get_ok("//$patron_2_userid:$password@/api/v1/public/lists?q=$q")
264
    # Test attempt to specify owner_id
113
        ->status_is( 200, "Not filtering with only_mine or only_public makes it return all accessible lists" )
265
    my $list_with_owner = {
114
        ->json_is(
266
        name                     => "Test List",
115
        [ $list_1->to_api( { public => 1 } ), $list_3->to_api( { public => 1 } ), $list_4->to_api( { public => 1 } ) ]
267
        owner_id                 => $another_patron->id,    # Should be rejected
268
        public                   => 1,
269
        allow_change_from_owner  => 1,
270
        allow_change_from_others => 0,
271
        default_sort_field       => 'title'
272
    };
273
274
    $t->post_ok( "//$patron_userid:$password@/api/v1/public/lists" => json => $list_with_owner )->status_is(400);
275
276
    $schema->storage->txn_rollback;
277
};
278
279
subtest 'public_update() tests' => sub {
280
    plan tests => 14;
281
282
    $schema->storage->txn_begin;
283
284
    my $password       = 'thePassword123';
285
    my $patron         = $builder->build_object( { class => 'Koha::Patrons' } );
286
    my $another_patron = $builder->build_object( { class => 'Koha::Patrons' } );
287
288
    $patron->set_password( { password => $password, skip_validation => 1 } );
289
    $another_patron->set_password( { password => $password, skip_validation => 1 } );
290
291
    my $patron_userid         = $patron->userid;
292
    my $another_patron_userid = $another_patron->userid;
293
294
    # Create a list that can be modified
295
    my $modifiable_list = $builder->build_object(
296
        {
297
            class => 'Koha::Virtualshelves',
298
            value => {
299
                owner                   => $patron->id,
300
                public                  => 1,
301
                allow_change_from_owner => 1
302
            }
303
        }
304
    );
305
306
    # Create a list that cannot be modified
307
    my $unmodifiable_list = $builder->build_object(
308
        {
309
            class => 'Koha::Virtualshelves',
310
            value => {
311
                owner                   => $patron->id,
312
                public                  => 1,
313
                allow_change_from_owner => 0
314
            }
315
        }
316
    );
317
318
    my $update_data = {
319
        name                     => "Updated List Name",
320
        public                   => 0,
321
        allow_change_from_owner  => 1,
322
        allow_change_from_others => 0,
323
        default_sort_field       => 'title'
324
    };
325
326
    # Test anonymous attempt
327
    $t->put_ok( "/api/v1/public/lists/" . $modifiable_list->id => json => $update_data )
328
        ->status_is( 401, 'Anonymous user cannot update list' );
329
330
    # Test non-owner update attempt
331
    $t->put_ok(
332
        "//$another_patron_userid:$password@/api/v1/public/lists/" . $modifiable_list->id => json => $update_data )
333
        ->status_is( 403, 'Non-owner cannot update list' );
334
335
    # Test owner update success
336
    $t->put_ok( "//$patron_userid:$password@/api/v1/public/lists/" . $modifiable_list->id => json => $update_data )
337
        ->status_is(200)->json_is( '/name' => 'Updated List Name', 'List name updated' )
338
        ->json_is( '/public' => 0, 'List privacy updated' );
339
340
    # Test update of non-existent list
341
    $t->put_ok( "//$patron_userid:$password@/api/v1/public/lists/99999999" => json => $update_data )
342
        ->status_is( 404, "List not found" );
343
344
    # Owner can always update their own list (allow_change_from_owner doesn't block property updates)
345
    my $update_data2 = {
346
        name                     => "Another Updated List Name",
347
        public                   => 0,
348
        allow_change_from_owner  => 1,
349
        allow_change_from_others => 0,
350
        default_sort_field       => 'title'
351
    };
352
    $t->put_ok( "//$patron_userid:$password@/api/v1/public/lists/" . $unmodifiable_list->id => json => $update_data2 )
353
        ->status_is( 200, "Owner can update list regardless of allow_change_from_owner flag" );
354
355
    # Create librarian with necessary permissions
356
    my $librarian = $builder->build_object(
357
        {
358
            class => 'Koha::Patrons',
359
            value => { flags => 2**13 }
360
        }
361
    );
362
    $librarian->set_password( { password => $password, skip_validation => 1 } );
363
    my $librarian_userid = $librarian->userid;
364
365
    # Test librarian using public endpoint
366
    $t->put_ok( "//$librarian_userid:$password@/api/v1/public/lists/" . $modifiable_list->id => json => $update_data )
367
        ->status_is(
368
        403,
369
        'Librarian must use admin endpoint to modify others lists'
116
        );
370
        );
117
371
118
    # conflicting params
372
    $schema->storage->txn_rollback;
119
    $t->get_ok("//$patron_1_userid:$password@/api/v1/public/lists?q=$q&only_public=1&only_mine=1")
373
};
120
        ->status_is( 200, "Passing only_public with a logged in user makes it return only public lists" )
374
121
        ->json_is( [ $list_1->to_api( { public => 1 } ) ] );
375
subtest 'public_delete() tests' => sub {
376
    plan tests => 12;
377
378
    $schema->storage->txn_begin;
379
380
    my $password       = 'thePassword123';
381
    my $patron         = $builder->build_object( { class => 'Koha::Patrons' } );
382
    my $another_patron = $builder->build_object( { class => 'Koha::Patrons' } );
383
384
    $patron->set_password( { password => $password, skip_validation => 1 } );
385
    $another_patron->set_password( { password => $password, skip_validation => 1 } );
386
387
    my $patron_userid         = $patron->userid;
388
    my $another_patron_userid = $another_patron->userid;
389
390
    # Create test lists for different scenarios
391
    my $modifiable_list = $builder->build_object(
392
        {
393
            class => 'Koha::Virtualshelves',
394
            value => {
395
                owner                   => $patron->id,
396
                public                  => 1,
397
                allow_change_from_owner => 1
398
            }
399
        }
400
    );
401
402
    my $other_patron_list = $builder->build_object(
403
        {
404
            class => 'Koha::Virtualshelves',
405
            value => {
406
                owner                   => $another_patron->id,
407
                public                  => 1,
408
                allow_change_from_owner => 1
409
            }
410
        }
411
    );
412
413
    my $unmodifiable_list = $builder->build_object(
414
        {
415
            class => 'Koha::Virtualshelves',
416
            value => {
417
                owner                   => $patron->id,
418
                public                  => 1,
419
                allow_change_from_owner => 0
420
            }
421
        }
422
    );
423
424
    # Test anonymous attempt
425
    $t->delete_ok( "/api/v1/public/lists/" . $modifiable_list->id )
426
        ->status_is( 401, 'Anonymous user cannot delete list' );
427
428
    # Test non-owner delete attempt
429
    $t->delete_ok( "//$another_patron_userid:$password@/api/v1/public/lists/" . $modifiable_list->id )
430
        ->status_is( 403, 'Non-owner cannot delete list' );
431
432
    # Test attempt to delete another patron's list
433
    $t->delete_ok( "//$patron_userid:$password@/api/v1/public/lists/" . $other_patron_list->id )
434
        ->status_is( 403, "Cannot delete another patron's list" );
435
436
    # Owner can always delete their own list (allow_change_from_owner doesn't block deletion)
437
    $t->delete_ok( "//$patron_userid:$password@/api/v1/public/lists/" . $unmodifiable_list->id )
438
        ->status_is( 204, "Owner can delete list regardless of allow_change_from_owner flag" );
439
440
    # Test delete of non-existent list
441
    $t->delete_ok("//$patron_userid:$password@/api/v1/public/lists/99999999")->status_is( 404, "List not found" );
442
443
    # Test successful delete by owner
444
    $t->delete_ok( "//$patron_userid:$password@/api/v1/public/lists/" . $modifiable_list->id )
445
        ->status_is( 204, 'List deleted successfully' );
122
446
123
    $schema->storage->txn_rollback;
447
    $schema->storage->txn_rollback;
124
};
448
};
125
- 
449
450
1;

Return to bug 38050