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

(-)a/t/Koha/Auth/Permissions.t (+8 lines)
Lines 153-158 subtest 'superlibrarian tests' => sub { Link Here
153
        'CAN_user_clubs_edit_templates'                             => 1,
153
        'CAN_user_clubs_edit_templates'                             => 1,
154
        'CAN_user_clubs_enroll'                                     => 1,
154
        'CAN_user_clubs_enroll'                                     => 1,
155
        'CAN_user_clubs'                                            => 1,
155
        'CAN_user_clubs'                                            => 1,
156
        'CAN_user_displays_remove_items_from_display'               => 1,
157
        'CAN_user_displays_add_items_to_display'                    => 1,
158
        'CAN_user_displays_add_items_to_display_from_any_libraries' => 1,
159
        'CAN_user_displays_delete_display'                          => 1,
160
        'CAN_user_displays_edit_display'                            => 1,
161
        'CAN_user_displays_add_display'                             => 1,
162
        'CAN_user_displays_view_display'                            => 1,
163
        'CAN_user_displays_view_display'                            => 1,
156
        'CAN_user_coursereserves_add_reserves'                      => 1,
164
        'CAN_user_coursereserves_add_reserves'                      => 1,
157
        'CAN_user_coursereserves_delete_reserves'                   => 1,
165
        'CAN_user_coursereserves_delete_reserves'                   => 1,
158
        'CAN_user_coursereserves_manage_courses'                    => 1,
166
        'CAN_user_coursereserves_manage_courses'                    => 1,
(-)a/t/db_dependent/Circulation/Displays.t (+200 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2025-2026 Open Fifth Ltd
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <https://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use Test::More tests => 3;
23
use Test::NoWarnings;
24
25
use C4::Circulation qw( AddIssue AddReturn );
26
use Koha::Database;
27
use Koha::DateUtils qw( dt_from_string );
28
29
use t::lib::TestBuilder;
30
use t::lib::Mocks;
31
32
my $schema = Koha::Database->new->schema;
33
$schema->storage->txn_begin;
34
35
my $builder = t::lib::TestBuilder->new;
36
37
t::lib::Mocks::mock_preference( 'UseDisplayModule', 1 );
38
39
subtest 'display_return_over - yes - any library' => sub {
40
    plan tests => 5;
41
42
    # Create test data
43
    my $library1 = $builder->build_object( { class => 'Koha::Libraries' } );
44
    my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
45
    my $patron =
46
        $builder->build_object( { class => 'Koha::Patrons', value => { branchcode => $library1->branchcode } } );
47
    my $item =
48
        $builder->build_sample_item( { homebranch => $library1->branchcode, holdingbranch => $library1->branchcode } );
49
50
    # Mock userenv
51
    t::lib::Mocks::mock_userenv( { branchcode => $library1->branchcode } );
52
53
    # Create a display with 'yes - any library' return_over setting
54
    my $display = $builder->build_object(
55
        {
56
            class => 'Koha::Displays',
57
            value => {
58
                display_return_over => 'yes - any library',
59
                enabled             => 1,
60
                start_date          => undef,
61
                end_date            => undef,
62
            }
63
        }
64
    );
65
66
    # Add item to display
67
    my $display_item = $builder->build_object(
68
        {
69
            class => 'Koha::DisplayItems',
70
            value => {
71
                display_id   => $display->display_id,
72
                itemnumber   => $item->itemnumber,
73
                biblionumber => $item->biblionumber,
74
            }
75
        }
76
    );
77
78
    # Verify item is in display
79
    my $active_display = $item->active_display_item;
80
    ok( defined $active_display, 'Item should have an active display item' );
81
    is( $active_display->display_id, $display->display_id, 'Display item should be linked to the correct display' );
82
83
    # Issue the item
84
    AddIssue( $patron, $item->barcode );
85
86
    # Return the item at the home library
87
    my ( $doreturn, $messages, $issue, $borrower ) = AddReturn( $item->barcode, $library1->branchcode );
88
    is( $doreturn, 1, 'Item should be returned successfully' );
89
    ok( exists $messages->{RemovedFromDisplay}, 'RemovedFromDisplay message should be present' );
90
91
    # Verify item was removed from display
92
    $active_display = $item->active_display_item;
93
    is( $active_display, undef, 'Item should no longer have an active display item after return' );
94
};
95
96
subtest 'display_return_over - yes - except at home library' => sub {
97
    plan tests => 9;
98
99
    # Create test data
100
    my $library1 = $builder->build_object( { class => 'Koha::Libraries' } );
101
    my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
102
    my $patron =
103
        $builder->build_object( { class => 'Koha::Patrons', value => { branchcode => $library1->branchcode } } );
104
105
    # Mock userenv
106
    t::lib::Mocks::mock_userenv( { branchcode => $library1->branchcode } );
107
108
    # Test 1: Return at non-home library should remove from display
109
    my $item1 =
110
        $builder->build_sample_item( { homebranch => $library1->branchcode, holdingbranch => $library1->branchcode } );
111
112
    my $display1 = $builder->build_object(
113
        {
114
            class => 'Koha::Displays',
115
            value => {
116
                display_return_over => 'yes - except at home library',
117
                enabled             => 1,
118
                start_date          => undef,
119
                end_date            => undef,
120
            }
121
        }
122
    );
123
124
    my $display_item1 = $builder->build_object(
125
        {
126
            class => 'Koha::DisplayItems',
127
            value => {
128
                display_id   => $display1->display_id,
129
                itemnumber   => $item1->itemnumber,
130
                biblionumber => $item1->biblionumber,
131
            }
132
        }
133
    );
134
135
    my $active_display = $item1->active_display_item;
136
    ok( defined $active_display, 'Item should have an active display item' );
137
138
    AddIssue( $patron, $item1->barcode );
139
    my ( $doreturn, $messages, $issue, $borrower ) = AddReturn( $item1->barcode, $library2->branchcode );
140
141
    is( $doreturn, 1, 'Item should be returned successfully at non-home library' );
142
    ok(
143
        exists $messages->{RemovedFromDisplay},
144
        'RemovedFromDisplay message should be present when returned at non-home library'
145
    );
146
147
    $active_display = $item1->active_display_item;
148
    is( $active_display, undef, 'Item should be removed from display when returned at non-home library' );
149
150
    # Test 2: Return at home library should NOT remove from display
151
    my $item2 =
152
        $builder->build_sample_item( { homebranch => $library1->branchcode, holdingbranch => $library1->branchcode } );
153
154
    my $display2 = $builder->build_object(
155
        {
156
            class => 'Koha::Displays',
157
            value => {
158
                display_return_over => 'yes - except at home library',
159
                enabled             => 1,
160
                start_date          => undef,
161
                end_date            => undef,
162
            }
163
        }
164
    );
165
166
    my $display_item2 = $builder->build_object(
167
        {
168
            class => 'Koha::DisplayItems',
169
            value => {
170
                display_id   => $display2->display_id,
171
                itemnumber   => $item2->itemnumber,
172
                biblionumber => $item2->biblionumber,
173
            }
174
        }
175
    );
176
177
    $active_display = $item2->active_display_item;
178
    ok( defined $active_display, 'Item should have an active display item before return' );
179
180
    AddIssue( $patron, $item2->barcode );
181
    ( $doreturn, $messages, $issue, $borrower ) = AddReturn( $item2->barcode, $library1->branchcode );
182
183
    is( $doreturn, 1, 'Item should be returned successfully at home library' );
184
    ok(
185
        !exists $messages->{RemovedFromDisplay},
186
        'RemovedFromDisplay message should NOT be present when returned at home library'
187
    );
188
189
    $active_display = $item2->active_display_item;
190
    ok( defined $active_display, 'Item should still have an active display item when returned at home library' );
191
    is(
192
        $active_display->display_id, $display2->display_id,
193
        'Display item should still be linked to the correct display'
194
    );
195
196
    # Clean up
197
    $display_item2->delete;
198
};
199
200
$schema->storage->txn_rollback;
(-)a/t/db_dependent/Koha/DisplayItems.t (+262 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2025-2026 Open Fifth Ltd
4
#
5
# This file is part of Koha
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <https://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use Test::NoWarnings;
23
use Test::More tests => 13;
24
25
use C4::Context;
26
use Koha::DateUtils qw( dt_from_string );
27
use Koha::DisplayItem;
28
use Koha::DisplayItems;
29
use Koha::Database;
30
31
use t::lib::TestBuilder;
32
33
my $schema = Koha::Database->new->schema;
34
$schema->storage->txn_begin;
35
36
my $builder             = t::lib::TestBuilder->new;
37
my $nb_of_display_items = Koha::DisplayItems->search->count;
38
39
# Create test data
40
my $library = $builder->build( { source => 'Branch' } );
41
my $biblio  = $builder->build( { source => 'Biblio' } );
42
my $item1   = $builder->build( { source => 'Item', value => { biblionumber => $biblio->{biblionumber} } } );
43
my $item2   = $builder->build( { source => 'Item', value => { biblionumber => $biblio->{biblionumber} } } );
44
45
my $display = $builder->build( { source => 'Display', value => { display_branch => $library->{branchcode} } } );
46
47
# Create display items for testing
48
my $new_display_item_1 = Koha::DisplayItem->new(
49
    {
50
        display_id   => $display->{display_id},
51
        itemnumber   => $item1->{itemnumber},
52
        biblionumber => $biblio->{biblionumber},
53
        date_added   => '2024-01-01',
54
        date_remove  => '2024-12-31',
55
    }
56
)->store;
57
58
my $new_display_item_2 = Koha::DisplayItem->new(
59
    {
60
        display_id   => $display->{display_id},
61
        itemnumber   => $item2->{itemnumber},
62
        biblionumber => $biblio->{biblionumber},
63
        date_added   => '2024-02-01',
64
        date_remove  => '2024-11-30',
65
    }
66
)->store;
67
68
# Test basic CRUD operations
69
like( $new_display_item_1->display_id, qr|^\d+$|, 'Adding a new display item should have set the display_id' );
70
is( Koha::DisplayItems->search->count, $nb_of_display_items + 2, 'The 2 display items should have been added' );
71
72
my $retrieved_display_item_1 = Koha::DisplayItems->find(
73
    {
74
        display_id => $new_display_item_1->display_id,
75
        itemnumber => $new_display_item_1->itemnumber
76
    }
77
);
78
is(
79
    $retrieved_display_item_1->biblionumber, $new_display_item_1->biblionumber,
80
    'Find a display item by composite key should return the correct display item'
81
);
82
83
# Test Koha::DisplayItem methods
84
subtest 'display method' => sub {
85
    plan tests => 2;
86
87
    my $display_obj = $new_display_item_1->display;
88
    isa_ok( $display_obj, 'Koha::Display', 'display should return a Koha::Display object' );
89
    is( $display_obj->display_id, $display->{display_id}, 'display should return the correct display' );
90
};
91
92
subtest 'item method' => sub {
93
    plan tests => 2;
94
95
    my $item_obj = $new_display_item_1->item;
96
    isa_ok( $item_obj, 'Koha::Item', 'item should return a Koha::Item object' );
97
    is( $item_obj->itemnumber, $item1->{itemnumber}, 'item should return the correct item' );
98
};
99
100
subtest 'biblio method' => sub {
101
    plan tests => 2;
102
103
    my $biblio_obj = $new_display_item_1->biblio;
104
    isa_ok( $biblio_obj, 'Koha::Biblio', 'biblio should return a Koha::Biblio object' );
105
    is( $biblio_obj->biblionumber, $biblio->{biblionumber}, 'biblio should return the correct biblio' );
106
};
107
108
# Test Koha::DisplayItems methods
109
subtest 'for_display method' => sub {
110
    plan tests => 2;
111
112
    my $display_items = Koha::DisplayItems->for_display( $display->{display_id} );
113
    isa_ok( $display_items, 'Koha::DisplayItems', 'for_display should return a Koha::DisplayItems object' );
114
    is( $display_items->count, 2, 'for_display should return the correct number of display items' );
115
};
116
117
subtest 'for_item method' => sub {
118
    plan tests => 2;
119
120
    my $display_items = Koha::DisplayItems->for_item( $item1->{itemnumber} );
121
    isa_ok( $display_items, 'Koha::DisplayItems', 'for_item should return a Koha::DisplayItems object' );
122
    is( $display_items->count, 1, 'for_item should return the correct number of display items' );
123
};
124
125
subtest 'for_biblio method' => sub {
126
    plan tests => 2;
127
128
    my $display_items = Koha::DisplayItems->for_biblio( $biblio->{biblionumber} );
129
    isa_ok( $display_items, 'Koha::DisplayItems', 'for_biblio should return a Koha::DisplayItems object' );
130
    is( $display_items->count, 2, 'for_biblio should return the correct number of display items' );
131
};
132
133
subtest 'due_for_removal method' => sub {
134
    plan tests => 3;
135
136
    # Create a display item that should be removed today
137
    my $today          = dt_from_string()->ymd;
138
    my $item_to_remove = $builder->build( { source => 'Item', value => { biblionumber => $biblio->{biblionumber} } } );
139
140
    my $display_item_due = Koha::DisplayItem->new(
141
        {
142
            display_id   => $display->{display_id},
143
            itemnumber   => $item_to_remove->{itemnumber},
144
            biblionumber => $biblio->{biblionumber},
145
            date_added   => '2024-01-01',
146
            date_remove  => $today,
147
        }
148
    )->store;
149
150
    my $due_items = Koha::DisplayItems->due_for_removal;
151
    isa_ok( $due_items, 'Koha::DisplayItems', 'due_for_removal should return a Koha::DisplayItems object' );
152
153
    my $found_due_item = 0;
154
    while ( my $item = $due_items->next ) {
155
        $found_due_item = 1 if $item->itemnumber == $item_to_remove->{itemnumber};
156
    }
157
    ok( $found_due_item, 'due_for_removal should include items due for removal today' );
158
159
    # Create a display item that should be removed in the future
160
    my $future_date = dt_from_string()->add( days => 30 )->ymd;
161
    my $item_future = $builder->build( { source => 'Item', value => { biblionumber => $biblio->{biblionumber} } } );
162
163
    my $display_item_future = Koha::DisplayItem->new(
164
        {
165
            display_id   => $display->{display_id},
166
            itemnumber   => $item_future->{itemnumber},
167
            biblionumber => $biblio->{biblionumber},
168
            date_added   => '2024-01-01',
169
            date_remove  => $future_date,
170
        }
171
    )->store;
172
173
    my $due_items_2       = Koha::DisplayItems->due_for_removal;
174
    my $found_future_item = 0;
175
    while ( my $item = $due_items_2->next ) {
176
        $found_future_item = 1 if $item->itemnumber == $item_future->{itemnumber};
177
    }
178
    ok( !$found_future_item, 'due_for_removal should not include items due for removal in the future' );
179
180
    # Clean up the additional display items
181
    $display_item_due->delete;
182
    $display_item_future->delete;
183
};
184
185
subtest 'display_days automatic date_remove calculation' => sub {
186
    plan tests => 5;
187
188
    # Create a display with display_days set to 30
189
    my $display_with_days = $builder->build_object(
190
        {
191
            class => 'Koha::Displays',
192
            value => { display_days => 30 }
193
        }
194
    );
195
196
    my $item3 = $builder->build( { source => 'Item', value => { biblionumber => $biblio->{biblionumber} } } );
197
198
    # Create display item without date_remove - should auto-calculate
199
    my $display_item_auto = Koha::DisplayItem->new(
200
        {
201
            display_id   => $display_with_days->display_id,
202
            itemnumber   => $item3->{itemnumber},
203
            biblionumber => $biblio->{biblionumber},
204
        }
205
    )->store;
206
207
    ok( defined $display_item_auto->date_remove, 'date_remove should be set automatically' );
208
209
    # Calculate expected date (today + 30 days)
210
    my $dt = dt_from_string();
211
    $dt->add( days => 30 );
212
    my $expected_date = $dt->ymd;
213
    is( $display_item_auto->date_remove, $expected_date, 'date_remove should be today + display_days' );
214
215
    # Create display item with explicit date_remove - should NOT auto-calculate
216
    my $item4       = $builder->build( { source => 'Item', value => { biblionumber => $biblio->{biblionumber} } } );
217
    my $manual_date = '2025-12-31';
218
    my $display_item_manual = Koha::DisplayItem->new(
219
        {
220
            display_id   => $display_with_days->display_id,
221
            itemnumber   => $item4->{itemnumber},
222
            biblionumber => $biblio->{biblionumber},
223
            date_remove  => $manual_date,
224
        }
225
    )->store;
226
227
    is( $display_item_manual->date_remove, $manual_date, 'Explicit date_remove should not be overridden' );
228
229
    # Create display without display_days - date_remove should be NULL
230
    my $display_no_days = $builder->build_object(
231
        {
232
            class => 'Koha::Displays',
233
            value => { display_days => undef }
234
        }
235
    );
236
    my $item5 = $builder->build( { source => 'Item', value => { biblionumber => $biblio->{biblionumber} } } );
237
238
    my $display_item_no_days = Koha::DisplayItem->new(
239
        {
240
            display_id   => $display_no_days->display_id,
241
            itemnumber   => $item5->{itemnumber},
242
            biblionumber => $biblio->{biblionumber},
243
        }
244
    )->store;
245
246
    is( $display_item_no_days->date_remove, undef, 'date_remove should be NULL when display has no display_days' );
247
248
    # Clean up
249
    $display_item_auto->delete;
250
    $display_item_manual->delete;
251
    $display_item_no_days->delete;
252
    $display_with_days->delete;
253
    $display_no_days->delete;
254
255
    pass('Cleanup successful');
256
};
257
258
# Test delete
259
$retrieved_display_item_1->delete;
260
is( Koha::DisplayItems->search->count, $nb_of_display_items + 1, 'Delete should have deleted the display item' );
261
262
$schema->storage->txn_rollback;
(-)a/t/db_dependent/Koha/Displays.t (+186 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2025-2026 Open Fifth Ltd
4
#
5
# This file is part of Koha
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <https://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use Test::NoWarnings;
23
use Test::More tests => 11;
24
25
use DateTime;
26
use Koha::Display;
27
use Koha::Displays;
28
use Koha::Database;
29
30
use t::lib::TestBuilder;
31
32
my $schema = Koha::Database->new->schema;
33
$schema->storage->txn_begin;
34
35
my $builder        = t::lib::TestBuilder->new;
36
my $nb_of_displays = Koha::Displays->search->count;
37
38
# Create a library for testing display_branch
39
my $library = $builder->build( { source => 'Branch' } );
40
41
# Create item types for testing
42
my $itemtype1 = $builder->build( { source => 'Itemtype' } );
43
my $itemtype2 = $builder->build( { source => 'Itemtype' } );
44
45
# Get current date for testing
46
my $today     = DateTime->today;
47
my $yesterday = $today->clone->subtract( days => 1 );
48
my $tomorrow  = $today->clone->add( days => 1 );
49
50
# Create displays for testing
51
my $new_display_1 = Koha::Display->new(
52
    {
53
        display_name           => 'my_display_name_for_test_1',
54
        enabled                => 1,
55
        display_return_over    => 'no',
56
        start_date             => $yesterday->ymd,
57
        end_date               => $tomorrow->ymd,
58
        display_location       => 'DISPLAY_LOC_1',
59
        display_code           => 'DISPLAY_CODE_1',
60
        display_holding_branch => $library->{branchcode},
61
        display_branch         => $library->{branchcode},
62
        display_itype          => $itemtype1->{itemtype},
63
    }
64
)->store;
65
66
my $new_display_2 = Koha::Display->new(
67
    {
68
        display_name        => 'my_display_name_for_test_2',
69
        enabled             => 0,
70
        display_return_over => 'yes - any library',
71
        display_location    => 'DISPLAY_LOC_2',
72
        display_code        => 'DISPLAY_CODE_2',
73
        display_itype       => $itemtype2->{itemtype},
74
    }
75
)->store;
76
77
# Test basic CRUD operations
78
like( $new_display_1->display_id, qr|^\d+$|, 'Adding a new display should have set the display_id' );
79
is( Koha::Displays->search->count, $nb_of_displays + 2, 'The 2 displays should have been added' );
80
81
my $retrieved_display_1 = Koha::Displays->find( $new_display_1->display_id );
82
is(
83
    $retrieved_display_1->display_name, $new_display_1->display_name,
84
    'Find a display by id should return the correct display'
85
);
86
87
# Test Koha::Display methods
88
subtest 'display_items method' => sub {
89
    plan tests => 2;
90
91
    my $display_items = $new_display_1->display_items;
92
    isa_ok( $display_items, 'Koha::DisplayItems', 'display_items should return a Koha::DisplayItems object' );
93
    is( $display_items->count, 0, 'New display should have no display items' );
94
};
95
96
subtest 'holding_library method' => sub {
97
    plan tests => 3;
98
99
    my $holding_library_obj = $new_display_1->holding_library;
100
    isa_ok( $holding_library_obj, 'Koha::Library', 'holding_library should return a Koha::Library object' );
101
    is( $holding_library_obj->branchcode, $library->{branchcode}, 'holding_library should return the correct library' );
102
103
    # Test display without holding_library
104
    is( $new_display_2->holding_library, undef, 'display without holding_library should return undef' );
105
};
106
107
subtest 'home_library method' => sub {
108
    plan tests => 3;
109
110
    my $home_library_obj = $new_display_1->home_library;
111
    isa_ok( $home_library_obj, 'Koha::Library', 'home_library should return a Koha::Library object' );
112
    is( $home_library_obj->branchcode, $library->{branchcode}, 'home_library should return the correct library' );
113
114
    # Test display without home_library
115
    is( $new_display_2->home_library, undef, 'display without home_library should return undef' );
116
};
117
118
# Test Koha::Displays methods
119
subtest 'enabled method' => sub {
120
    plan tests => 2;
121
122
    my $enabled_displays = Koha::Displays->enabled;
123
    isa_ok( $enabled_displays, 'Koha::Displays', 'enabled should return a Koha::Displays object' );
124
125
    my $found_enabled = 0;
126
    while ( my $display = $enabled_displays->next ) {
127
        $found_enabled = 1 if $display->display_id == $new_display_1->display_id;
128
    }
129
    ok( $found_enabled, 'enabled displays should include the enabled display' );
130
};
131
132
subtest 'for_branch method' => sub {
133
    plan tests => 2;
134
135
    my $branch_displays = Koha::Displays->for_branch( $library->{branchcode} );
136
    isa_ok( $branch_displays, 'Koha::Displays', 'for_branch should return a Koha::Displays object' );
137
138
    my $found_branch_display = 0;
139
    while ( my $display = $branch_displays->next ) {
140
        $found_branch_display = 1 if $display->display_id == $new_display_1->display_id;
141
    }
142
    ok( $found_branch_display, 'for_branch should include displays for the specified branch' );
143
};
144
145
subtest 'active method' => sub {
146
    plan tests => 3;
147
148
    my $active_displays = Koha::Displays->active;
149
    isa_ok( $active_displays, 'Koha::Displays', 'active should return a Koha::Displays object' );
150
151
    my $found_active = 0;
152
    while ( my $display = $active_displays->next ) {
153
        $found_active = 1 if $display->display_id == $new_display_1->display_id;
154
    }
155
    ok( $found_active, 'active displays should include the active display within date range' );
156
157
    # Create a display that's not active (dates in the past)
158
    my $past_end   = $today->clone->subtract( days => 10 );
159
    my $past_start = $today->clone->subtract( days => 20 );
160
161
    my $inactive_display = Koha::Display->new(
162
        {
163
            display_name        => 'inactive_display',
164
            enabled             => 1,
165
            display_return_over => 'no',
166
            start_date          => $past_start->ymd,
167
            end_date            => $past_end->ymd,
168
        }
169
    )->store;
170
171
    my $active_displays_2 = Koha::Displays->active;
172
    my $found_inactive    = 0;
173
    while ( my $display = $active_displays_2->next ) {
174
        $found_inactive = 1 if $display->display_id == $inactive_display->display_id;
175
    }
176
    ok( !$found_inactive, 'active displays should not include displays outside date range' );
177
178
    # Clean up the inactive display
179
    $inactive_display->delete;
180
};
181
182
# Test delete
183
$retrieved_display_1->delete;
184
is( Koha::Displays->search->count, $nb_of_displays + 1, 'Delete should have deleted the display' );
185
186
$schema->storage->txn_rollback;
(-)a/t/db_dependent/Search.t (+2 lines)
Lines 136-141 $contextmodule->mock( Link Here
136
            return '1';
136
            return '1';
137
        } elsif ( $pref eq 'UseControlNumber' ) {
137
        } elsif ( $pref eq 'UseControlNumber' ) {
138
            return '0';
138
            return '0';
139
        } elsif ( $pref eq 'UseDisplayModule' ) {
140
            return '0';
139
        } elsif ( $pref eq 'UseICUStyleQuotes' ) {
141
        } elsif ( $pref eq 'UseICUStyleQuotes' ) {
140
            return '0';
142
            return '0';
141
        } elsif ( $pref eq 'viewISBD' ) {
143
        } elsif ( $pref eq 'viewISBD' ) {
(-)a/t/db_dependent/api/v1/displayitems.t (+492 lines)
Line 0 Link Here
1
#!/usr/bin/env perl
2
3
# Copyright 2025-2026 Open Fifth Ltd
4
#
5
# This file is part of Koha
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <https://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use Test::NoWarnings;
23
use Test::More tests => 6;
24
use Test::Mojo;
25
use JSON;
26
27
use t::lib::TestBuilder;
28
use t::lib::Mocks;
29
30
use Koha::DisplayItems;
31
use Koha::Database;
32
33
my $schema  = Koha::Database->new->schema;
34
my $builder = t::lib::TestBuilder->new;
35
36
my $t = Test::Mojo->new('Koha::REST::V1');
37
t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 );
38
39
subtest 'list() tests' => sub {
40
41
    plan tests => 20;
42
43
    $schema->storage->txn_begin;
44
45
    Koha::DisplayItems->search->delete;
46
47
    my $librarian = $builder->build_object(
48
        {
49
            class => 'Koha::Patrons',
50
            value => { flags => 1 }     # displays permission
51
        }
52
    );
53
    my $password = 'thePassword123';
54
    $librarian->set_password( { password => $password, skip_validation => 1 } );
55
    my $userid = $librarian->userid;
56
57
    my $patron = $builder->build_object(
58
        {
59
            class => 'Koha::Patrons',
60
            value => { flags => 0 }
61
        }
62
    );
63
64
    $patron->set_password( { password => $password, skip_validation => 1 } );
65
    my $unauth_userid = $patron->userid;
66
67
    ## Authorized user tests
68
    # No displayitems, so empty array should be returned
69
    $t->get_ok("//$userid:$password@/api/v1/display/items")->status_is(200)->json_is( [] );
70
71
    # Create test data
72
    my $display = $builder->build_object( { class => 'Koha::Displays' } );
73
    my $biblio  = $builder->build_sample_biblio();
74
    my $item    = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } );
75
76
    my $displayitem = $builder->build_object(
77
        {
78
            class => 'Koha::DisplayItems',
79
            value => {
80
                display_id   => $display->display_id,
81
                itemnumber   => $item->itemnumber,
82
                biblionumber => $biblio->biblionumber
83
            }
84
        }
85
    );
86
87
    # One displayitem created, should get returned
88
    $t->get_ok("//$userid:$password@/api/v1/display/items")->status_is(200)->json_is( [ $displayitem->to_api ] );
89
90
    # Create another displayitem with same display
91
    my $item2               = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } );
92
    my $another_displayitem = $builder->build_object(
93
        {
94
            class => 'Koha::DisplayItems',
95
            value => {
96
                display_id   => $display->display_id,
97
                itemnumber   => $item2->itemnumber,
98
                biblionumber => $biblio->biblionumber
99
            }
100
        }
101
    );
102
103
    # Create displayitem with different display
104
    my $display2                      = $builder->build_object( { class => 'Koha::Displays' } );
105
    my $item3                         = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } );
