Bug 41918: (QA follow-up) Add unit tests for Koha::Reports->running

Adds a 'running' subtest covering the new method:

- Returns a Koha::Reports resultset (object identity, not opaque ids).
- Returns an empty resultset when nothing is in flight, and when the
  user_id / report_id filter does not match any process.
- Parses saved_sql.id markers out of synthetic processlist rows and
  resolves them to real Koha::Report objects, ignoring rows without
  the marker.
- Sends parameterised LIKE binds for user_id (proves the WHERE clause
  is built without string interpolation).
- Falls back to an empty resultset and does not propagate the
  exception when the underlying selectall_arrayref call dies, eg.
  when the database user lacks PROCESS privilege.

To make the failure path mockable without trying to mock DBI::db
(which is not loadable as a separate file), the dbh selectall is
extracted into a tiny Koha::Reports::_processlist_rows helper so
Test::MockModule can replace it cleanly. The helper is otherwise
behaviour-preserving.

Test plan:
1) prove t/db_dependent/Koha/Reports.t -> 11 tests, all pass.
2) Existing prep_report subtest still passes.

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This commit is contained in:
Martin Renvoize 2026-04-27 17:47:57 +01:00 committed by Lucas Gass
parent 081e895ccb
commit c8f0f064d6
No known key found for this signature in database
GPG Key ID: 60B447398B0D36CA
2 changed files with 70 additions and 3 deletions

View File

@ -79,9 +79,8 @@ sub running {
my $sql = 'SELECT info FROM information_schema.processlist WHERE ' . join( ' AND ', @where );
my $schema = Koha::Database->new->schema;
my $rows;
eval { $rows = $schema->storage->dbh->selectall_arrayref( $sql, { Slice => {} }, @binds ); };
eval { $rows = $class->_processlist_rows( $sql, @binds ); };
if ($@) {
Koha::Logger->get->warn("Koha::Reports->running: unable to query information_schema.processlist: $@");
$rows = [];
@ -95,6 +94,11 @@ sub running {
return $class->search( { id => { -in => [ keys %report_ids ] } } );
}
sub _processlist_rows {
my ( $class, $sql, @binds ) = @_;
return Koha::Database->new->schema->storage->dbh->selectall_arrayref( $sql, { Slice => {} }, @binds );
}
=head3 _type
Returns name of corresponding DBIC resultset

View File

@ -17,8 +17,9 @@
use Modern::Perl;
use Test::MockModule;
use Test::NoWarnings;
use Test::More tests => 10;
use Test::More tests => 11;
use Koha::Report;
use Koha::Reports;
@ -213,4 +214,66 @@ subtest 'apply_execution_time_limit' => sub {
);
};
subtest 'running' => sub {
plan tests => 7;
my $running = Koha::Reports->running;
isa_ok( $running, 'Koha::Reports', 'running() returns a Koha::Reports resultset' );
# No saved-report SQL is in flight in the test process; processlist may show
# this very test connection but its current statement carries no saved_sql.id
# marker, so the result must be empty.
is( Koha::Reports->running->count, 0, 'no running reports => empty resultset' );
is(
Koha::Reports->running( { user_id => 999_999_999 } )->count, 0,
'unknown user_id => empty resultset'
);
is(
Koha::Reports->running( { report_id => 999_999_999 } )->count, 0,
'unknown report_id => empty resultset'
);
# Capture the SQL bindings and feed synthetic rows back; this exercises the
# WHERE-clause assembly and the saved_sql.id parser without relying on a
# real long-running query in the database.
my $reports_mock = Test::MockModule->new('Koha::Reports');
my @captured_binds;
my $synthetic_rows = [];
$reports_mock->mock(
'_processlist_rows',
sub {
my ( $class, $sql, @binds ) = @_;
push @captured_binds, [@binds];
return $synthetic_rows;
}
);
my $report_a = $builder->build_object( { class => 'Koha::Reports' } );
my $report_b = $builder->build_object( { class => 'Koha::Reports' } );
my ( $a_id, $b_id ) = ( $report_a->id, $report_b->id );
$synthetic_rows = [
{ info => "SELECT 1 /* { saved_sql.id: $a_id } { user_id: 17 } */" },
{ info => "SELECT 1 /* { saved_sql.id: $b_id } { user_id: 18 } */" },
{ info => "SELECT 1 /* no marker, should be ignored */" },
];
is( Koha::Reports->running->count, 2, 'parses saved_sql.id markers and returns matching reports' );
@captured_binds = ();
Koha::Reports->running( { user_id => 17 } );
is_deeply(
$captured_binds[0],
[ 'Sleep', '%saved_sql.id:%', '%{ user_id: 17 }%' ],
'user_id contributes a parameterised LIKE bind'
);
# Graceful degradation when the underlying DB call dies (eg. PROCESS denied).
$reports_mock->mock( '_processlist_rows', sub { die "Access denied for user\n"; } );
is(
Koha::Reports->running->count, 0,
'DB failure => empty resultset (caller is not aborted)'
);
};
$schema->storage->txn_rollback;