Bugzilla – Attachment 193733 Details for
Bug 14962
Temp Shelving Location / On Display Module
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 14962: On Display perl tests
8f13152.patch (text/plain), 62.11 KB, created by
Martin Renvoize (ashimema)
on 2026-02-24 13:36:12 UTC
(
hide
)
Description:
Bug 14962: On Display perl tests
Filename:
MIME Type:
Creator:
Martin Renvoize (ashimema)
Created:
2026-02-24 13:36:12 UTC
Size:
62.11 KB
patch
obsolete
>From 8f131529679c7afadccdd217b9bbe1fbd80b584e Mon Sep 17 00:00:00 2001 >From: Jake Deery <jake.deery@openfifth.co.uk> >Date: Tue, 2 Dec 2025 09:16:05 +0000 >Subject: [PATCH] Bug 14962: On Display perl tests > >This bug provides the test harness for Bug 14962's new perl methods. The front-end tests are contained within the Cypress tests patch, and you should review the commit message for that patch for end-user test steps. > >TO TEST: >APPLY PATCHSET >a) run prove -vv t/db_dependent/Circulation/Displays.t > *) verify it passes >b) run prove -vv t/db_dependent/Koha/DisplayItems.t > *) verify it passes >c) run prove -vv t/db_dependent/Koha/Displays.t > *) verify it passes >d) run prove -vv t/db_dependent/api/v1/displayitems.t > *) verify it passes >e) run prove -vv t/db_dependent/api/v1/displays.t > *) verify it passes >f) run prove -vv t/db_dependent/api/v1/items.t > *) verify it passes > >Sponsored-by: ByWater Solutions >Signed-of-by: Martin Renvoize <martin.renvoize@openfifth.co.uk> >--- > t/Koha/Auth/Permissions.t | 8 + > t/db_dependent/Circulation/Displays.t | 200 +++++++++++ > t/db_dependent/Koha/DisplayItems.t | 262 ++++++++++++++ > t/db_dependent/Koha/Displays.t | 186 ++++++++++ > t/db_dependent/Search.t | 2 + > t/db_dependent/api/v1/displayitems.t | 492 ++++++++++++++++++++++++++ > t/db_dependent/api/v1/displays.t | 427 ++++++++++++++++++++++ > t/db_dependent/api/v1/items.t | 87 ++++- > 8 files changed, 1663 insertions(+), 1 deletion(-) > create mode 100755 t/db_dependent/Circulation/Displays.t > create mode 100755 t/db_dependent/Koha/DisplayItems.t > create mode 100755 t/db_dependent/Koha/Displays.t > create mode 100755 t/db_dependent/api/v1/displayitems.t > create mode 100755 t/db_dependent/api/v1/displays.t > >diff --git a/t/Koha/Auth/Permissions.t b/t/Koha/Auth/Permissions.t >index 32081bc70f4..04290cbe9b1 100755 >--- a/t/Koha/Auth/Permissions.t >+++ b/t/Koha/Auth/Permissions.t >@@ -153,6 +153,14 @@ subtest 'superlibrarian tests' => sub { > 'CAN_user_clubs_edit_templates' => 1, > 'CAN_user_clubs_enroll' => 1, > 'CAN_user_clubs' => 1, >+ 'CAN_user_displays_remove_items_from_display' => 1, >+ 'CAN_user_displays_add_items_to_display' => 1, >+ 'CAN_user_displays_add_items_to_display_from_any_libraries' => 1, >+ 'CAN_user_displays_delete_display' => 1, >+ 'CAN_user_displays_edit_display' => 1, >+ 'CAN_user_displays_add_display' => 1, >+ 'CAN_user_displays_view_display' => 1, >+ 'CAN_user_displays_view_display' => 1, > 'CAN_user_coursereserves_add_reserves' => 1, > 'CAN_user_coursereserves_delete_reserves' => 1, > 'CAN_user_coursereserves_manage_courses' => 1, >diff --git a/t/db_dependent/Circulation/Displays.t b/t/db_dependent/Circulation/Displays.t >new file mode 100755 >index 00000000000..60bc4576194 >--- /dev/null >+++ b/t/db_dependent/Circulation/Displays.t >@@ -0,0 +1,200 @@ >+#!/usr/bin/perl >+ >+# Copyright 2025-2026 Open Fifth Ltd >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <https://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Test::More tests => 3; >+use Test::NoWarnings; >+ >+use C4::Circulation qw( AddIssue AddReturn ); >+use Koha::Database; >+use Koha::DateUtils qw( dt_from_string ); >+ >+use t::lib::TestBuilder; >+use t::lib::Mocks; >+ >+my $schema = Koha::Database->new->schema; >+$schema->storage->txn_begin; >+ >+my $builder = t::lib::TestBuilder->new; >+ >+t::lib::Mocks::mock_preference( 'UseDisplayModule', 1 ); >+ >+subtest 'display_return_over - yes - any library' => sub { >+ plan tests => 5; >+ >+ # Create test data >+ my $library1 = $builder->build_object( { class => 'Koha::Libraries' } ); >+ my $library2 = $builder->build_object( { class => 'Koha::Libraries' } ); >+ my $patron = >+ $builder->build_object( { class => 'Koha::Patrons', value => { branchcode => $library1->branchcode } } ); >+ my $item = >+ $builder->build_sample_item( { homebranch => $library1->branchcode, holdingbranch => $library1->branchcode } ); >+ >+ # Mock userenv >+ t::lib::Mocks::mock_userenv( { branchcode => $library1->branchcode } ); >+ >+ # Create a display with 'yes - any library' return_over setting >+ my $display = $builder->build_object( >+ { >+ class => 'Koha::Displays', >+ value => { >+ display_return_over => 'yes - any library', >+ enabled => 1, >+ start_date => undef, >+ end_date => undef, >+ } >+ } >+ ); >+ >+ # Add item to display >+ my $display_item = $builder->build_object( >+ { >+ class => 'Koha::DisplayItems', >+ value => { >+ display_id => $display->display_id, >+ itemnumber => $item->itemnumber, >+ biblionumber => $item->biblionumber, >+ } >+ } >+ ); >+ >+ # Verify item is in display >+ my $active_display = $item->active_display_item; >+ ok( defined $active_display, 'Item should have an active display item' ); >+ is( $active_display->display_id, $display->display_id, 'Display item should be linked to the correct display' ); >+ >+ # Issue the item >+ AddIssue( $patron, $item->barcode ); >+ >+ # Return the item at the home library >+ my ( $doreturn, $messages, $issue, $borrower ) = AddReturn( $item->barcode, $library1->branchcode ); >+ is( $doreturn, 1, 'Item should be returned successfully' ); >+ ok( exists $messages->{RemovedFromDisplay}, 'RemovedFromDisplay message should be present' ); >+ >+ # Verify item was removed from display >+ $active_display = $item->active_display_item; >+ is( $active_display, undef, 'Item should no longer have an active display item after return' ); >+}; >+ >+subtest 'display_return_over - yes - except at home library' => sub { >+ plan tests => 9; >+ >+ # Create test data >+ my $library1 = $builder->build_object( { class => 'Koha::Libraries' } ); >+ my $library2 = $builder->build_object( { class => 'Koha::Libraries' } ); >+ my $patron = >+ $builder->build_object( { class => 'Koha::Patrons', value => { branchcode => $library1->branchcode } } ); >+ >+ # Mock userenv >+ t::lib::Mocks::mock_userenv( { branchcode => $library1->branchcode } ); >+ >+ # Test 1: Return at non-home library should remove from display >+ my $item1 = >+ $builder->build_sample_item( { homebranch => $library1->branchcode, holdingbranch => $library1->branchcode } ); >+ >+ my $display1 = $builder->build_object( >+ { >+ class => 'Koha::Displays', >+ value => { >+ display_return_over => 'yes - except at home library', >+ enabled => 1, >+ start_date => undef, >+ end_date => undef, >+ } >+ } >+ ); >+ >+ my $display_item1 = $builder->build_object( >+ { >+ class => 'Koha::DisplayItems', >+ value => { >+ display_id => $display1->display_id, >+ itemnumber => $item1->itemnumber, >+ biblionumber => $item1->biblionumber, >+ } >+ } >+ ); >+ >+ my $active_display = $item1->active_display_item; >+ ok( defined $active_display, 'Item should have an active display item' ); >+ >+ AddIssue( $patron, $item1->barcode ); >+ my ( $doreturn, $messages, $issue, $borrower ) = AddReturn( $item1->barcode, $library2->branchcode ); >+ >+ is( $doreturn, 1, 'Item should be returned successfully at non-home library' ); >+ ok( >+ exists $messages->{RemovedFromDisplay}, >+ 'RemovedFromDisplay message should be present when returned at non-home library' >+ ); >+ >+ $active_display = $item1->active_display_item; >+ is( $active_display, undef, 'Item should be removed from display when returned at non-home library' ); >+ >+ # Test 2: Return at home library should NOT remove from display >+ my $item2 = >+ $builder->build_sample_item( { homebranch => $library1->branchcode, holdingbranch => $library1->branchcode } ); >+ >+ my $display2 = $builder->build_object( >+ { >+ class => 'Koha::Displays', >+ value => { >+ display_return_over => 'yes - except at home library', >+ enabled => 1, >+ start_date => undef, >+ end_date => undef, >+ } >+ } >+ ); >+ >+ my $display_item2 = $builder->build_object( >+ { >+ class => 'Koha::DisplayItems', >+ value => { >+ display_id => $display2->display_id, >+ itemnumber => $item2->itemnumber, >+ biblionumber => $item2->biblionumber, >+ } >+ } >+ ); >+ >+ $active_display = $item2->active_display_item; >+ ok( defined $active_display, 'Item should have an active display item before return' ); >+ >+ AddIssue( $patron, $item2->barcode ); >+ ( $doreturn, $messages, $issue, $borrower ) = AddReturn( $item2->barcode, $library1->branchcode ); >+ >+ is( $doreturn, 1, 'Item should be returned successfully at home library' ); >+ ok( >+ !exists $messages->{RemovedFromDisplay}, >+ 'RemovedFromDisplay message should NOT be present when returned at home library' >+ ); >+ >+ $active_display = $item2->active_display_item; >+ ok( defined $active_display, 'Item should still have an active display item when returned at home library' ); >+ is( >+ $active_display->display_id, $display2->display_id, >+ 'Display item should still be linked to the correct display' >+ ); >+ >+ # Clean up >+ $display_item2->delete; >+}; >+ >+$schema->storage->txn_rollback; >diff --git a/t/db_dependent/Koha/DisplayItems.t b/t/db_dependent/Koha/DisplayItems.t >new file mode 100755 >index 00000000000..a66f22972d4 >--- /dev/null >+++ b/t/db_dependent/Koha/DisplayItems.t >@@ -0,0 +1,262 @@ >+#!/usr/bin/perl >+ >+# Copyright 2025-2026 Open Fifth Ltd >+# >+# This file is part of Koha >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <https://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Test::NoWarnings; >+use Test::More tests => 13; >+ >+use C4::Context; >+use Koha::DateUtils qw( dt_from_string ); >+use Koha::DisplayItem; >+use Koha::DisplayItems; >+use Koha::Database; >+ >+use t::lib::TestBuilder; >+ >+my $schema = Koha::Database->new->schema; >+$schema->storage->txn_begin; >+ >+my $builder = t::lib::TestBuilder->new; >+my $nb_of_display_items = Koha::DisplayItems->search->count; >+ >+# Create test data >+my $library = $builder->build( { source => 'Branch' } ); >+my $biblio = $builder->build( { source => 'Biblio' } ); >+my $item1 = $builder->build( { source => 'Item', value => { biblionumber => $biblio->{biblionumber} } } ); >+my $item2 = $builder->build( { source => 'Item', value => { biblionumber => $biblio->{biblionumber} } } ); >+ >+my $display = $builder->build( { source => 'Display', value => { display_branch => $library->{branchcode} } } ); >+ >+# Create display items for testing >+my $new_display_item_1 = Koha::DisplayItem->new( >+ { >+ display_id => $display->{display_id}, >+ itemnumber => $item1->{itemnumber}, >+ biblionumber => $biblio->{biblionumber}, >+ date_added => '2024-01-01', >+ date_remove => '2024-12-31', >+ } >+)->store; >+ >+my $new_display_item_2 = Koha::DisplayItem->new( >+ { >+ display_id => $display->{display_id}, >+ itemnumber => $item2->{itemnumber}, >+ biblionumber => $biblio->{biblionumber}, >+ date_added => '2024-02-01', >+ date_remove => '2024-11-30', >+ } >+)->store; >+ >+# Test basic CRUD operations >+like( $new_display_item_1->display_id, qr|^\d+$|, 'Adding a new display item should have set the display_id' ); >+is( Koha::DisplayItems->search->count, $nb_of_display_items + 2, 'The 2 display items should have been added' ); >+ >+my $retrieved_display_item_1 = Koha::DisplayItems->find( >+ { >+ display_id => $new_display_item_1->display_id, >+ itemnumber => $new_display_item_1->itemnumber >+ } >+); >+is( >+ $retrieved_display_item_1->biblionumber, $new_display_item_1->biblionumber, >+ 'Find a display item by composite key should return the correct display item' >+); >+ >+# Test Koha::DisplayItem methods >+subtest 'display method' => sub { >+ plan tests => 2; >+ >+ my $display_obj = $new_display_item_1->display; >+ isa_ok( $display_obj, 'Koha::Display', 'display should return a Koha::Display object' ); >+ is( $display_obj->display_id, $display->{display_id}, 'display should return the correct display' ); >+}; >+ >+subtest 'item method' => sub { >+ plan tests => 2; >+ >+ my $item_obj = $new_display_item_1->item; >+ isa_ok( $item_obj, 'Koha::Item', 'item should return a Koha::Item object' ); >+ is( $item_obj->itemnumber, $item1->{itemnumber}, 'item should return the correct item' ); >+}; >+ >+subtest 'biblio method' => sub { >+ plan tests => 2; >+ >+ my $biblio_obj = $new_display_item_1->biblio; >+ isa_ok( $biblio_obj, 'Koha::Biblio', 'biblio should return a Koha::Biblio object' ); >+ is( $biblio_obj->biblionumber, $biblio->{biblionumber}, 'biblio should return the correct biblio' ); >+}; >+ >+# Test Koha::DisplayItems methods >+subtest 'for_display method' => sub { >+ plan tests => 2; >+ >+ my $display_items = Koha::DisplayItems->for_display( $display->{display_id} ); >+ isa_ok( $display_items, 'Koha::DisplayItems', 'for_display should return a Koha::DisplayItems object' ); >+ is( $display_items->count, 2, 'for_display should return the correct number of display items' ); >+}; >+ >+subtest 'for_item method' => sub { >+ plan tests => 2; >+ >+ my $display_items = Koha::DisplayItems->for_item( $item1->{itemnumber} ); >+ isa_ok( $display_items, 'Koha::DisplayItems', 'for_item should return a Koha::DisplayItems object' ); >+ is( $display_items->count, 1, 'for_item should return the correct number of display items' ); >+}; >+ >+subtest 'for_biblio method' => sub { >+ plan tests => 2; >+ >+ my $display_items = Koha::DisplayItems->for_biblio( $biblio->{biblionumber} ); >+ isa_ok( $display_items, 'Koha::DisplayItems', 'for_biblio should return a Koha::DisplayItems object' ); >+ is( $display_items->count, 2, 'for_biblio should return the correct number of display items' ); >+}; >+ >+subtest 'due_for_removal method' => sub { >+ plan tests => 3; >+ >+ # Create a display item that should be removed today >+ my $today = dt_from_string()->ymd; >+ my $item_to_remove = $builder->build( { source => 'Item', value => { biblionumber => $biblio->{biblionumber} } } ); >+ >+ my $display_item_due = Koha::DisplayItem->new( >+ { >+ display_id => $display->{display_id}, >+ itemnumber => $item_to_remove->{itemnumber}, >+ biblionumber => $biblio->{biblionumber}, >+ date_added => '2024-01-01', >+ date_remove => $today, >+ } >+ )->store; >+ >+ my $due_items = Koha::DisplayItems->due_for_removal; >+ isa_ok( $due_items, 'Koha::DisplayItems', 'due_for_removal should return a Koha::DisplayItems object' ); >+ >+ my $found_due_item = 0; >+ while ( my $item = $due_items->next ) { >+ $found_due_item = 1 if $item->itemnumber == $item_to_remove->{itemnumber}; >+ } >+ ok( $found_due_item, 'due_for_removal should include items due for removal today' ); >+ >+ # Create a display item that should be removed in the future >+ my $future_date = dt_from_string()->add( days => 30 )->ymd; >+ my $item_future = $builder->build( { source => 'Item', value => { biblionumber => $biblio->{biblionumber} } } ); >+ >+ my $display_item_future = Koha::DisplayItem->new( >+ { >+ display_id => $display->{display_id}, >+ itemnumber => $item_future->{itemnumber}, >+ biblionumber => $biblio->{biblionumber}, >+ date_added => '2024-01-01', >+ date_remove => $future_date, >+ } >+ )->store; >+ >+ my $due_items_2 = Koha::DisplayItems->due_for_removal; >+ my $found_future_item = 0; >+ while ( my $item = $due_items_2->next ) { >+ $found_future_item = 1 if $item->itemnumber == $item_future->{itemnumber}; >+ } >+ ok( !$found_future_item, 'due_for_removal should not include items due for removal in the future' ); >+ >+ # Clean up the additional display items >+ $display_item_due->delete; >+ $display_item_future->delete; >+}; >+ >+subtest 'display_days automatic date_remove calculation' => sub { >+ plan tests => 5; >+ >+ # Create a display with display_days set to 30 >+ my $display_with_days = $builder->build_object( >+ { >+ class => 'Koha::Displays', >+ value => { display_days => 30 } >+ } >+ ); >+ >+ my $item3 = $builder->build( { source => 'Item', value => { biblionumber => $biblio->{biblionumber} } } ); >+ >+ # Create display item without date_remove - should auto-calculate >+ my $display_item_auto = Koha::DisplayItem->new( >+ { >+ display_id => $display_with_days->display_id, >+ itemnumber => $item3->{itemnumber}, >+ biblionumber => $biblio->{biblionumber}, >+ } >+ )->store; >+ >+ ok( defined $display_item_auto->date_remove, 'date_remove should be set automatically' ); >+ >+ # Calculate expected date (today + 30 days) >+ my $dt = dt_from_string(); >+ $dt->add( days => 30 ); >+ my $expected_date = $dt->ymd; >+ is( $display_item_auto->date_remove, $expected_date, 'date_remove should be today + display_days' ); >+ >+ # Create display item with explicit date_remove - should NOT auto-calculate >+ my $item4 = $builder->build( { source => 'Item', value => { biblionumber => $biblio->{biblionumber} } } ); >+ my $manual_date = '2025-12-31'; >+ my $display_item_manual = Koha::DisplayItem->new( >+ { >+ display_id => $display_with_days->display_id, >+ itemnumber => $item4->{itemnumber}, >+ biblionumber => $biblio->{biblionumber}, >+ date_remove => $manual_date, >+ } >+ )->store; >+ >+ is( $display_item_manual->date_remove, $manual_date, 'Explicit date_remove should not be overridden' ); >+ >+ # Create display without display_days - date_remove should be NULL >+ my $display_no_days = $builder->build_object( >+ { >+ class => 'Koha::Displays', >+ value => { display_days => undef } >+ } >+ ); >+ my $item5 = $builder->build( { source => 'Item', value => { biblionumber => $biblio->{biblionumber} } } ); >+ >+ my $display_item_no_days = Koha::DisplayItem->new( >+ { >+ display_id => $display_no_days->display_id, >+ itemnumber => $item5->{itemnumber}, >+ biblionumber => $biblio->{biblionumber}, >+ } >+ )->store; >+ >+ is( $display_item_no_days->date_remove, undef, 'date_remove should be NULL when display has no display_days' ); >+ >+ # Clean up >+ $display_item_auto->delete; >+ $display_item_manual->delete; >+ $display_item_no_days->delete; >+ $display_with_days->delete; >+ $display_no_days->delete; >+ >+ pass('Cleanup successful'); >+}; >+ >+# Test delete >+$retrieved_display_item_1->delete; >+is( Koha::DisplayItems->search->count, $nb_of_display_items + 1, 'Delete should have deleted the display item' ); >+ >+$schema->storage->txn_rollback; >diff --git a/t/db_dependent/Koha/Displays.t b/t/db_dependent/Koha/Displays.t >new file mode 100755 >index 00000000000..8cba330d6ea >--- /dev/null >+++ b/t/db_dependent/Koha/Displays.t >@@ -0,0 +1,186 @@ >+#!/usr/bin/perl >+ >+# Copyright 2025-2026 Open Fifth Ltd >+# >+# This file is part of Koha >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <https://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Test::NoWarnings; >+use Test::More tests => 11; >+ >+use DateTime; >+use Koha::Display; >+use Koha::Displays; >+use Koha::Database; >+ >+use t::lib::TestBuilder; >+ >+my $schema = Koha::Database->new->schema; >+$schema->storage->txn_begin; >+ >+my $builder = t::lib::TestBuilder->new; >+my $nb_of_displays = Koha::Displays->search->count; >+ >+# Create a library for testing display_branch >+my $library = $builder->build( { source => 'Branch' } ); >+ >+# Create item types for testing >+my $itemtype1 = $builder->build( { source => 'Itemtype' } ); >+my $itemtype2 = $builder->build( { source => 'Itemtype' } ); >+ >+# Get current date for testing >+my $today = DateTime->today; >+my $yesterday = $today->clone->subtract( days => 1 ); >+my $tomorrow = $today->clone->add( days => 1 ); >+ >+# Create displays for testing >+my $new_display_1 = Koha::Display->new( >+ { >+ display_name => 'my_display_name_for_test_1', >+ enabled => 1, >+ display_return_over => 'no', >+ start_date => $yesterday->ymd, >+ end_date => $tomorrow->ymd, >+ display_location => 'DISPLAY_LOC_1', >+ display_code => 'DISPLAY_CODE_1', >+ display_holding_branch => $library->{branchcode}, >+ display_branch => $library->{branchcode}, >+ display_itype => $itemtype1->{itemtype}, >+ } >+)->store; >+ >+my $new_display_2 = Koha::Display->new( >+ { >+ display_name => 'my_display_name_for_test_2', >+ enabled => 0, >+ display_return_over => 'yes - any library', >+ display_location => 'DISPLAY_LOC_2', >+ display_code => 'DISPLAY_CODE_2', >+ display_itype => $itemtype2->{itemtype}, >+ } >+)->store; >+ >+# Test basic CRUD operations >+like( $new_display_1->display_id, qr|^\d+$|, 'Adding a new display should have set the display_id' ); >+is( Koha::Displays->search->count, $nb_of_displays + 2, 'The 2 displays should have been added' ); >+ >+my $retrieved_display_1 = Koha::Displays->find( $new_display_1->display_id ); >+is( >+ $retrieved_display_1->display_name, $new_display_1->display_name, >+ 'Find a display by id should return the correct display' >+); >+ >+# Test Koha::Display methods >+subtest 'display_items method' => sub { >+ plan tests => 2; >+ >+ my $display_items = $new_display_1->display_items; >+ isa_ok( $display_items, 'Koha::DisplayItems', 'display_items should return a Koha::DisplayItems object' ); >+ is( $display_items->count, 0, 'New display should have no display items' ); >+}; >+ >+subtest 'holding_library method' => sub { >+ plan tests => 3; >+ >+ my $holding_library_obj = $new_display_1->holding_library; >+ isa_ok( $holding_library_obj, 'Koha::Library', 'holding_library should return a Koha::Library object' ); >+ is( $holding_library_obj->branchcode, $library->{branchcode}, 'holding_library should return the correct library' ); >+ >+ # Test display without holding_library >+ is( $new_display_2->holding_library, undef, 'display without holding_library should return undef' ); >+}; >+ >+subtest 'home_library method' => sub { >+ plan tests => 3; >+ >+ my $home_library_obj = $new_display_1->home_library; >+ isa_ok( $home_library_obj, 'Koha::Library', 'home_library should return a Koha::Library object' ); >+ is( $home_library_obj->branchcode, $library->{branchcode}, 'home_library should return the correct library' ); >+ >+ # Test display without home_library >+ is( $new_display_2->home_library, undef, 'display without home_library should return undef' ); >+}; >+ >+# Test Koha::Displays methods >+subtest 'enabled method' => sub { >+ plan tests => 2; >+ >+ my $enabled_displays = Koha::Displays->enabled; >+ isa_ok( $enabled_displays, 'Koha::Displays', 'enabled should return a Koha::Displays object' ); >+ >+ my $found_enabled = 0; >+ while ( my $display = $enabled_displays->next ) { >+ $found_enabled = 1 if $display->display_id == $new_display_1->display_id; >+ } >+ ok( $found_enabled, 'enabled displays should include the enabled display' ); >+}; >+ >+subtest 'for_branch method' => sub { >+ plan tests => 2; >+ >+ my $branch_displays = Koha::Displays->for_branch( $library->{branchcode} ); >+ isa_ok( $branch_displays, 'Koha::Displays', 'for_branch should return a Koha::Displays object' ); >+ >+ my $found_branch_display = 0; >+ while ( my $display = $branch_displays->next ) { >+ $found_branch_display = 1 if $display->display_id == $new_display_1->display_id; >+ } >+ ok( $found_branch_display, 'for_branch should include displays for the specified branch' ); >+}; >+ >+subtest 'active method' => sub { >+ plan tests => 3; >+ >+ my $active_displays = Koha::Displays->active; >+ isa_ok( $active_displays, 'Koha::Displays', 'active should return a Koha::Displays object' ); >+ >+ my $found_active = 0; >+ while ( my $display = $active_displays->next ) { >+ $found_active = 1 if $display->display_id == $new_display_1->display_id; >+ } >+ ok( $found_active, 'active displays should include the active display within date range' ); >+ >+ # Create a display that's not active (dates in the past) >+ my $past_end = $today->clone->subtract( days => 10 ); >+ my $past_start = $today->clone->subtract( days => 20 ); >+ >+ my $inactive_display = Koha::Display->new( >+ { >+ display_name => 'inactive_display', >+ enabled => 1, >+ display_return_over => 'no', >+ start_date => $past_start->ymd, >+ end_date => $past_end->ymd, >+ } >+ )->store; >+ >+ my $active_displays_2 = Koha::Displays->active; >+ my $found_inactive = 0; >+ while ( my $display = $active_displays_2->next ) { >+ $found_inactive = 1 if $display->display_id == $inactive_display->display_id; >+ } >+ ok( !$found_inactive, 'active displays should not include displays outside date range' ); >+ >+ # Clean up the inactive display >+ $inactive_display->delete; >+}; >+ >+# Test delete >+$retrieved_display_1->delete; >+is( Koha::Displays->search->count, $nb_of_displays + 1, 'Delete should have deleted the display' ); >+ >+$schema->storage->txn_rollback; >diff --git a/t/db_dependent/Search.t b/t/db_dependent/Search.t >index 8ebcd4e2b2a..b671a80d27d 100755 >--- a/t/db_dependent/Search.t >+++ b/t/db_dependent/Search.t >@@ -136,6 +136,8 @@ $contextmodule->mock( > return '1'; > } elsif ( $pref eq 'UseControlNumber' ) { > return '0'; >+ } elsif ( $pref eq 'UseDisplayModule' ) { >+ return '0'; > } elsif ( $pref eq 'UseICUStyleQuotes' ) { > return '0'; > } elsif ( $pref eq 'viewISBD' ) { >diff --git a/t/db_dependent/api/v1/displayitems.t b/t/db_dependent/api/v1/displayitems.t >new file mode 100755 >index 00000000000..87d9c9e9f1c >--- /dev/null >+++ b/t/db_dependent/api/v1/displayitems.t >@@ -0,0 +1,492 @@ >+#!/usr/bin/env perl >+ >+# Copyright 2025-2026 Open Fifth Ltd >+# >+# This file is part of Koha >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <https://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Test::NoWarnings; >+use Test::More tests => 6; >+use Test::Mojo; >+use JSON; >+ >+use t::lib::TestBuilder; >+use t::lib::Mocks; >+ >+use Koha::DisplayItems; >+use Koha::Database; >+ >+my $schema = Koha::Database->new->schema; >+my $builder = t::lib::TestBuilder->new; >+ >+my $t = Test::Mojo->new('Koha::REST::V1'); >+t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); >+ >+subtest 'list() tests' => sub { >+ >+ plan tests => 20; >+ >+ $schema->storage->txn_begin; >+ >+ Koha::DisplayItems->search->delete; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 1 } # displays permission >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ ## Authorized user tests >+ # No displayitems, so empty array should be returned >+ $t->get_ok("//$userid:$password@/api/v1/display/items")->status_is(200)->json_is( [] ); >+ >+ # Create test data >+ my $display = $builder->build_object( { class => 'Koha::Displays' } ); >+ my $biblio = $builder->build_sample_biblio(); >+ my $item = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } ); >+ >+ my $displayitem = $builder->build_object( >+ { >+ class => 'Koha::DisplayItems', >+ value => { >+ display_id => $display->display_id, >+ itemnumber => $item->itemnumber, >+ biblionumber => $biblio->biblionumber >+ } >+ } >+ ); >+ >+ # One displayitem created, should get returned >+ $t->get_ok("//$userid:$password@/api/v1/display/items")->status_is(200)->json_is( [ $displayitem->to_api ] ); >+ >+ # Create another displayitem with same display >+ my $item2 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } ); >+ my $another_displayitem = $builder->build_object( >+ { >+ class => 'Koha::DisplayItems', >+ value => { >+ display_id => $display->display_id, >+ itemnumber => $item2->itemnumber, >+ biblionumber => $biblio->biblionumber >+ } >+ } >+ ); >+ >+ # Create displayitem with different display >+ my $display2 = $builder->build_object( { class => 'Koha::Displays' } ); >+ my $item3 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } ); >+ my $displayitem_different_display = $builder->build_object( >+ { >+ class => 'Koha::DisplayItems', >+ value => { >+ display_id => $display2->display_id, >+ itemnumber => $item3->itemnumber, >+ biblionumber => $biblio->biblionumber >+ } >+ } >+ ); >+ >+ # Three displayitems created, they should all be returned >+ $t->get_ok("//$userid:$password@/api/v1/display/items")->status_is(200)->json_is( >+ [ >+ $displayitem->to_api, >+ $another_displayitem->to_api, >+ $displayitem_different_display->to_api >+ ] >+ ); >+ >+ # Filtering works, two displayitems sharing display_id >+ $t->get_ok( "//$userid:$password@/api/v1/display/items?display_id=" . $display->display_id ) >+ ->status_is(200) >+ ->json_is( >+ [ >+ $displayitem->to_api, >+ $another_displayitem->to_api >+ ] >+ ); >+ >+ $t->get_ok( "//$userid:$password@/api/v1/display/items?itemnumber=" . $item->itemnumber ) >+ ->status_is(200) >+ ->json_is( [ $displayitem->to_api ] ); >+ >+ # Warn on unsupported query parameter >+ $t->get_ok("//$userid:$password@/api/v1/display/items?displayitem_blah=blah") >+ ->status_is(400) >+ ->json_is( [ { path => '/query/displayitem_blah', message => 'Malformed query string' } ] ); >+ >+ # Unauthorized access >+ $t->get_ok("//$unauth_userid:$password@/api/v1/display/items")->status_is(403); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'get() tests' => sub { >+ >+ plan tests => 11; >+ >+ $schema->storage->txn_begin; >+ >+ # Create test data >+ my $display = $builder->build_object( { class => 'Koha::Displays' } ); >+ my $biblio = $builder->build_sample_biblio(); >+ my $item = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } ); >+ >+ my $displayitem = $builder->build_object( >+ { >+ class => 'Koha::DisplayItems', >+ value => { >+ display_id => $display->display_id, >+ itemnumber => $item->itemnumber, >+ biblionumber => $biblio->biblionumber >+ } >+ } >+ ); >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 1 } # displays permission >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ $t->get_ok( >+ "//$userid:$password@/api/v1/display/items/" . $displayitem->display_id . "/" . $displayitem->itemnumber ) >+ ->status_is(200) >+ ->json_is( $displayitem->to_api ); >+ >+ $t->get_ok( "//$unauth_userid:$password@/api/v1/display/items/" >+ . $displayitem->display_id . "/" >+ . $displayitem->itemnumber )->status_is(403); >+ >+ # Create displayitem to delete >+ my $item_to_delete = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } ); >+ my $displayitem_to_delete = $builder->build_object( >+ { >+ class => 'Koha::DisplayItems', >+ value => { >+ display_id => $display->display_id, >+ itemnumber => $item_to_delete->itemnumber, >+ biblionumber => $biblio->biblionumber >+ } >+ } >+ ); >+ my $non_existent_display_id = $displayitem_to_delete->display_id; >+ my $non_existent_item_id = $displayitem_to_delete->itemnumber; >+ $displayitem_to_delete->delete; >+ >+ $t->get_ok("//$userid:$password@/api/v1/display/items/$non_existent_display_id/$non_existent_item_id") >+ ->status_is(404) >+ ->json_is( '/error' => 'Display item not found' ); >+ >+ # Test with completely non-existent IDs >+ $t->get_ok("//$userid:$password@/api/v1/display/items/99999/99999") >+ ->status_is(404) >+ ->json_is( '/error' => 'Display item not found' ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'add() tests' => sub { >+ >+ plan tests => 17; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 1 } # displays permission >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ # Create test data >+ my $display = $builder->build_object( { class => 'Koha::Displays' } ); >+ my $biblio = $builder->build_sample_biblio(); >+ my $item = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } ); >+ >+ my $displayitem = { >+ display_id => $display->display_id, >+ itemnumber => $item->itemnumber, >+ biblionumber => $biblio->biblionumber, >+ date_added => "2024-01-15", >+ date_remove => "2024-02-15" >+ }; >+ >+ # Unauthorized attempt to write >+ $t->post_ok( "//$unauth_userid:$password@/api/v1/display/items" => json => $displayitem )->status_is(403); >+ >+ # Authorized attempt to write invalid data >+ my $displayitem_with_invalid_field = { >+ blah => "DisplayItem Blah", >+ display_id => $display->display_id, >+ itemnumber => $item->itemnumber, >+ biblionumber => $biblio->biblionumber >+ }; >+ >+ $t->post_ok( "//$userid:$password@/api/v1/display/items" => json => $displayitem_with_invalid_field ) >+ ->status_is(400) >+ ->json_is( >+ "/errors" => [ >+ { >+ message => "Properties not allowed: blah.", >+ path => "/body" >+ } >+ ] >+ ); >+ >+ # Authorized attempt to write >+ $t->post_ok( "//$userid:$password@/api/v1/display/items" => json => $displayitem ) >+ ->status_is( 201, 'REST3.2.1' ) >+ ->header_like( >+ Location => qr|^\/api\/v1\/display/items/\d+/\d+|, >+ 'REST3.4.1' >+ ) >+ ->json_is( '/display_id' => $displayitem->{display_id} ) >+ ->json_is( '/itemnumber' => $displayitem->{itemnumber} ) >+ ->json_is( '/biblionumber' => $displayitem->{biblionumber} ) >+ ->json_is( '/date_added' => $displayitem->{date_added} ); >+ >+ # Test missing required field (display_id is required) >+ my $displayitem_missing_field = { >+ itemnumber => $item->itemnumber, >+ biblionumber => $biblio->biblionumber >+ >+ # Missing display_id (required field) >+ }; >+ >+ $t->post_ok( "//$userid:$password@/api/v1/display/items" => json => $displayitem_missing_field ) >+ ->status_is(400) >+ ->json_has('/errors'); >+ >+ # Test successful creation with different item >+ my $item2 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } ); >+ my $displayitem2 = { >+ display_id => $display->display_id, >+ itemnumber => $item2->itemnumber, >+ biblionumber => $biblio->biblionumber, >+ date_added => "2024-01-16", >+ date_remove => "2024-02-16" >+ }; >+ $t->post_ok( "//$userid:$password@/api/v1/display/items" => json => $displayitem2 )->status_is(201); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'update() tests' => sub { >+ >+ plan tests => 15; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 1 } # displays permission >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ # Create test data >+ my $display = $builder->build_object( { class => 'Koha::Displays' } ); >+ my $biblio = $builder->build_sample_biblio(); >+ my $item = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } ); >+ >+ my $displayitem = $builder->build_object( >+ { >+ class => 'Koha::DisplayItems', >+ value => { >+ display_id => $display->display_id, >+ itemnumber => $item->itemnumber, >+ biblionumber => $biblio->biblionumber >+ } >+ } >+ ); >+ >+ # Unauthorized attempt to update >+ $t->put_ok( "//$unauth_userid:$password@/api/v1/display/items/" >+ . $displayitem->display_id . "/" >+ . $displayitem->itemnumber => json => { date_added => '2024-01-20' } )->status_is(403); >+ >+ # Attempt partial update on a PUT (missing required field) >+ my $displayitem_with_missing_field = { >+ biblionumber => $biblio->biblionumber >+ >+ # Missing display_id and itemnumber (required fields) >+ }; >+ >+ $t->put_ok( "//$userid:$password@/api/v1/display/items/" >+ . $displayitem->display_id . "/" >+ . $displayitem->itemnumber => json => $displayitem_with_missing_field ) >+ ->status_is(400) >+ ->json_has("/errors"); >+ >+ # Full object update on PUT >+ my $displayitem_with_updated_field = { >+ display_id => $display->display_id, >+ itemnumber => $item->itemnumber, >+ biblionumber => $biblio->biblionumber, >+ date_added => "2024-01-20", >+ date_remove => "2024-03-20" >+ }; >+ >+ $t->put_ok( "//$userid:$password@/api/v1/display/items/" >+ . $displayitem->display_id . "/" >+ . $displayitem->itemnumber => json => $displayitem_with_updated_field ) >+ ->status_is(200) >+ ->json_is( '/date_added' => '2024-01-20' ); >+ >+ # Authorized attempt to write invalid data >+ my $displayitem_with_invalid_field = { >+ blah => "DisplayItem Blah", >+ display_id => $display->display_id, >+ itemnumber => $item->itemnumber, >+ biblionumber => $biblio->biblionumber >+ }; >+ >+ $t->put_ok( "//$userid:$password@/api/v1/display/items/" >+ . $displayitem->display_id . "/" >+ . $displayitem->itemnumber => json => $displayitem_with_invalid_field )->status_is(400)->json_is( >+ "/errors" => [ >+ { >+ message => "Properties not allowed: blah.", >+ path => "/body" >+ } >+ ] >+ ); >+ >+ # Test with non-existent displayitem >+ $t->put_ok( "//$userid:$password@/api/v1/display/items/99999/99999" => json => $displayitem_with_updated_field ) >+ ->status_is(404); >+ >+ # Wrong method (POST) >+ $t->post_ok( "//$userid:$password@/api/v1/display/items/" >+ . $displayitem->display_id . "/" >+ . $displayitem->itemnumber => json => $displayitem_with_updated_field )->status_is(404); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'delete() tests' => sub { >+ >+ plan tests => 7; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 1 } # displays permission >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ # Create test data >+ my $display = $builder->build_object( { class => 'Koha::Displays' } ); >+ my $biblio = $builder->build_sample_biblio(); >+ my $item = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } ); >+ >+ my $displayitem = $builder->build_object( >+ { >+ class => 'Koha::DisplayItems', >+ value => { >+ display_id => $display->display_id, >+ itemnumber => $item->itemnumber, >+ biblionumber => $biblio->biblionumber >+ } >+ } >+ ); >+ >+ # Unauthorized attempt to delete >+ $t->delete_ok( "//$unauth_userid:$password@/api/v1/display/items/" >+ . $displayitem->display_id . "/" >+ . $displayitem->itemnumber )->status_is(403); >+ >+ $t->delete_ok( >+ "//$userid:$password@/api/v1/display/items/" . $displayitem->display_id . "/" . $displayitem->itemnumber ) >+ ->status_is( 204, 'REST3.2.4' ) >+ ->content_is( '', 'REST3.3.4' ); >+ >+ $t->delete_ok( >+ "//$userid:$password@/api/v1/display/items/" . $displayitem->display_id . "/" . $displayitem->itemnumber ) >+ ->status_is(404); >+ >+ $schema->storage->txn_rollback; >+}; >diff --git a/t/db_dependent/api/v1/displays.t b/t/db_dependent/api/v1/displays.t >new file mode 100755 >index 00000000000..72bb9918bb0 >--- /dev/null >+++ b/t/db_dependent/api/v1/displays.t >@@ -0,0 +1,427 @@ >+#!/usr/bin/env perl >+ >+# Copyright 2025-2026 Open Fifth Ltd >+# >+# This file is part of Koha >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <https://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Test::NoWarnings; >+use Test::More tests => 7; >+use Test::Mojo; >+use JSON; >+ >+use t::lib::TestBuilder; >+use t::lib::Mocks; >+ >+use Koha::Displays; >+use Koha::Database; >+ >+my $schema = Koha::Database->new->schema; >+my $builder = t::lib::TestBuilder->new; >+ >+my $t = Test::Mojo->new('Koha::REST::V1'); >+t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); >+ >+subtest 'config() tests' => sub { >+ >+ plan tests => 5; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 1 } # displays permission >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ t::lib::Mocks::mock_preference( 'UseDisplayModule', 1 ); >+ >+ ## Authorized user tests >+ # No displays, so empty array should be returned >+ $t->get_ok("//$userid:$password@/api/v1/displays/config") >+ ->status_is(200) >+ ->json_is( { settings => { enabled => 1 } } ); >+ >+ # Unauthorized access >+ $t->get_ok("//$unauth_userid:$password@/api/v1/displays/config")->status_is(403); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'list() tests' => sub { >+ >+ plan tests => 20; >+ >+ $schema->storage->txn_begin; >+ >+ Koha::Displays->search->delete; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 1 } # displays permission >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ ## Authorized user tests >+ # No displays, so empty array should be returned >+ $t->get_ok("//$userid:$password@/api/v1/displays")->status_is(200)->json_is( [] ); >+ >+ my $display = $builder->build_object( { class => 'Koha::Displays' } ); >+ >+ # One display created, should get returned >+ $t->get_ok("//$userid:$password@/api/v1/displays")->status_is(200)->json_is( [ $display->to_api ] ); >+ >+ my $another_display = >+ $builder->build_object( >+ { class => 'Koha::Displays', value => { display_branch => $display->display_branch } } ); >+ my $display_with_another_branch = $builder->build_object( { class => 'Koha::Displays' } ); >+ >+ # Three displays created, they should all be returned >+ $t->get_ok("//$userid:$password@/api/v1/displays")->status_is(200)->json_is( >+ [ >+ $display->to_api, >+ $another_display->to_api, >+ $display_with_another_branch->to_api >+ ] >+ ); >+ >+ # Filtering works, two displays sharing display_branch >+ $t->get_ok( "//$userid:$password@/api/v1/displays?display_branch=" . $display->display_branch ) >+ ->status_is(200) >+ ->json_is( >+ [ >+ $display->to_api, >+ $another_display->to_api >+ ] >+ ); >+ >+ $t->get_ok( "//$userid:$password@/api/v1/displays?display_name=" . $display->display_name ) >+ ->status_is(200) >+ ->json_is( [ $display->to_api ] ); >+ >+ # Warn on unsupported query parameter >+ $t->get_ok("//$userid:$password@/api/v1/displays?display_blah=blah") >+ ->status_is(400) >+ ->json_is( [ { path => '/query/display_blah', message => 'Malformed query string' } ] ); >+ >+ # Unauthorized access >+ $t->get_ok("//$unauth_userid:$password@/api/v1/displays")->status_is(403); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'get() tests' => sub { >+ >+ plan tests => 8; >+ >+ $schema->storage->txn_begin; >+ >+ my $display = $builder->build_object( { class => 'Koha::Displays' } ); >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 1 } # displays permission >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ $t->get_ok( "//$userid:$password@/api/v1/displays/" . $display->display_id ) >+ ->status_is(200) >+ ->json_is( $display->to_api ); >+ >+ $t->get_ok( "//$unauth_userid:$password@/api/v1/displays/" . $display->display_id )->status_is(403); >+ >+ my $display_to_delete = $builder->build_object( { class => 'Koha::Displays' } ); >+ my $non_existent_id = $display_to_delete->display_id; >+ $display_to_delete->delete; >+ >+ $t->get_ok("//$userid:$password@/api/v1/displays/$non_existent_id") >+ ->status_is(404) >+ ->json_is( '/error' => 'Display not found' ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'add() tests' => sub { >+ >+ plan tests => 18; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 1 } # displays permission >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ my $itemtype = $builder->build_object( { class => 'Koha::ItemTypes' } ); >+ >+ my $display = { >+ display_name => "Test Display", >+ enabled => JSON::true, >+ display_return_over => "no", >+ start_date => "2024-01-01", >+ end_date => "2024-12-31", >+ display_location => "DISPLAY", >+ display_code => "TEST", >+ display_branch => "CPL", >+ display_itype => $itemtype->itemtype >+ }; >+ >+ # Unauthorized attempt to write >+ $t->post_ok( "//$unauth_userid:$password@/api/v1/displays" => json => $display )->status_is(403); >+ >+ # Authorized attempt to write invalid data >+ my $display_with_invalid_field = { >+ blah => "Display Blah", >+ display_name => "Test Display", >+ enabled => JSON::true, >+ display_return_over => "no" >+ }; >+ >+ $t->post_ok( "//$userid:$password@/api/v1/displays" => json => $display_with_invalid_field ) >+ ->status_is(400) >+ ->json_is( >+ "/errors" => [ >+ { >+ message => "Properties not allowed: blah.", >+ path => "/body" >+ } >+ ] >+ ); >+ >+ # Authorized attempt to write >+ my $display_id = >+ $t->post_ok( "//$userid:$password@/api/v1/displays" => json => $display ) >+ ->status_is( 201, 'REST3.2.1' ) >+ ->header_like( >+ Location => qr|^\/api\/v1\/displays/\d*|, >+ 'REST3.4.1' >+ ) >+ ->json_is( '/display_name' => $display->{display_name} ) >+ ->json_is( '/enabled' => $display->{enabled} ) >+ ->json_is( '/display_return_over' => $display->{display_return_over} ) >+ ->json_is( '/start_date' => $display->{start_date} ) >+ ->tx->res->json->{display_id}; >+ >+ # Authorized attempt to create with null id >+ $display->{display_id} = undef; >+ $t->post_ok( "//$userid:$password@/api/v1/displays" => json => $display )->status_is(400)->json_has('/errors'); >+ >+ # Authorized attempt to create with existing id >+ $display->{display_id} = $display_id; >+ $t->post_ok( "//$userid:$password@/api/v1/displays" => json => $display )->status_is(400)->json_is( >+ "/errors" => [ >+ { >+ message => "Read-only.", >+ path => "/body/display_id" >+ } >+ ] >+ ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'update() tests' => sub { >+ >+ plan tests => 15; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 1 } # displays permission >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ my $itemtype = $builder->build_object( { class => 'Koha::ItemTypes' } ); >+ >+ my $display_id = $builder->build_object( { class => 'Koha::Displays' } )->display_id; >+ >+ # Unauthorized attempt to update >+ $t->put_ok( "//$unauth_userid:$password@/api/v1/displays/$display_id" => json => >+ { display_name => 'New unauthorized name change' } )->status_is(403); >+ >+ # Attempt partial update on a PUT >+ my $display_with_missing_field = { >+ display_name => 'Updated Display', >+ enabled => JSON::false >+ }; >+ >+ $t->put_ok( "//$userid:$password@/api/v1/displays/$display_id" => json => $display_with_missing_field ) >+ ->status_is(400) >+ ->json_is( "/errors" => [ { message => "Missing property.", path => "/body/display_return_over" } ] ); >+ >+ # Full object update on PUT >+ my $display_with_updated_field = { >+ display_name => "Updated Display Name", >+ enabled => JSON::false, >+ display_return_over => "yes - any library", >+ start_date => "2024-02-01", >+ end_date => "2024-11-30", >+ display_location => "NEWDISPLAY", >+ display_code => "UPDATED", >+ display_branch => "CPL", >+ display_itype => $itemtype->itemtype >+ }; >+ >+ $t->put_ok( "//$userid:$password@/api/v1/displays/$display_id" => json => $display_with_updated_field ) >+ ->status_is(200) >+ ->json_is( '/display_name' => 'Updated Display Name' ); >+ >+ # Authorized attempt to write invalid data >+ my $display_with_invalid_field = { >+ blah => "Display Blah", >+ display_name => "Test Display", >+ enabled => JSON::true, >+ display_return_over => "no" >+ }; >+ >+ $t->put_ok( "//$userid:$password@/api/v1/displays/$display_id" => json => $display_with_invalid_field ) >+ ->status_is(400) >+ ->json_is( >+ "/errors" => [ >+ { >+ message => "Properties not allowed: blah.", >+ path => "/body" >+ } >+ ] >+ ); >+ >+ my $display_to_delete = $builder->build_object( { class => 'Koha::Displays' } ); >+ my $non_existent_id = $display_to_delete->display_id; >+ $display_to_delete->delete; >+ >+ $t->put_ok( "//$userid:$password@/api/v1/displays/$non_existent_id" => json => $display_with_updated_field ) >+ ->status_is(404); >+ >+ # Wrong method (POST) >+ $display_with_updated_field->{display_id} = 2; >+ >+ $t->post_ok( "//$userid:$password@/api/v1/displays/$display_id" => json => $display_with_updated_field ) >+ ->status_is(404); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'delete() tests' => sub { >+ >+ plan tests => 7; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 1 } # displays permission >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ my $display_id = $builder->build_object( { class => 'Koha::Displays' } )->display_id; >+ >+ # Unauthorized attempt to delete >+ $t->delete_ok("//$unauth_userid:$password@/api/v1/displays/$display_id")->status_is(403); >+ >+ $t->delete_ok("//$userid:$password@/api/v1/displays/$display_id") >+ ->status_is( 204, 'REST3.2.4' ) >+ ->content_is( '', 'REST3.3.4' ); >+ >+ $t->delete_ok("//$userid:$password@/api/v1/displays/$display_id")->status_is(404); >+ >+ $schema->storage->txn_rollback; >+}; >diff --git a/t/db_dependent/api/v1/items.t b/t/db_dependent/api/v1/items.t >index de21cdedf0a..8bd59a90d45 100755 >--- a/t/db_dependent/api/v1/items.t >+++ b/t/db_dependent/api/v1/items.t >@@ -19,8 +19,8 @@ > > use Modern::Perl; > >+use Test::More tests => 7; > use Test::NoWarnings; >-use Test::More tests => 6; > use Test::MockModule; > use Test::Mojo; > use Test::Warn; >@@ -626,3 +626,88 @@ subtest 'pickup_locations() tests' => sub { > > $schema->storage->txn_rollback; > }; >+ >+subtest 'effective display fields tests' => sub { >+ >+ plan tests => 12; >+ >+ $schema->storage->txn_begin; >+ >+ t::lib::Mocks::mock_preference( 'UseDisplayModule', 1 ); >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 1 } >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ # Create an item with specific location, collection code, homebranch, and holdingbranch >+ my $item = $builder->build_sample_item( >+ { >+ location => 'SHELF', >+ ccode => 'COLL1', >+ homebranch => $librarian->branchcode, >+ holdingbranch => $librarian->branchcode, >+ } >+ ); >+ >+ # Test without display - should return regular values >+ $t->get_ok( "//$userid:$password@/api/v1/items/" . $item->id ) >+ ->status_is(200) >+ ->json_is( '/effective_location' => 'SHELF' ) >+ ->json_is( '/effective_collection_code' => 'COLL1' ) >+ ->json_is( '/effective_home_library_id' => $librarian->branchcode ) >+ ->json_is( '/effective_holding_library_id' => $librarian->branchcode ); >+ >+ # Create a display with different values >+ my $display_library = $builder->build_object( { class => 'Koha::Libraries' } ); >+ my $display = $builder->build_object( >+ { >+ class => 'Koha::Displays', >+ value => { >+ display_location => 'DISPLAY', >+ display_code => 'DISPLAYCOLL', >+ display_branch => $display_library->branchcode, >+ display_holding_branch => $display_library->branchcode, >+ enabled => 1, >+ start_date => undef, >+ end_date => undef, >+ } >+ } >+ ); >+ >+ # Add item to display >+ my $display_item = $builder->build_object( >+ { >+ class => 'Koha::DisplayItems', >+ value => { >+ display_id => $display->display_id, >+ itemnumber => $item->itemnumber, >+ biblionumber => $item->biblionumber, >+ } >+ } >+ ); >+ >+ # Test with display - should return display values >+ $t->get_ok( "//$userid:$password@/api/v1/items/" . $item->id ) >+ ->status_is(200) >+ ->json_is( '/effective_location' => 'DISPLAY' ) >+ ->json_is( '/effective_collection_code' => 'DISPLAYCOLL' ) >+ ->json_is( '/effective_home_library_id' => $display_library->branchcode ) >+ ->json_is( '/effective_holding_library_id' => $display_library->branchcode ); >+ >+ $schema->storage->txn_rollback; >+}; >-- >2.53.0 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 14962
:
190216
|
190217
|
190218
|
190219
|
190220
|
190221
|
190222
|
190223
|
190224
|
190225
|
190226
|
190227
|
190228
|
190229
|
190230
|
190231
|
190232
|
190571
|
190572
|
193719
|
193720
|
193721
|
193722
|
193723
|
193724
|
193725
|
193726
|
193727
|
193728
|
193729
|
193730
|
193731
|
193732
|
193733
|
193734
|
193735
|
193736
|
193737
|
193738
|
193739
|
193740
|
193741
|
193742
|
193743
|
193744
|
193745
|
193746
|
193747
|
193748
|
193749
|
193750
|
193751
|
193752
|
193753
|
193754
|
193755
|
194285
|
194286
|
194287
|
194288
|
194289
|
194290
|
194291
|
194292
|
194293
|
194294
|
194295
|
194296
|
194297
|
194298
|
194299
|
194300
|
194301
|
194302
|
194303
|
194304
|
194305
|
194306
|
194307
|
194308
|
194309
|
194310
|
194311
|
194312
|
194313
|
194314
|
194315
|
194316
|
194317
|
194318
|
194320
|
194321
|
194322