DBIO
view release on metacpan or search on metacpan
t/resultset/update_or_create_related.t view on Meta::CPAN
$storage->reset_captured;
# find() returns an existing artist with a DIFFERENT name -> update fires
$storage->mock_persistent(qr/FROM "artist"/i, [[ 1, 'Old Name', 13, undef ]]);
my $row;
lives_ok {
$row = $schema->resultset('Artist')->update_or_create(
{ artistid => 1, name => 'Updated Name' },
{ key => 'primary' },
);
} 'plain update_or_create (row present) lives';
isa_ok $row, 'DBIO::Row', 'returned row';
my @ops = op_tables();
ok( ( grep { $_ eq 'select:artist' } @ops ), 'find() ran a SELECT on the main table' );
ok( ( grep { $_ eq 'update:artist' } @ops ), 'existing row was UPDATEd (name differed)' );
ok( !( grep { $_->{op} eq 'txn_begin' } $storage->captured_queries ),
'no transaction is opened for the plain path' );
$storage->clear_mocks;
$storage->reset_captured;
# no mock -> find() misses -> row is INSERTed
lives_ok {
$schema->resultset('Artist')->update_or_create(
{ artistid => 99, name => 'Brand New' },
{ key => 'primary' },
);
} 'plain update_or_create (row absent) lives';
@ops = op_tables();
ok( ( grep { $_ eq 'insert:artist' } @ops ), 'missing row was INSERTed' );
}
# --- (b) composition: the row layer receives the FULL condition unsplit ------
# The find() lookup drops a non-concrete relation (the has_many 'cds'), but the
# row layer -- $row->update on a hit, new_result on a miss -- receives the FULL
# condition with 'cds' still present, restoring the pre-0.900001 data flow.
# Recording stand-ins are used for the row layer: what matters here is what
# update_or_create hands off, not how a real row processes a nested sub-hash
# (that is the row layer's own job -- see t/relationship/set_from_related_guard.t
# and ADR 0034). No transaction is opened -- nothing is split into a second write.
{
package TestRecordingResultSet;
use base 'DBIO::Test::BaseResultSet';
our @find_calls;
our @new_result_calls;
our $find_returns; # test sets this: undef (miss) or a FakeRow (hit)
sub find { my $self = shift; push @find_calls, [@_]; return $find_returns }
sub new_result { my $self = shift; push @new_result_calls, [@_]; return TestRecordingResultSet::FakeRow->new }
package TestRecordingResultSet::FakeRow;
our @update_calls;
sub new { bless {}, shift }
sub insert { $_[0] }
sub update { my $self = shift; push @update_calls, [@_]; $self }
}
my $cond = {
artistid => 1,
name => 'Updated Name',
cds => { title => 'New Title', year => 2020 },
};
my $lookup = { artistid => 1 }; # the constraint's columns only (ADR 0070); name is data, cds non-concrete
# (b1) row found -> $row->update() gets the full condition unsplit
{
$storage->clear_mocks;
$storage->reset_captured;
@TestRecordingResultSet::find_calls = ();
@TestRecordingResultSet::FakeRow::update_calls = ();
$TestRecordingResultSet::find_returns = TestRecordingResultSet::FakeRow->new;
my $artist_source = $schema->source('Artist');
my $orig_resultset_class = $artist_source->resultset_class;
$artist_source->resultset_class('TestRecordingResultSet');
$schema->resultset('Artist')->update_or_create($cond, { key => 'primary' });
$artist_source->resultset_class($orig_resultset_class);
is scalar(@TestRecordingResultSet::find_calls), 1, 'find() ran exactly once';
is_deeply $TestRecordingResultSet::find_calls[0][0], $lookup,
'find() received the key columns only -- the non-concrete has_many and the data column are not in the lookup';
is scalar(@TestRecordingResultSet::FakeRow::update_calls), 1,
'row->update() ran exactly once';
is_deeply $TestRecordingResultSet::FakeRow::update_calls[0][0], $cond,
'row->update() received the FULL condition -- cds reached the row layer unsplit';
ok( !( grep { $_->{op} eq 'txn_begin' } $storage->captured_queries ),
'no transaction is opened -- nothing is split into a second write' );
}
# (b2) row absent -> new_result() gets the full condition unsplit
{
$storage->clear_mocks;
$storage->reset_captured;
@TestRecordingResultSet::find_calls = ();
@TestRecordingResultSet::new_result_calls = ();
$TestRecordingResultSet::find_returns = undef;
my $artist_source = $schema->source('Artist');
my $orig_resultset_class = $artist_source->resultset_class;
$artist_source->resultset_class('TestRecordingResultSet');
$schema->resultset('Artist')->update_or_create($cond, { key => 'primary' });
$artist_source->resultset_class($orig_resultset_class);
is_deeply $TestRecordingResultSet::find_calls[0][0], $lookup,
'find() received the lookup only (row absent)';
is scalar(@TestRecordingResultSet::new_result_calls), 1,
'new_result() ran exactly once';
is_deeply $TestRecordingResultSet::new_result_calls[0][0], $cond,
'new_result() received the FULL condition -- cds reached the row layer unsplit';
( run in 1.212 second using v1.01-cache-2.11-cpan-145b269f2f3 )