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 )