DBIO
view release on metacpan or search on metacpan
t/test/15_async_orchestration.t view on Meta::CPAN
sub acquire_txn { Test::SyncFuture->done('CONN') }
sub release { push @{ $_[0]->{released} }, $_[1]; 1 }
sub available { $_[0]->{available} }
sub shutdown { $_[0]->{available} = 0 }
}
# --- The fake transport backend --------------------------------------------
# A concrete DBIO::Storage::Async that overrides ONLY the seam hooks, all
# resolving immediately. Every query the orchestration emits is captured, and
# the rows the transport "returns" are settable per call via {next_rows}.
{
package Test::SyncBackend;
use base 'DBIO::Storage::Async';
sub new {
my ($class, $schema) = @_;
my $self = $class->SUPER::new($schema);
$self->{captured} = [];
$self->{next_rows} = [];
return $self;
}
# seam: Future implementation
sub future_class { 'Test::SyncFuture' }
# seam: SQL maker
sub sql_maker_class { 'DBIO::SQLMaker' }
sub _sql_maker_args { (quote_char => '"', name_sep => '.') }
# seam: SQL shaping (identity transform, PG-style RETURNING)
sub _transform_sql { $_[1] }
sub _post_insert_sql { ' RETURNING *' }
# seam: pool
sub pool { $_[0]->{pool} ||= Test::SyncPool->new }
# seam: query transport -- capture and resolve immediately
sub _query_async {
my ($self, $sql, $bind) = @_;
push @{ $self->{captured} }, { sql => $sql, bind => $bind, pinned => 0 };
return $self->future_class->done(@{ $self->{next_rows} });
}
sub _query_async_pinned {
my ($self, $conn, $sql, $bind) = @_;
push @{ $self->{captured} }, { sql => $sql, bind => $bind, pinned => 1, conn => $conn };
return $self->future_class->done(@{ $self->{next_rows} });
}
# seam: pipeline bracketing
sub _pipeline_enter { push @{ $_[0]->{captured} }, { sql => 'PIPELINE_ENTER' }; 1 }
sub _pipeline_exit { push @{ $_[0]->{captured} }, { sql => 'PIPELINE_EXIT' }; 1 }
sub _pipeline_sync { push @{ $_[0]->{captured} }, { sql => 'PIPELINE_SYNC' }; $_[0]->future_class->done }
# test helper
sub _last_sql { $_[0]->{captured}[-1]{sql} }
}
sub new_backend {
my $schema = DBIO::Test->init_schema;
# keep the schema alive for the caller (backend weakens its ref)
my $backend = Test::SyncBackend->new($schema);
return ($backend, $schema);
}
# ---------------------------------------------------------------------------
# connect_info normalization
# ---------------------------------------------------------------------------
{
my ($backend) = new_backend();
my $ci = [ { host => 'localhost', dbname => 'test', pool_size => 3 }, { RaiseError => 1 } ];
is_deeply $backend->connect_info($ci), $ci,
'connect_info returns the stored raw connect info';
is $backend->{_pool_size}, 3, 'pool_size extracted from conninfo';
is_deeply $backend->{_conninfo}, { host => 'localhost', dbname => 'test' },
'conninfo copied with pool_size stripped';
is_deeply $backend->{_opts}, { RaiseError => 1 }, 'opts copied through';
is_deeply $ci->[0], { host => 'localhost', dbname => 'test', pool_size => 3 },
'caller conninfo untouched (normalization works on a copy)';
# default pool size + default opts
my ($b2) = new_backend();
$b2->connect_info([ { host => 'h' } ]);
is $b2->{_pool_size}, 5, 'pool_size defaults to 5';
is_deeply $b2->{_opts}, {}, 'opts default to empty hash';
# _normalize_conninfo is an identity seam by default
my $raw = [ { host => 'h' }, {} ];
is $backend->_normalize_conninfo($raw), $raw,
'_normalize_conninfo defaults to identity pass-through';
}
# ---------------------------------------------------------------------------
# _run_crud: select / select_single (via the pooled runner)
# ---------------------------------------------------------------------------
{
my ($backend) = new_backend();
$backend->{next_rows} = [ [ 1, 'Miles Davis' ], [ 2, 'John Coltrane' ] ];
my $f = $backend->select_async('artist', [ 'artistid', 'name' ], { name => { -like => '%' } });
isa_ok $f, 'Test::SyncFuture', 'select_async returns a Future';
is_deeply [ $f->get ], [ [ 1, 'Miles Davis' ], [ 2, 'John Coltrane' ] ],
'select_async resolves with the raw result rows';
like $backend->_last_sql, qr/^SELECT .*FROM "artist"/,
'select_async emitted real maker SQL for the artist table';
# select_single resolves with only the first row
$backend->{next_rows} = [ [ 1, 'Miles Davis' ], [ 2, 'John Coltrane' ] ];
my $sf = $backend->select_single_async('artist', [ 'artistid', 'name' ], {});
is_deeply scalar $sf->get, [ 1, 'Miles Davis' ],
'select_single_async resolves with only the first row';
# select_single on no rows resolves with undef
$backend->{next_rows} = [];
my $ef = $backend->select_single_async('artist', [ 'artistid' ], {});
is scalar $ef->get, undef, 'select_single_async resolves undef when no rows';
}
# ---------------------------------------------------------------------------
( run in 1.206 second using v1.01-cache-2.11-cpan-f03e8824b8d )