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 )