106
    my $displayitem_different_display = $builder->build_object(
107
        {
108
            class => 'Koha::DisplayItems',
109
            value => {
110
                display_id   => $display2->display_id,
111
                itemnumber   => $item3->itemnumber,
112
                biblionumber => $biblio->biblionumber
113
            }
114
        }
115
    );
116
117
    # Three displayitems created, they should all be returned
118
    $t->get_ok("//$userid:$password@/api/v1/display/items")->status_is(200)->json_is(
119
        [
120
            $displayitem->to_api,
121
            $another_displayitem->to_api,
122
            $displayitem_different_display->to_api
123
        ]
124
    );
125
126
    # Filtering works, two displayitems sharing display_id
127
    $t->get_ok( "//$userid:$password@/api/v1/display/items?display_id=" . $display->display_id )
128
        ->status_is(200)
129
        ->json_is(
130
        [
131
            $displayitem->to_api,
132
            $another_displayitem->to_api
133
        ]
134
        );
135
136
    $t->get_ok( "//$userid:$password@/api/v1/display/items?itemnumber=" . $item->itemnumber )
137
        ->status_is(200)
138
        ->json_is( [ $displayitem->to_api ] );
139
140
    # Warn on unsupported query parameter
141
    $t->get_ok("//$userid:$password@/api/v1/display/items?displayitem_blah=blah")
