Database-Join
view release on metacpan or search on metacpan
t/function.t view on Meta::CPAN
# other public methods are tested on real Database::Join objects built with
# inline MinimalDA mocks.
#
# MinimalDA is an in-process stub that inherits from Database::Abstraction
# purely to satisfy the isa() check. Its selectall_arrayref, columns(), and
# schema() are configurable at construction time so each subtest can control
# exactly what the component database returns.
#
# Test::Mockingbird is used to mock List::Util::max (used by updated()) and
# to spy on internal call chains. Test::Returns validates return-type
# contracts. Test::Memory::Cycle verifies no circular references exist.
# ---------------------------------------------------------------------------
use Test::Most;
use Test::Mockingbird;
use Test::Returns;
use Test::Memory::Cycle;
use Readonly;
use Scalar::Util qw(blessed refaddr);
BEGIN {
eval { require Database::Abstraction };
plan skip_all => 'Database::Abstraction required' if $@;
plan tests => 119;
use_ok('Database::Join');
}
# ---------------------------------------------------------------------------
# Constants -- prevent magic strings from appearing in the body below
# ---------------------------------------------------------------------------
Readonly::Scalar my $JC => 'entry'; # canonical join column
Readonly::Scalar my $JC_ALT => 'statecode'; # alternative join_column name
Readonly::Scalar my $JC_ALIAS => 'id'; # local alias used by DB 1 in join_map tests
Readonly::Scalar my $COL_A => 'name';
Readonly::Scalar my $COL_B => 'score';
Readonly::Scalar my $COL_C => 'tier';
Readonly::Scalar my $TS_A => 1_000_000;
Readonly::Scalar my $TS_B => 2_000_000;
# ---------------------------------------------------------------------------
# MinimalDA: inline Database::Abstraction subclass used as a mock component.
# Configurable columns/rows/schema/updated at construction time.
# ---------------------------------------------------------------------------
{
package MinimalDA;
use parent -norequire, 'Database::Abstraction';
sub new {
my ($class, %args) = @_;
return bless {
id => $args{id} // 'entry',
_cols => $args{cols} // ['entry'],
_rows => $args{rows} // [],
_schema => $args{schema} // {},
_ts => $args{updated} // 1,
}, $class;
}
sub columns { return $_[0]->{_cols} }
sub schema { return $_[0]->{_schema} }
sub updated { return $_[0]->{_ts} }
sub set_logger { $_[0]->{_logger} = $_[1]; return $_[0] }
sub selectall_arrayref {
my ($self, $criteria) = @_;
my @rows = @{ $self->{_rows} };
# Simple equality filter for tests that need it
for my $col (keys %{ $criteria // {} }) {
my $val = $criteria->{$col};
next if ref $val;
@rows = grep { defined $_->{$col} && $_->{$col} eq $val } @rows;
}
return \@rows;
}
sub DESTROY {}
}
# ---------------------------------------------------------------------------
# WhiteBox shim: subclass that exposes protected methods for unit testing.
# Protected subs check caller; calling them through a subclass passes the check.
# ---------------------------------------------------------------------------
{
package Database::Join::WhiteBox;
use parent -norequire, 'Database::Join';
sub expose_msg { shift; Database::Join::_msg(@_) }
sub expose_merge_criteria { shift; Database::Join::_merge_criteria(@_) }
sub expose_parse_query_args { my $self = shift; return $self->_parse_query_args(@_) }
sub expose_partition { my $self = shift; return $self->_partition_criteria(@_) }
sub expose_fetch_indexed { my $self = shift; return $self->_fetch_indexed(@_) }
sub expose_joined_query { my $self = shift; return $self->_joined_query(@_) }
sub expose_err { my $self = shift; return $self->_err(@_) }
sub expose_build_col_index { my $self = shift; return $self->_build_col_index(@_) }
}
# Convenience builder for a bare WhiteBox skeleton (no databases needed for
# pure-helper tests that don't touch _dbs).
sub _bare_whitebox {
my (%extra) = @_;
return bless {
_join_col => $JC,
_join_type => 'left',
_join_map => {},
_filters => {},
_dbs => [],
_col_db => {},
_db_cols => [],
_removed_cols => {},
_col_cache => undef,
_schema_cache => undef,
_autoload_pk => $JC,
_logger => undef,
_i18n => undef,
%extra,
}, 'Database::Join::WhiteBox';
}
# Convenience: build a standard two-DB join for integration-style subtests
sub _make_join {
my (%opts) = @_;
my $db_a = MinimalDA->new(
cols => [$JC, $COL_A, $COL_C],
rows => [
( run in 1.286 second using v1.01-cache-2.11-cpan-c221a9de4ec )