DBIO

 view release on metacpan or  search on metacpan

t/composed/async_layer_core_read_capability.t  view on Meta::CPAN

# ABSTRACT: an async layer that requires broker_connect_options -- a capability
# core reads, not layers -- over a transport with no query seam (karr k268)
#
# The layer gate in DBIO::Storage::DBI::_async_storage answers "does this
# transport offer what the layer needs" from transport_capabilities, all of
# them. One of those names is not about layers: broker_connect_options says the
# transport applies the connect options given beside an AccessBroker to every
# connection it opens, and core reads it for that (karr k243). It describes no
# query path. Since DBIO::Forked declares it (dbio-forked k12), a layer naming
# it as its requirement passes the gate on a Model A transport -- one that does
# not own the query path -- and composes over nothing it could wrap.
#
# k268 (decision 2026-10-09): core-read names are kept out of the layer gate.
# A layer requiring one is refused on every transport, Model A and B alike.
# No layer in the family asks for that name.
use strict;
use warnings;

use Test::More;

use DBIO::Test;
use DBIO::Test::Storage;
use DBIO::Storage::DBI;
use DBIO::Storage::Async;
use DBIO::Storage::Composed;
use DBIO::Future::Immediate;

# ---------------------------------------------------------------------------
# Transports. Model A is the DBIO::Forked shape: it overrides the *_async entry
# points and inherits the abstract _query_async stub -- no seam for a layer.
# ---------------------------------------------------------------------------
{
  package T::K268::ModelA;
  use base 'DBIO::Storage::Async';
  use mro 'c3';
  our @SELECTED;
  sub future_class        { 'DBIO::Future::Immediate' }
  sub select_async        { push @SELECTED, $_[1]; DBIO::Future::Immediate->done([ 1, 'row' ]) }
  sub select_single_async { DBIO::Future::Immediate->done([]) }
  sub insert_async        { DBIO::Future::Immediate->done([]) }
  sub update_async        { DBIO::Future::Immediate->done(0)  }
  sub delete_async        { DBIO::Future::Immediate->done(0)  }
  sub txn_do_async        { DBIO::Future::Immediate->done(1)  }
}

# Model A, taking connect options beside a broker: DBIO::Forked since k12.
{
  package T::K268::ModelA::Declaring;
  use base 'T::K268::ModelA';
  use mro 'c3';
  sub transport_capabilities { ('broker_connect_options') }
}

# Model B: owns the query path, and declares the same.
{
  package T::K268::ModelB::Declaring;
  use base 'DBIO::Storage::Async';
  use mro 'c3';
  sub future_class           { 'DBIO::Future::Immediate' }
  sub transport_capabilities { ('broker_connect_options') }
  sub _query_async           { DBIO::Future::Immediate->done([]) }
}

# ---------------------------------------------------------------------------
# Layers. "Asking" requires the core-read name and wraps the query seam, which
# is all an async layer can do; "Silent" declares nothing.
# ---------------------------------------------------------------------------
{ package T::K268::Asking; sub asking_sync { 1 } }
{
  package T::K268::Asking::Async;
  use mro 'c3';
  our $WRAPPED = 0;
  sub required_transport_capabilities { ('broker_connect_options') }
  sub _query_async { my $self = shift; $WRAPPED++; return $self->next::method(@_) }
}

{ package T::K268::Silent;        sub silent_sync  { 1 } }
{ package T::K268::Silent::Async; sub silent_async { 1 } }

# Sanity: the transports differ in the property under test.
my $abstract_seam = DBIO::Storage::Async->can('_query_async');
is( T::K268::ModelA::Declaring->can('_query_async'), $abstract_seam,
  'sanity: the Model A transport inherits the abstract stub -- no seam' );
isnt( T::K268::ModelB::Declaring->can('_query_async'), $abstract_seam,
  'sanity: the Model B transport overrides the _query_async seam' );

# A fresh sync storage of $storage_class, bound to a live schema carrying
# @layers, in the 'ram' mode that class registered. The caller holds $schema.
sub build_storage {
  my ($storage_class, @layers) = @_;
  my $schema = DBIO::Test->init_schema;
  $schema->register_storage_layer($_) for @layers;
  my $storage = $storage_class->new($schema);
  $storage->connect_info([ 'dbi:K268Mock:', '', '', { async => 'ram' } ]);
  return ($storage, $schema);
}

{
  package T::K268::A::Storage;  use base 'DBIO::Test::Storage'; use mro 'c3';
  package T::K268::AD::Storage; use base 'DBIO::Test::Storage'; use mro 'c3';
  package T::K268::BD::Storage; use base 'DBIO::Test::Storage'; use mro 'c3';



( run in 0.637 second using v1.01-cache-2.11-cpan-145b269f2f3 )