142
        ->status_is(400)
143
        ->json_is( [ { path => '/query/displayitem_blah', message => 'Malformed query string' } ] );
144
145
    # Unauthorized access
146
    $t->get_ok("//$unauth_userid:$password@/api/v1/display/items")->status_is(403);
147
148
    $schema->storage->txn_rollback;
149
};
150
151
subtest 'get() tests' => sub {
152
153
    plan tests => 11;
154
155
    $schema->storage->txn_begin;
156
157
    # Create test data
158
    my $display = $builder->build_object( { class => 'Koha::Displays' } );
159
    my $biblio  = $builder->build_sample_biblio();
160
    my $item    = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } );
161
162
    my $displayitem = $builder->build_object(
163
        {
164
            class => 'Koha::DisplayItems',
165
            value => {
166
                display_id   => $display->display_id,
167
                itemnumber   => $item->itemnumber,
168
                biblionumber => $biblio->biblionumber
169
            }
170
        }
171
    );
172
173
    my $librarian = $builder->build_object(
174
        {
175
            class => 'Koha::Patrons',
176
            value => { flags => 1 }     # displays permission
177
        }
178
    );
179
    my $password = 'thePassword123';
180
    $librarian->set_password( { password => $password, skip_validation => 1 } );
181
    my $userid = $librarian->userid;
