DBIO
view release on metacpan or search on metacpan
t/test/16_async_future_io_convention.t view on Meta::CPAN
sub future_class { 'AsyncConv::SyncFuture' }
sub sql_maker_class { 'DBIO::SQLMaker' }
sub _sql_maker_args { (quote_char => '"', name_sep => '.') }
sub _transform_sql { $_[1] }
sub _post_insert_sql { ' RETURNING *' }
sub pool { $_[0]->{pool} ||= AsyncConv::SyncPool->new }
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} });
}
sub _last_sql { $_[0]->{captured}[-1]{sql} }
}
# Two more valid adapters, distinct classes, used to prove precedence: the
# convention sibling of AsyncConv::Override::Storage, and an explicitly
# registered override target. Both inherit the seams above.
{
package AsyncConv::Override::Storage::Async; # convention sibling
use base 'AsyncConv::WithAdapter::Storage::Async';
}
{
package AsyncConv::ExplicitAdapter; # explicit-registration target
use base 'AsyncConv::WithAdapter::Storage::Async';
}
# --- Driver storage classes (subclass the mock storage) --------------------
# ref($storage) is what the convention derives ::Async from. Subclassing
# DBIO::Test::Storage reuses its no-dbh / driver-determined / schema wiring.
{
package AsyncConv::WithAdapter::Storage; # has AsyncConv::WithAdapter::Storage::Async
use base 'DBIO::Test::Storage';
use mro 'c3';
}
{
package AsyncConv::NoAdapter::Storage; # NO ::Async sibling exists
use base 'DBIO::Test::Storage';
use mro 'c3';
}
{
package AsyncConv::Override::Storage; # has a convention sibling AND an explicit reg
use base 'DBIO::Test::Storage';
use mro 'c3';
}
{
package AsyncConv::BaseReg::Storage; # NO ::Async sibling; used with a base-class reg
use base 'DBIO::Test::Storage';
use mro 'c3';
}
# Build a driver storage of $class in future_io mode, bound to a live schema.
# Returns ($storage, $schema) -- the caller must keep $schema alive (the async
# backend weakens its schema ref).
sub driver_storage {
my ($class) = @_;
my $schema = DBIO::Test->init_schema;
my $storage = $class->new($schema);
$storage->_async_mode('future_io'); # Test::Storage defaults to 'immediate'
delete $storage->{_async_storage_obj};
$storage->_connect_info([ { host => 'localhost' } ]);
return ($storage, $schema);
}
# ---------------------------------------------------------------------------
# 1. WITH a convention ::Async adapter: future_io resolves it + CRUD roundtrip
# ---------------------------------------------------------------------------
{
my ($storage, $schema) = driver_storage('AsyncConv::WithAdapter::Storage');
my $async = $storage->async;
isa_ok $async, 'AsyncConv::WithAdapter::Storage::Async',
'future_io resolved the convention adapter (ref($storage)::Async)';
isa_ok $async, 'DBIO::Storage::Async',
'... which is a DBIO::Storage::Async';
is $storage->_async_storage, $async,
'the same adapter feeds the *_async CRUD dispatch (cached)';
$async->{next_rows} = [ [ 1, 'Miles Davis' ], [ 2, 'John Coltrane' ] ];
my $f = $storage->select_async('artist', [ 'artistid', 'name' ], { name => { -like => '%' } });
isa_ok $f, 'AsyncConv::SyncFuture', 'select_async returns the adapter Future';
is_deeply [ $f->get ], [ [ 1, 'Miles Davis' ], [ 2, 'John Coltrane' ] ],
'select_async round-trips rows through the convention adapter';
like $async->_last_sql, qr/^SELECT .*FROM "artist"/,
'real DBIO::SQLMaker SQL was emitted through the adapter';
# a write op round-trips too
$async->{next_rows} = [];
my $uf = $storage->update_async('artist', { name => 'X' }, { artistid => 1 });
ok $uf->is_ready, 'update_async resolves through the convention adapter';
like $async->_last_sql, qr/^UPDATE "artist" SET/, 'update_async emitted UPDATE SQL';
}
# ---------------------------------------------------------------------------
# 2. WITHOUT a convention ::Async adapter: EARLY, CLEAR croak
# ---------------------------------------------------------------------------
{
my ($storage) = driver_storage('AsyncConv::NoAdapter::Storage');
throws_ok { $storage->async }
qr/\Qdriver AsyncConv::NoAdapter::Storage does not support future_io -- no AsyncConv::NoAdapter::Storage::Async\E/,
'a driver without a convention ::Async adapter croaks early, naming the missing adapter';
# the same early croak surfaces through a *_async op (not a late, opaque
# "must override _submit_query" from an abstract base)
my ($s2) = driver_storage('AsyncConv::NoAdapter::Storage');
throws_ok { $s2->select_async('artist', [ '*' ], {}) }
qr/does not support future_io/,
'the early convention croak surfaces through select_async too';
}
# ---------------------------------------------------------------------------
# 3. Precedence: an EXPLICIT per-driver registration overrides the convention
# ---------------------------------------------------------------------------
( run in 0.679 second using v1.01-cache-2.11-cpan-995e09ba956 )