Bug 33782: Add unit tests for Koha::Auth::Client::OAuth
This patch adds comprehensive unit tests for the OAuth authentication client, specifically covering the _get_data_and_patron method which handles JWT token processing and UserInfo endpoint integration. Test plan: 1. Apply patch 2. Run: $ ktd --shell k$ prove t/db_dependent/Koha/Auth/Client/OAuth.t => SUCCESS: Tests pass! 3. Tests cover: - JWT id_token processing and patron matching - UserInfo endpoint calls and data merging - Non-existent user scenarios - Combined JWT + UserInfo data sources 4. Sign off :-D Signed-off-by: David Nind <david@davidnind.com> Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com> Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This commit is contained in:
parent
b16ff5a8a0
commit
4ff2eb3469
|
|
@ -0,0 +1,257 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# Copyright 2025 Koha Development Team
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses>.
|
||||
|
||||
use Modern::Perl;
|
||||
|
||||
use Test::More tests => 4;
|
||||
use Test::MockModule;
|
||||
use Test::NoWarnings;
|
||||
|
||||
use JSON qw(encode_json);
|
||||
use MIME::Base64 qw(encode_base64url);
|
||||
|
||||
use Koha::Auth::Client::OAuth;
|
||||
use Koha::Database;
|
||||
|
||||
use t::lib::TestBuilder;
|
||||
|
||||
my $schema = Koha::Database->new->schema;
|
||||
my $builder = t::lib::TestBuilder->new;
|
||||
|
||||
subtest '_get_data_and_patron() with id_token tests' => sub {
|
||||
plan tests => 4;
|
||||
|
||||
$schema->storage->txn_begin;
|
||||
|
||||
my $client = Koha::Auth::Client::OAuth->new;
|
||||
|
||||
# Create test patron
|
||||
my $patron = $builder->build_object(
|
||||
{
|
||||
class => 'Koha::Patrons',
|
||||
value => { email => 'test@example.com' }
|
||||
}
|
||||
);
|
||||
|
||||
# Create provider with email matchpoint
|
||||
my $provider = $builder->build_object(
|
||||
{
|
||||
class => 'Koha::Auth::Identity::Providers',
|
||||
value => {
|
||||
matchpoint => 'email',
|
||||
mapping => encode_json(
|
||||
{
|
||||
email => 'mail',
|
||||
firstname => 'given_name',
|
||||
surname => 'family_name'
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
# Create JWT token with user data
|
||||
my $claims = {
|
||||
mail => 'test@example.com',
|
||||
given_name => 'John',
|
||||
family_name => 'Doe'
|
||||
};
|
||||
|
||||
my $id_token = 'header.' . encode_base64url( encode_json($claims) ) . '.signature';
|
||||
|
||||
my $data = { id_token => $id_token };
|
||||
my $config = {};
|
||||
|
||||
# Test the method
|
||||
my ( $mapped_data, $found_patron ) = $client->_get_data_and_patron(
|
||||
{
|
||||
provider => $provider,
|
||||
data => $data,
|
||||
config => $config
|
||||
}
|
||||
);
|
||||
|
||||
# Verify results
|
||||
is( $mapped_data->{email}, 'test@example.com', 'Email mapped correctly' );
|
||||
is( $mapped_data->{firstname}, 'John', 'First name mapped correctly' );
|
||||
is( $mapped_data->{surname}, 'Doe', 'Surname mapped correctly' );
|
||||
is( $found_patron->id, $patron->id, 'Patron found by email matchpoint' );
|
||||
|
||||
$schema->storage->txn_rollback;
|
||||
};
|
||||
|
||||
subtest '_get_data_and_patron() with userinfo_url tests' => sub {
|
||||
plan tests => 5;
|
||||
|
||||
$schema->storage->txn_begin;
|
||||
|
||||
my $client = Koha::Auth::Client::OAuth->new;
|
||||
|
||||
# Create test patron
|
||||
my $patron = $builder->build_object(
|
||||
{
|
||||
class => 'Koha::Patrons',
|
||||
value => { email => 'userinfo@example.com' }
|
||||
}
|
||||
);
|
||||
|
||||
# Create provider with email matchpoint
|
||||
my $provider = $builder->build_object(
|
||||
{
|
||||
class => 'Koha::Auth::Identity::Providers',
|
||||
value => {
|
||||
matchpoint => 'email',
|
||||
mapping => encode_json(
|
||||
{
|
||||
email => 'email',
|
||||
firstname => 'first_name'
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
# Mock UserAgent for userinfo endpoint
|
||||
my $ua_mock = Test::MockModule->new('Mojo::UserAgent');
|
||||
my $tx_mock = Test::MockModule->new('Mojo::Transaction::HTTP');
|
||||
my $res_mock = Test::MockModule->new('Mojo::Message::Response');
|
||||
my $headers_mock = Test::MockModule->new('Mojo::Headers');
|
||||
|
||||
$headers_mock->mock( 'content_type', sub { 'application/json' } );
|
||||
$res_mock->mock( 'code', sub { '200' } );
|
||||
$res_mock->mock(
|
||||
'json',
|
||||
sub {
|
||||
return {
|
||||
email => 'userinfo@example.com',
|
||||
first_name => 'Jane'
|
||||
};
|
||||
}
|
||||
);
|
||||
$res_mock->mock(
|
||||
'headers',
|
||||
sub {
|
||||
my $headers = {};
|
||||
bless $headers, 'Mojo::Headers';
|
||||
return $headers;
|
||||
}
|
||||
);
|
||||
|
||||
$tx_mock->mock(
|
||||
'res',
|
||||
sub {
|
||||
my $res = {};
|
||||
bless $res, 'Mojo::Message::Response';
|
||||
return $res;
|
||||
}
|
||||
);
|
||||
|
||||
$ua_mock->mock(
|
||||
'get',
|
||||
sub {
|
||||
my $tx = {};
|
||||
bless $tx, 'Mojo::Transaction::HTTP';
|
||||
return $tx;
|
||||
}
|
||||
);
|
||||
|
||||
my $data = { access_token => 'test_token' };
|
||||
my $config = { userinfo_url => 'https://provider.com/userinfo' };
|
||||
|
||||
# Test the method
|
||||
my ( $mapped_data, $found_patron ) = $client->_get_data_and_patron(
|
||||
{
|
||||
provider => $provider,
|
||||
data => $data,
|
||||
config => $config
|
||||
}
|
||||
);
|
||||
|
||||
# Verify results
|
||||
is( $mapped_data->{email}, 'userinfo@example.com', 'Email mapped from userinfo' );
|
||||
is( $mapped_data->{firstname}, 'Jane', 'First name mapped from userinfo' );
|
||||
is( $found_patron->id, $patron->id, 'Patron found by email from userinfo' );
|
||||
|
||||
# Test with both id_token and userinfo (patron should be found from id_token first)
|
||||
my $id_token_claims = { email => 'token@example.com' };
|
||||
my $id_token = 'header.' . encode_base64url( encode_json($id_token_claims) ) . '.signature';
|
||||
|
||||
my $token_patron = $builder->build_object(
|
||||
{
|
||||
class => 'Koha::Patrons',
|
||||
value => { email => 'token@example.com' }
|
||||
}
|
||||
);
|
||||
|
||||
$data->{id_token} = $id_token;
|
||||
|
||||
( $mapped_data, $found_patron ) = $client->_get_data_and_patron(
|
||||
{
|
||||
provider => $provider,
|
||||
data => $data,
|
||||
config => $config
|
||||
}
|
||||
);
|
||||
|
||||
is( $found_patron->id, $token_patron->id, 'Patron found from id_token takes precedence' );
|
||||
is( $mapped_data->{email}, 'userinfo@example.com', 'But userinfo data still merged' );
|
||||
|
||||
$schema->storage->txn_rollback;
|
||||
};
|
||||
|
||||
subtest '_get_data_and_patron() no patron found tests' => sub {
|
||||
plan tests => 2;
|
||||
|
||||
$schema->storage->txn_begin;
|
||||
|
||||
my $client = Koha::Auth::Client::OAuth->new;
|
||||
|
||||
# Create provider
|
||||
my $provider = $builder->build_object(
|
||||
{
|
||||
class => 'Koha::Auth::Identity::Providers',
|
||||
value => {
|
||||
matchpoint => 'email',
|
||||
mapping => encode_json( { email => 'mail' } )
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
# Create token for non-existent user
|
||||
my $claims = { mail => 'nonexistent@example.com' };
|
||||
my $id_token = 'header.' . encode_base64url( encode_json($claims) ) . '.signature';
|
||||
|
||||
my $data = { id_token => $id_token };
|
||||
my $config = {};
|
||||
|
||||
# Test the method
|
||||
my ( $mapped_data, $found_patron ) = $client->_get_data_and_patron(
|
||||
{
|
||||
provider => $provider,
|
||||
data => $data,
|
||||
config => $config
|
||||
}
|
||||
);
|
||||
|
||||
# Verify results
|
||||
is( $mapped_data->{email}, 'nonexistent@example.com', 'Email mapped correctly' );
|
||||
is( $found_patron, undef, 'No patron found for non-existent email' );
|
||||
|
||||
$schema->storage->txn_rollback;
|
||||
};
|
||||
Loading…
Reference in New Issue