182
183
    my $patron = $builder->build_object(
184
        {
185
            class => 'Koha::Patrons',
186
            value => { flags => 0 }
187
        }
188
    );
189
190
    $patron->set_password( { password => $password, skip_validation => 1 } );
191
    my $unauth_userid = $patron->userid;
192
193
    $t->get_ok(
194
        "//$userid:$password@/api/v1/display/items/" . $displayitem->display_id . "/" . $displayitem->itemnumber )
195
        ->status_is(200)
196
        ->json_is( $displayitem->to_api );
197
198
    $t->get_ok( "//$unauth_userid:$password@/api/v1/display/items/"
199
            . $displayitem->display_id . "/"
200
            . $displayitem->itemnumber )->status_is(403);
201
202
    # Create displayitem to delete
203
    my $item_to_delete        = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } );
204
    my $displayitem_to_delete = $builder->build_object(
205
        {
206
            class => 'Koha::DisplayItems',
207
            value => {
208
                display_id   => $display->display_id,
209
                itemnumber   => $item_to_delete->itemnumber,
210
                biblionumber => $biblio->biblionumber
211
            }
212
        }
213
    );
214
    my $non_existent_display_id = $displayitem_to_delete->display_id;
215
    my $non_existent_item_id    = $displayitem_to_delete->itemnumber;
