From a9b608bf0f7fe686a7aea7b0bcd28b771aaf25d6 Mon Sep 17 00:00:00 2001 From: Matt Blenkinsop Date: Thu, 5 Jun 2025 13:40:25 +0100 Subject: [PATCH] Bug 40082: Add unit tests Test plan: 1) Choose an existing patron and note their first and second names as well as their date of birth 2) In the interface, create a new patron and enter all of those details to be the same as your patron from step 1 3) Submit the form, you will get a warning saying that there is a duplicate 4) In the OPAC, self register a new patron using the same details again 5) The form should submit with no error or warning 6) Apply patch, yarn api:bundle and restart_all 7) Repeat steps 2 - 5 8) Step 3 should still have the same behaviour 9) Step 5 should now show a warning at the top to say that there is a duplicate, please contact a member of staff 10) Find the 'PatronDuplicateMatchingAddFields' syspref and select an extra field there, ensuring that your patron from step 1 has data for that field 11) In either the interface or the OPAC, register a new patron but this time change the data of the new field you added in step 10 12) No warning should show for a duplicate patron 13) prove t/db_dependent/api/v1/patrons.t 14) prove t/db_dependent/Koha/Patrons.t Signed-off-by: Andrew Fuerste Henry --- t/db_dependent/Koha/Patrons.t | 34 +++++++++++++++++++++++++++++- t/db_dependent/api/v1/patrons.t | 37 +++++++++++++++++++-------------- 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/t/db_dependent/Koha/Patrons.t b/t/db_dependent/Koha/Patrons.t index aa4d1903232..204c8c3d60f 100755 --- a/t/db_dependent/Koha/Patrons.t +++ b/t/db_dependent/Koha/Patrons.t @@ -20,7 +20,7 @@ use Modern::Perl; use Test::NoWarnings; -use Test::More tests => 47; +use Test::More tests => 48; use Test::Warn; use Test::Exception; use Test::MockModule; @@ -3623,4 +3623,36 @@ subtest 'find_by_identifier() tests' => sub { $schema->storage->txn_rollback; }; +subtest 'check_for_existing_matches' => sub { + plan tests => 5; + + $schema->storage->txn_begin; + + my $patron = $builder->build_object( + { class => 'Koha::Patrons', value => { firstname => 'John', surname => 'Smith', dateofbirth => '1980-01-01' } } + ); + + t::lib::Mocks::mock_preference( 'PatronDuplicateMatchingAddFields', 'dateofbirth|firstname|surname' ); + + my $match_result = Koha::Patrons->check_for_existing_matches( + { firstname => 'John', surname => 'Smith', dateofbirth => '1980-01-01' } ); + is( $match_result->{duplicate_found}, 1, 'Duplicate found' ); + is( $match_result->{matching_patrons}->next->borrowernumber, $patron->borrowernumber, 'Matching patron' ); + + t::lib::Mocks::mock_preference( 'PatronDuplicateMatchingAddFields', 'dateofbirth|firstname|surname|city' ); + $match_result = Koha::Patrons->check_for_existing_matches( + { firstname => 'John', surname => 'Smith', dateofbirth => '1980-01-01', city => '' } ); + is( $match_result->{duplicate_found}, 1, 'Duplicate found despite missing data for the city field' ); + is( + $match_result->{matching_patrons}->next->borrowernumber, $patron->borrowernumber, + 'Patron match ignores undefined field and still identifies the match' + ); + + $match_result = Koha::Patrons->check_for_existing_matches( + { firstname => 'John', surname => 'Smith', dateofbirth => '1980-01-01', city => 'Sandwich, Kent' } ); + is( $match_result->{duplicate_found}, 0, 'No duplicate found' ); + + $schema->storage->txn_rollback; +}; + $schema->storage->txn_rollback; diff --git a/t/db_dependent/api/v1/patrons.t b/t/db_dependent/api/v1/patrons.t index aff93e092b0..82e579af404 100755 --- a/t/db_dependent/api/v1/patrons.t +++ b/t/db_dependent/api/v1/patrons.t @@ -428,7 +428,7 @@ subtest 'add() tests' => sub { $schema->storage->txn_rollback; subtest 'librarian access tests' => sub { - plan tests => 37; + plan tests => 40; $schema->storage->txn_begin; @@ -459,6 +459,14 @@ subtest 'add() tests' => sub { delete $newpatron->{expired}; delete $newpatron->{anonymized}; + my $password = 'thePassword123'; + $librarian->set_password( { password => $password, skip_validation => 1 } ); + my $userid = $librarian->userid; + t::lib::Mocks::mock_preference( 'PatronDuplicateMatchingAddFields', 'firstname|surname|dateofbirth' ); + + $t->post_ok( "//$userid:$password@/api/v1/patrons" => json => $newpatron )->status_is(409) + ->json_is( '/error' => "A patron record matching these details already exists" ); + # Create a library just to make sure its ID doesn't exist on the DB my $library_to_delete = $builder->build_object( { class => 'Koha::Libraries' } ); my $deleted_library_id = $library_to_delete->id; @@ -466,23 +474,20 @@ subtest 'add() tests' => sub { # Delete library $library_to_delete->delete; - my $password = 'thePassword123'; - $librarian->set_password( { password => $password, skip_validation => 1 } ); - my $userid = $librarian->userid; - $newpatron->{library_id} = $deleted_library_id; # Test duplicate userid constraint - $t->post_ok( "//$userid:$password@/api/v1/patrons" => json => $newpatron )->status_is(400) - ->json_is( '/error' => "Problem with " . $newpatron->{userid} ); + $t->post_ok( "//$userid:$password@/api/v1/patrons" => { 'x-confirm-not-duplicate' => 1 } => json => $newpatron ) + ->status_is(400)->json_is( '/error' => "Problem with " . $newpatron->{userid} ); $newpatron->{library_id} = $patron->branchcode; # Test duplicate cardnumber constraint $newpatron->{userid} = undef; # force regeneration warning_like { - $t->post_ok( "//$userid:$password@/api/v1/patrons" => json => $newpatron )->status_is(409) - ->json_has( '/error', 'Fails when trying to POST duplicate cardnumber' ) + $t->post_ok( + "//$userid:$password@/api/v1/patrons" => { 'x-confirm-not-duplicate' => 1 } => json => $newpatron ) + ->status_is(409)->json_has( '/error', 'Fails when trying to POST duplicate cardnumber' ) ->json_like( '/conflict' => qr/(borrowers\.)?cardnumber/ ); } qr/DBD::mysql::st execute failed: Duplicate entry '(.*?)' for key '(borrowers\.)?cardnumber'/; @@ -496,8 +501,8 @@ subtest 'add() tests' => sub { $newpatron->{category_id} = $deleted_category_id; # Test invalid patron category - $t->post_ok( "//$userid:$password@/api/v1/patrons" => json => $newpatron )->status_is(400) - ->json_is( '/error' => "Given category_id does not exist" ); + $t->post_ok( "//$userid:$password@/api/v1/patrons" => { 'x-confirm-not-duplicate' => 1 } => json => $newpatron ) + ->status_is(400)->json_is( '/error' => "Given category_id does not exist" ); $newpatron->{category_id} = $patron->categorycode; $newpatron->{falseproperty} = "Non existent property"; @@ -543,8 +548,8 @@ subtest 'add() tests' => sub { $newpatron->{userid} = "newuserid"; $letter_enqueued = 0; t::lib::Mocks::mock_preference( 'AutoEmailNewUser', 1 ); - $t->post_ok( - "//$userid:$password@/api/v1/patrons" => { 'x-koha-override' => 'welcome_no' } => json => $newpatron ) + $t->post_ok( "//$userid:$password@/api/v1/patrons" => + { 'x-koha-override' => 'welcome_no', 'x-confirm-not-duplicate' => 1 } => json => $newpatron ) ->status_is( 201, 'Patron created successfully with welcome_no override' ); is( $letter_enqueued, 0, "No welcome notice sent due to welcome_no override" ); @@ -553,7 +558,7 @@ subtest 'add() tests' => sub { $newpatron->{userid} = "newuserid2"; $letter_enqueued = 0; t::lib::Mocks::mock_preference( 'AutoEmailNewUser', 1 ); - $t->post_ok( "//$userid:$password@/api/v1/patrons" => json => $newpatron ) + $t->post_ok( "//$userid:$password@/api/v1/patrons" => { 'x-confirm-not-duplicate' => 1 } => json => $newpatron ) ->status_is( 201, 'Patron created successfully with AutoEmailNewUser = 1' ); is( $letter_enqueued, 1, "Welcome notice sent due to AutoEmailNewUser = 1" ); @@ -563,7 +568,7 @@ subtest 'add() tests' => sub { $newpatron->{userid} = "newuserid3"; $letter_enqueued = 0; t::lib::Mocks::mock_preference( 'AutoEmailNewUser', 1 ); - $t->post_ok( "//$userid:$password@/api/v1/patrons" => json => $newpatron ) + $t->post_ok( "//$userid:$password@/api/v1/patrons" => { 'x-confirm-not-duplicate' => 1 } => json => $newpatron ) ->status_is( 201, 'Patron created successfully but email will not be sent' ); is( $letter_enqueued, 0, "No welcome notice sent due to missing email address" ); $mocked_patron->unmock('notice_email_address'); @@ -574,7 +579,7 @@ subtest 'add() tests' => sub { $letter_enqueued = 0; $message_has_content = 0; t::lib::Mocks::mock_preference( 'AutoEmailNewUser', 1 ); - $t->post_ok( "//$userid:$password@/api/v1/patrons" => json => $newpatron ) + $t->post_ok( "//$userid:$password@/api/v1/patrons" => { 'x-confirm-not-duplicate' => 1 } => json => $newpatron ) ->status_is( 201, 'Patron created successfully with AutoEmailNewUser = 1 and empty WELCOME notice' ); is( $letter_enqueued, 0, "Welcome not sent as it would be empty" ); $message_has_content = 1; -- 2.39.5