216
    $displayitem_to_delete->delete;
217
218
    $t->get_ok("//$userid:$password@/api/v1/display/items/$non_existent_display_id/$non_existent_item_id")
219
        ->status_is(404)
220
        ->json_is( '/error' => 'Display item not found' );
221
222
    # Test with completely non-existent IDs
223
    $t->get_ok("//$userid:$password@/api/v1/display/items/99999/99999")
224
        ->status_is(404)
225
        ->json_is( '/error' => 'Display item not found' );
226
227
    $schema->storage->txn_rollback;
228
};
229
230
subtest 'add() tests' => sub {
231
232
    plan tests => 17;
233
234
    $schema->storage->txn_begin;
235
236
    my $librarian = $builder->build_object(
237
        {
238
            class => 'Koha::Patrons',
239
            value => { flags => 1 }     # displays permission
240
        }
241
    );
242
    my $password = 'thePassword123';
243
    $librarian->set_password( { password => $password, skip_validation => 1 } );
244
    my $userid = $librarian->userid;
245
246
    my $patron = $builder->build_object(
247
        {
248
            class => 'Koha::Patrons',
249
            value => { flags => 0 }
250
        }
251
    );
252
253
    $patron->set_password( { password => $password, skip_validation => 1 } );
254
    my $unauth_userid = $patron->userid;
255
256
    # Create test data
257
    my $display = $builder->build_object( { class => 'Koha::Displays' } );
258
    my $biblio  = $builder->build_sample_biblio();
259
    my $item    = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } );
260
261
    my $displayitem = {
262
        display_id   => $display->display_id,
263
        itemnumber   => $item->itemnumber,
264
        biblionumber => $biblio->biblionumber,
265
        date_added   => "2024-01-15",
266
        date_remove  => "2024-02-15"
267
    };
268
269
    # Unauthorized attempt to write
270
    $t->post_ok( "//$unauth_userid:$password@/api/v1/display/items" => json => $displayitem )->status_is(403);
271
272
    # Authorized attempt to write invalid data
273
    my $displayitem_with_invalid_field = {
274
        blah         => "DisplayItem Blah",
275
        display_id   => $display->display_id,
276
        itemnumber   => $item->itemnumber,
277
        biblionumber => $biblio->biblionumber
278
    };
279
280
    $t->post_ok( "//$userid:$password@/api/v1/display/items" => json => $displayitem_with_invalid_field )
281
        ->status_is(400)
282
        ->json_is(
283
        "/errors" => [
284
            {
285
                message => "Properties not allowed: blah.",
286
                path    => "/body"
287
            }
288
        ]
289
        );
290
291
    # Authorized attempt to write
292
    $t->post_ok( "//$userid:$password@/api/v1/display/items" => json => $displayitem )
293
        ->status_is( 201, 'REST3.2.1' )
294
        ->header_like(
295
        Location => qr|^\/api\/v1\/display/items/\d+/\d+|,
296
        'REST3.4.1'
297
        )
298
        ->json_is( '/display_id'   => $displayitem->{display_id} )
299
        ->json_is( '/itemnumber'   => $displayitem->{itemnumber} )
300
        ->json_is( '/biblionumber' => $displayitem->{biblionumber} )
301
        ->json_is( '/date_added'   => $displayitem->{date_added} );
302
303
    # Test missing required field (display_id is required)
304
    my $displayitem_missing_field = {
305
        itemnumber   => $item->itemnumber,
306
        biblionumber => $biblio->biblionumber
307
308
        # Missing display_id (required field)
309
    };
310
311
    $t->post_ok( "//$userid:$password@/api/v1/display/items" => json => $displayitem_missing_field )
312
        ->status_is(400)
313
        ->json_has('/errors');
314
315
    # Test successful creation with different item
316
    my $item2        = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } );
317
    my $displayitem2 = {
318
        display_id   => $display->display_id,
319
        itemnumber   => $item2->itemnumber,
320
        biblionumber => $biblio->biblionumber,
321
        date_added   => "2024-01-16",
322
        date_remove  => "2024-02-16"
323
    };
324
    $t->post_ok( "//$userid:$password@/api/v1/display/items" => json => $displayitem2 )->status_is(201);
325
326
    $schema->storage->txn_rollback;
327
};
328
329
subtest 'update() tests' => sub {
330
331
    plan tests => 15;
332
333
    $schema->storage->txn_begin;
334
335
    my $librarian = $builder->build_object(
336
        {
337
            class => 'Koha::Patrons',
338
            value => { flags => 1 }     # displays permission
339
        }
340
    );
341
    my $password = 'thePassword123';
342
    $librarian->set_password( { password => $password, skip_validation => 1 } );
343
    my $userid = $librarian->userid;
344
345
    my $patron = $builder->build_object(
346
        {
347
            class => 'Koha::Patrons',
348
            value => { flags => 0 }
349
        }
350
    );
351
352
    $patron->set_password( { password => $password, skip_validation => 1 } );
353
    my $unauth_userid = $patron->userid;
354
355
    # Create test data
356
    my $display = $builder->build_object( { class => 'Koha::Displays' } );
357
    my $biblio  = $builder->build_sample_biblio();
358
    my $item    = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } );
359
360
    my $displayitem = $builder->build_object(
361
        {
362
            class => 'Koha::DisplayItems',
363
            value => {
364
                display_id   => $display->display_id,
365
                itemnumber   => $item->itemnumber,
366
                biblionumber => $biblio->biblionumber
367
            }
368
        }
369
    );
370
371
    # Unauthorized attempt to update
372
    $t->put_ok( "//$unauth_userid:$password@/api/v1/display/items/"
373
            . $displayitem->display_id . "/"
374
            . $displayitem->itemnumber => json => { date_added => '2024-01-20' } )->status_is(403);
375
376
    # Attempt partial update on a PUT (missing required field)
377
    my $displayitem_with_missing_field = {
378
        biblionumber => $biblio->biblionumber
379
380
            # Missing display_id and itemnumber (required fields)
381
    };
382
383
    $t->put_ok( "//$userid:$password@/api/v1/display/items/"
384
            . $displayitem->display_id . "/"
385
            . $displayitem->itemnumber => json => $displayitem_with_missing_field )
386
        ->status_is(400)
387
        ->json_has("/errors");
388
389
    # Full object update on PUT
390
    my $displayitem_with_updated_field = {
391
        display_id   => $display->display_id,
392
        itemnumber   => $item->itemnumber,
393
        biblionumber => $biblio->biblionumber,
394
        date_added   => "2024-01-20",
395
        date_remove  => "2024-03-20"
396
    };
397
398
    $t->put_ok( "//$userid:$password@/api/v1/display/items/"
399
            . $displayitem->display_id . "/"
400
            . $displayitem->itemnumber => json => $displayitem_with_updated_field )
401
        ->status_is(200)
402
        ->json_is( '/date_added' => '2024-01-20' );
403
404
    # Authorized attempt to write invalid data
405
    my $displayitem_with_invalid_field = {
406
        blah         => "DisplayItem Blah",
407
        display_id   => $display->display_id,
408
        itemnumber   => $item->itemnumber,
409
        biblionumber => $biblio->biblionumber
410
    };
411
412
    $t->put_ok( "//$userid:$password@/api/v1/display/items/"
413
            . $displayitem->display_id . "/"
414
            . $displayitem->itemnumber => json => $displayitem_with_invalid_field )->status_is(400)->json_is(
415
        "/errors" => [
416
            {
417
                message => "Properties not allowed: blah.",
418
                path    => "/body"
419
            }
420
        ]
421
            );
422
423
    # Test with non-existent displayitem
424
    $t->put_ok( "//$userid:$password@/api/v1/display/items/99999/99999" => json => $displayitem_with_updated_field )
425
        ->status_is(404);
426
427
    # Wrong method (POST)
428
    $t->post_ok( "//$userid:$password@/api/v1/display/items/"
429
            . $displayitem->display_id . "/"
430
            . $displayitem->itemnumber => json => $displayitem_with_updated_field )->status_is(404);
431
432
    $schema->storage->txn_rollback;
433
};
434
435
subtest 'delete() tests' => sub {
436
437
    plan tests => 7;
438
439
    $schema->storage->txn_begin;
440
441
    my $librarian = $builder->build_object(
442
        {
443
            class => 'Koha::Patrons',
444
            value => { flags => 1 }     # displays permission
445
        }
446
    );
447
    my $password = 'thePassword123';
448
    $librarian->set_password( { password => $password, skip_validation => 1 } );
449
    my $userid = $librarian->userid;
450
451
    my $patron = $builder->build_object(
452
        {
453
            class => 'Koha::Patrons',
454
            value => { flags => 0 }
455
        }
456
    );
457
458
    $patron->set_password( { password => $password, skip_validation => 1 } );
459
    my $unauth_userid = $patron->userid;
460
461
    # Create test data
462
    my $display = $builder->build_object( { class => 'Koha::Displays' } );
463
    my $biblio  = $builder->build_sample_biblio();
464
    my $item    = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } );
465
466
    my $displayitem = $builder->build_object(
467
        {
468
            class => 'Koha::DisplayItems',
469
            value => {
470
                display_id   => $display->display_id,
471
                itemnumber   => $item->itemnumber,
472
                biblionumber => $biblio->biblionumber
473
            }
474
        }
475
    );
476
477
    # Unauthorized attempt to delete
478
    $t->delete_ok( "//$unauth_userid:$password@/api/v1/display/items/"
479
            . $displayitem->display_id . "/"
480
            . $displayitem->itemnumber )->status_is(403);
481
482
    $t->delete_ok(
483
        "//$userid:$password@/api/v1/display/items/" . $displayitem->display_id . "/" . $displayitem->itemnumber )
484
        ->status_is( 204, 'REST3.2.4' )
485
        ->content_is( '', 'REST3.3.4' );
486
487
    $t->delete_ok(
488
        "//$userid:$password@/api/v1/display/items/" . $displayitem->display_id . "/" . $displayitem->itemnumber )
489
        ->status_is(404);
490
491
    $schema->storage->txn_rollback;
492
};
(-)a/t/db_dependent/api/v1/displays.t (+427 lines)
Line 0 Link Here
1
#!/usr/bin/env perl
2
3
# Copyright 2025-2026 Open Fifth Ltd
4
#
5
# This file is part of Koha
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <https://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use Test::NoWarnings;
23
use Test::More tests => 7;
24
use Test::Mojo;
25
use JSON;
26
27
use t::lib::TestBuilder;
28
use t::lib::Mocks;
29
30
use Koha::Displays;
31
use Koha::Database;
32
33
my $schema  = Koha::Database->new->schema;
34
my $builder = t::lib::TestBuilder->new;
35
36
my $t = Test::Mojo->new('Koha::REST::V1');
37
t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 );
38
39
subtest 'config() tests' => sub {
40
41
    plan tests => 5;
42
43
    $schema->storage->txn_begin;
44
45
    my $librarian = $builder->build_object(
46
        {
47
            class => 'Koha::Patrons',
48
            value => { flags => 1 }     # displays permission
49
        }
50
    );
51
    my $password = 'thePassword123';
52
    $librarian->set_password( { password => $password, skip_validation => 1 } );
53
    my $userid = $librarian->userid;
54
55
    my $patron = $builder->build_object(
56
        {
57
            class => 'Koha::Patrons',
58
            value => { flags => 0 }
59
        }
60
    );
61
62
    $patron->set_password( { password => $password, skip_validation => 1 } );
63
    my $unauth_userid = $patron->userid;
64
65
    t::lib::Mocks::mock_preference( 'UseDisplayModule', 1 );
66
67
    ## Authorized user tests
68
    # No displays, so empty array should be returned
69
    $t->get_ok("//$userid:$password@/api/v1/displays/config")
70
        ->status_is(200)
71
        ->json_is( { settings => { enabled => 1 } } );
72
73
    # Unauthorized access
74
    $t->get_ok("//$unauth_userid:$password@/api/v1/displays/config")->status_is(403);
75
76
    $schema->storage->txn_rollback;
77
};
78
79
subtest 'list() tests' => sub {
80
81
    plan tests => 20;
82
83
    $schema->storage->txn_begin;
84
85
    Koha::Displays->search->delete;
86
87
    my $librarian = $builder->build_object(
88
        {
89
            class => 'Koha::Patrons',
90
            value => { flags => 1 }     # displays permission
91
        }
92
    );
93
    my $password = 'thePassword123';
94
    $librarian->set_password( { password => $password, skip_validation => 1 } );
95
    my $userid = $librarian->userid;
96
97
    my $patron = $builder->build_object(
98
        {
99
            class => 'Koha::Patrons',
100
            value => { flags => 0 }
101
        }
102
    );
103
104
    $patron->set_password( { password => $password, skip_validation => 1 } );
105
    my $unauth_userid = $patron->userid;
106
107
    ## Authorized user tests
108
    # No displays, so empty array should be returned
109
    $t->get_ok("//$userid:$password@/api/v1/displays")->status_is(200)->json_is( [] );
110
111
    my $display = $builder->build_object( { class => 'Koha::Displays' } );
112
113
    # One display created, should get returned
114
    $t->get_ok("//$userid:$password@/api/v1/displays")->status_is(200)->json_is( [ $display->to_api ] );
115
116
    my $another_display =
117
        $builder->build_object(
118
        { class => 'Koha::Displays', value => { display_branch => $display->display_branch } } );
119
    my $display_with_another_branch = $builder->build_object( { class => 'Koha::Displays' } );
120
121
    # Three displays created, they should all be returned
122
    $t->get_ok("//$userid:$password@/api/v1/displays")->status_is(200)->json_is(
123
        [
124
            $display->to_api,
125
            $another_display->to_api,
126
            $display_with_another_branch->to_api
127
        ]
128
    );
129
130
    # Filtering works, two displays sharing display_branch
131
    $t->get_ok( "//$userid:$password@/api/v1/displays?display_branch=" . $display->display_branch )
132
        ->status_is(200)
133
        ->json_is(
134
        [
135
            $display->to_api,
136
            $another_display->to_api
137
        ]
138
        );
139
140
    $t->get_ok( "//$userid:$password@/api/v1/displays?display_name=" . $display->display_name )
141
        ->status_is(200)
142
        ->json_is( [ $display->to_api ] );
143
144
    # Warn on unsupported query parameter
145
    $t->get_ok("//$userid:$password@/api/v1/displays?display_blah=blah")
146
        ->status_is(400)
147
        ->json_is( [ { path => '/query/display_blah', message => 'Malformed query string' } ] );
148
149
    # Unauthorized access
150
    $t->get_ok("//$unauth_userid:$password@/api/v1/displays")->status_is(403);
151
152
    $schema->storage->txn_rollback;
153
};
154
155
subtest 'get() tests' => sub {
156
157
    plan tests => 8;
158
159
    $schema->storage->txn_begin;
160
161
    my $display   = $builder->build_object( { class => 'Koha::Displays' } );
162
    my $librarian = $builder->build_object(
163
        {
164
            class => 'Koha::Patrons',
165
            value => { flags => 1 }     # displays permission
166
        }
167
    );
168
    my $password = 'thePassword123';
169
    $librarian->set_password( { password => $password, skip_validation => 1 } );
170
    my $userid = $librarian->userid;
171
172
    my $patron = $builder->build_object(
173
        {
174
            class => 'Koha::Patrons',
175
            value => { flags => 0 }
176
        }
177
    );
178
179
    $patron->set_password( { password => $password, skip_validation => 1 } );
180
    my $unauth_userid = $patron->userid;
181
182
    $t->get_ok( "//$userid:$password@/api/v1/displays/" . $display->display_id )
183
        ->status_is(200)
184
        ->json_is( $display->to_api );
185
186
    $t->get_ok( "//$unauth_userid:$password@/api/v1/displays/" . $display->display_id )->status_is(403);
187
188
    my $display_to_delete = $builder->build_object( { class => 'Koha::Displays' } );
189
    my $non_existent_id   = $display_to_delete->display_id;
190
    $display_to_delete->delete;
191
192
    $t->get_ok("//$userid:$password@/api/v1/displays/$non_existent_id")
193
        ->status_is(404)
194
        ->json_is( '/error' => 'Display not found' );
195
196
    $schema->storage->txn_rollback;
197
};
198
199
subtest 'add() tests' => sub {
200
201
    plan tests => 18;
202
203
    $schema->storage->txn_begin;
204
205
    my $librarian = $builder->build_object(
206
        {
207
            class => 'Koha::Patrons',
208
            value => { flags => 1 }     # displays permission
209
        }
210
    );
211
    my $password = 'thePassword123';
212
    $librarian->set_password( { password => $password, skip_validation => 1 } );
213
    my $userid = $librarian->userid;
214
215
    my $patron = $builder->build_object(
216
        {
217
            class => 'Koha::Patrons',
218
            value => { flags => 0 }
219
        }
220
    );
221
222
    $patron->set_password( { password => $password, skip_validation => 1 } );
223
    my $unauth_userid = $patron->userid;
224
225
    my $itemtype = $builder->build_object( { class => 'Koha::ItemTypes' } );
226
227
    my $display = {
228
        display_name        => "Test Display",
229
        enabled             => JSON::true,
230
        display_return_over => "no",
231
        start_date          => "2024-01-01",
232
        end_date            => "2024-12-31",
233
        display_location    => "DISPLAY",
234
        display_code        => "TEST",
235
        display_branch      => "CPL",
236
        display_itype       => $itemtype->itemtype
237
    };
238
239
    # Unauthorized attempt to write
240
    $t->post_ok( "//$unauth_userid:$password@/api/v1/displays" => json => $display )->status_is(403);
241
242
    # Authorized attempt to write invalid data
243
    my $display_with_invalid_field = {
244
        blah                => "Display Blah",
245
        display_name        => "Test Display",
246
        enabled             => JSON::true,
247
        display_return_over => "no"
248
    };
249
250
    $t->post_ok( "//$userid:$password@/api/v1/displays" => json => $display_with_invalid_field )
251
        ->status_is(400)
252
        ->json_is(
253
        "/errors" => [
254
            {
255
                message => "Properties not allowed: blah.",
256
                path    => "/body"
257
            }
258
        ]
259
        );
260
261
    # Authorized attempt to write
262
    my $display_id =
263
        $t->post_ok( "//$userid:$password@/api/v1/displays" => json => $display )
264
        ->status_is( 201, 'REST3.2.1' )
265
        ->header_like(
266
        Location => qr|^\/api\/v1\/displays/\d*|,
267
        'REST3.4.1'
268
        )
269
        ->json_is( '/display_name'        => $display->{display_name} )
270
        ->json_is( '/enabled'             => $display->{enabled} )
271
        ->json_is( '/display_return_over' => $display->{display_return_over} )
272
        ->json_is( '/start_date'          => $display->{start_date} )
273
        ->tx->res->json->{display_id};
274
275
    # Authorized attempt to create with null id
276
    $display->{display_id} = undef;
277
    $t->post_ok( "//$userid:$password@/api/v1/displays" => json => $display )->status_is(400)->json_has('/errors');
278
279
    # Authorized attempt to create with existing id
280
    $display->{display_id} = $display_id;
281
    $t->post_ok( "//$userid:$password@/api/v1/displays" => json => $display )->status_is(400)->json_is(
282
        "/errors" => [
283
            {
284
                message => "Read-only.",
285
                path    => "/body/display_id"
286
            }
287
        ]
288
    );
289
290
    $schema->storage->txn_rollback;
291
};
292
293
subtest 'update() tests' => sub {
294
295
    plan tests => 15;
296
297
    $schema->storage->txn_begin;
298
299
    my $librarian = $builder->build_object(
300
        {
301
            class => 'Koha::Patrons',
302
            value => { flags => 1 }     # displays permission
303
        }
304
    );
305
    my $password = 'thePassword123';
306
    $librarian->set_password( { password => $password, skip_validation => 1 } );
307
    my $userid = $librarian->userid;
308
309
    my $patron = $builder->build_object(
310
        {
311
            class => 'Koha::Patrons',
312
            value => { flags => 0 }
313
        }
314
    );
315
316
    $patron->set_password( { password => $password, skip_validation => 1 } );
317
    my $unauth_userid = $patron->userid;
318
319
    my $itemtype = $builder->build_object( { class => 'Koha::ItemTypes' } );
320
321
    my $display_id = $builder->build_object( { class => 'Koha::Displays' } )->display_id;
322
323
    # Unauthorized attempt to update
324
    $t->put_ok( "//$unauth_userid:$password@/api/v1/displays/$display_id" => json =>
325
            { display_name => 'New unauthorized name change' } )->status_is(403);
326
327
    # Attempt partial update on a PUT
328
    my $display_with_missing_field = {
329
        display_name => 'Updated Display',
330
        enabled      => JSON::false
331
    };
332
333
    $t->put_ok( "//$userid:$password@/api/v1/displays/$display_id" => json => $display_with_missing_field )
334
        ->status_is(400)
335
        ->json_is( "/errors" => [ { message => "Missing property.", path => "/body/display_return_over" } ] );
336
337
    # Full object update on PUT
338
    my $display_with_updated_field = {
339
        display_name        => "Updated Display Name",
340
        enabled             => JSON::false,
341
        display_return_over => "yes - any library",
342
        start_date          => "2024-02-01",
343
        end_date            => "2024-11-30",
344
        display_location    => "NEWDISPLAY",
345
        display_code        => "UPDATED",
346
        display_branch      => "CPL",
347
        display_itype       => $itemtype->itemtype
348
    };
349
350
    $t->put_ok( "//$userid:$password@/api/v1/displays/$display_id" => json => $display_with_updated_field )
351
        ->status_is(200)
352
        ->json_is( '/display_name' => 'Updated Display Name' );
353
354
    # Authorized attempt to write invalid data
355
    my $display_with_invalid_field = {
356
        blah                => "Display Blah",
357
        display_name        => "Test Display",
358
        enabled             => JSON::true,
359
        display_return_over => "no"
360
    };
361
362
    $t->put_ok( "//$userid:$password@/api/v1/displays/$display_id" => json => $display_with_invalid_field )
363
        ->status_is(400)
364
        ->json_is(
365
        "/errors" => [
366
            {
367
                message => "Properties not allowed: blah.",
368
                path    => "/body"
369
            }
370
        ]
371
        );
372
373
    my $display_to_delete = $builder->build_object( { class => 'Koha::Displays' } );
374
    my $non_existent_id   = $display_to_delete->display_id;
375
    $display_to_delete->delete;
376
377
    $t->put_ok( "//$userid:$password@/api/v1/displays/$non_existent_id" => json => $display_with_updated_field )
378
        ->status_is(404);
379
380
    # Wrong method (POST)
381
    $display_with_updated_field->{display_id} = 2;
382
383
    $t->post_ok( "//$userid:$password@/api/v1/displays/$display_id" => json => $display_with_updated_field )
384
        ->status_is(404);
385
386
    $schema->storage->txn_rollback;
387
};
388
389
subtest 'delete() tests' => sub {
390
391
    plan tests => 7;
392
393
    $schema->storage->txn_begin;
394
395
    my $librarian = $builder->build_object(
396
        {
397
            class => 'Koha::Patrons',
398
            value => { flags => 1 }     # displays permission
399
        }
400
    );
401
    my $password = 'thePassword123';
402
    $librarian->set_password( { password => $password, skip_validation => 1 } );
403
    my $userid = $librarian->userid;
404
405
    my $patron = $builder->build_object(
406
        {
407
            class => 'Koha::Patrons',
408
            value => { flags => 0 }
409
        }
410
    );
411
412
    $patron->set_password( { password => $password, skip_validation => 1 } );
413
    my $unauth_userid = $patron->userid;
414
415
    my $display_id = $builder->build_object( { class => 'Koha::Displays' } )->display_id;
416
417
    # Unauthorized attempt to delete
418
    $t->delete_ok("//$unauth_userid:$password@/api/v1/displays/$display_id")->status_is(403);
419
420
    $t->delete_ok("//$userid:$password@/api/v1/displays/$display_id")
421
        ->status_is( 204, 'REST3.2.4' )
422
        ->content_is( '', 'REST3.3.4' );
423
424
    $t->delete_ok("//$userid:$password@/api/v1/displays/$display_id")->status_is(404);
425
426
    $schema->storage->txn_rollback;
427
};
(-)a/t/db_dependent/api/v1/items.t (-2 / +86 lines)
Lines 19-26 Link Here
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Test::More tests => 7;
22
use Test::NoWarnings;
23
use Test::NoWarnings;
23
use Test::More tests => 6;
24
use Test::MockModule;
24
use Test::MockModule;
25
use Test::Mojo;
25
use Test::Mojo;
26
use Test::Warn;
26
use Test::Warn;
Lines 626-628 subtest 'pickup_locations() tests' => sub { Link Here
626
626
627
    $schema->storage->txn_rollback;
627
    $schema->storage->txn_rollback;
628
};
628
};
629
- 
629
630
subtest 'effective display fields tests' => sub {
631
632
    plan tests => 12;
633
634
    $schema->storage->txn_begin;
635
636
    t::lib::Mocks::mock_preference( 'UseDisplayModule', 1 );
637
638
    my $librarian = $builder->build_object(
639
        {
640
            class => 'Koha::Patrons',
641
            value => { flags => 1 }
642
        }
643
    );
644
    my $password = 'thePassword123';
645
    $librarian->set_password( { password => $password, skip_validation => 1 } );
646
    my $userid = $librarian->userid;
647
648
    my $patron = $builder->build_object(
649
        {
650
            class => 'Koha::Patrons',
651
            value => { flags => 0 }
652
        }
653
    );
654
    $patron->set_password( { password => $password, skip_validation => 1 } );
655
    my $unauth_userid = $patron->userid;
656
657
    # Create an item with specific location, collection code, homebranch, and holdingbranch
658
    my $item = $builder->build_sample_item(
659
        {
660
            location      => 'SHELF',
661
            ccode         => 'COLL1',
662
            homebranch    => $librarian->branchcode,
663
            holdingbranch => $librarian->branchcode,
664
        }
665
    );
666
667
    # Test without display - should return regular values
668
    $t->get_ok( "//$userid:$password@/api/v1/items/" . $item->id )
669
        ->status_is(200)
670
        ->json_is( '/effective_location'           => 'SHELF' )
671
        ->json_is( '/effective_collection_code'    => 'COLL1' )
672
        ->json_is( '/effective_home_library_id'    => $librarian->branchcode )
673
        ->json_is( '/effective_holding_library_id' => $librarian->branchcode );
674
675
    # Create a display with different values
676
    my $display_library = $builder->build_object( { class => 'Koha::Libraries' } );
677
    my $display         = $builder->build_object(
678
        {
679
            class => 'Koha::Displays',
680
            value => {
681
                display_location       => 'DISPLAY',
682
                display_code           => 'DISPLAYCOLL',
683
                display_branch         => $display_library->branchcode,
684
                display_holding_branch => $display_library->branchcode,
685
                enabled                => 1,
686
                start_date             => undef,
687
                end_date               => undef,
688
            }
689
        }
690
    );
691
692
    # Add item to display
693
    my $display_item = $builder->build_object(
694
        {
695
            class => 'Koha::DisplayItems',
696
            value => {
697
                display_id   => $display->display_id,
698
                itemnumber   => $item->itemnumber,
699
                biblionumber => $item->biblionumber,
700
            }
701
        }
702
    );
703
704
    # Test with display - should return display values
705
    $t->get_ok( "//$userid:$password@/api/v1/items/" . $item->id )
706
        ->status_is(200)
707
        ->json_is( '/effective_location'           => 'DISPLAY' )
708
        ->json_is( '/effective_collection_code'    => 'DISPLAYCOLL' )
709
        ->json_is( '/effective_home_library_id'    => $display_library->branchcode )
710
        ->json_is( '/effective_holding_library_id' => $display_library->branchcode );
711
712
    $schema->storage->txn_rollback;
713
};

Return to bug 14962