DBIO
view release on metacpan or search on metacpan
t/helpers/row_helpers_storage.t view on Meta::CPAN
__PACKAGE__->proxy_resultset_method('score');
}
TestPRM::Schema->register_class(Widget => 'TestPRM::Schema::Result::Widget');
# --- Connect all three schemas to fake storage -----------------------
sub connect_fake {
my $schema_class = shift;
my $schema = $schema_class->connect(sub { });
$schema->storage(DBIO::Test::Storage->new($schema));
return $schema;
}
subtest 'StorageValues: snapshot + get_storage_value + refresh' => sub {
my $schema = connect_fake('TestSV::Schema');
my $rs = $schema->resultset('Post');
my $post = $rs->new_result({ title => 'First draft', body => 'hello' });
is_deeply(
$post->storage_value_columns,
['title'],
'only keep_storage_value columns are snapshotted'
);
ok(defined $post->_storage_values, '_storage_values is initialised in new');
$post->insert;
is($post->get_storage_value('title'), 'First draft',
'after insert, storage_value matches the inserted value');
$post->title('Second draft');
is($post->get_storage_value('title'), 'First draft',
'dirty change does not update storage_value');
$post->update;
is($post->get_storage_value('title'), 'Second draft',
'after update, storage_value is refreshed');
# inflate_result path
my $rsrc = $schema->source('Post');
my $inflated = TestSV::Schema::Result::Post->inflate_result(
$rsrc, { id => 7, title => 'Via inflate_result', body => 'x' }
);
is($inflated->get_storage_value('title'), 'Via inflate_result',
'inflate_result captures storage value');
};
subtest 'OnColumnChange: before/after fire with correct args' => sub {
my $schema = connect_fake('TestOCC::Schema');
my $rs = $schema->resultset('Account');
# Create and insert an account
TestOCC::Changes::reset();
my $acc = $rs->new_result({ amount => 100, owner => 'alice' });
$acc->insert;
# No callbacks should fire on insert
is(scalar TestOCC::Changes::all(), 0,
'callbacks do not fire on insert');
# Dirty the tracked column and update
$acc->amount(250);
$acc->update;
my @log = TestOCC::Changes::all();
is(scalar @log, 2, 'before + after callbacks fired');
is($log[0]{phase}, 'before', 'before fires first');
is($log[0]{old}, 100, 'before sees old storage value');
is($log[0]{new}, 250, 'before sees new value');
is($log[1]{phase}, 'after', 'after fires last');
# After callbacks get ($old, $new) where $old has been stored â
# the post-update storage value matches the new value.
is($log[1]{old}, 250, 'after sees updated storage value');
is($log[1]{new}, 250, 'after sees new value');
# Updating a non-registered column is a no-op for callbacks
TestOCC::Changes::reset();
$acc->owner('bob');
$acc->update;
is(scalar TestOCC::Changes::all(), 0,
'non-registered column change does not trigger callbacks');
# Confirm zero-cost for a class with no callbacks (the SV schema).
my $post_schema = connect_fake('TestSV::Schema');
my $post_rs = $post_schema->resultset('Post');
my $post = $post_rs->new_result({ title => 'x', body => 'y' });
$post->insert;
$post->title('z');
lives_ok { $post->update } 'update on a class with no callbacks works';
};
subtest 'ProxyResultSetMethod: installs accessor, registers slot' => sub {
my $schema = connect_fake('TestPRM::Schema');
# Class method is installed as an accessor sub on the Result class
can_ok(
'TestPRM::Schema::Result::Widget',
'score',
);
is_deeply(
TestPRM::Schema::Result::Widget->_proxy_slots,
['score'],
'_proxy_slots records the registered slot'
);
# Accessor hands back cached value if the slot is loaded
my $rsrc = $schema->source('Widget');
my $w = TestPRM::Schema::Result::Widget->inflate_result(
$rsrc, { id => 1, name => 'cached', score => 99 }
);
is($w->score, 99, 'proxy accessor returns loaded value directly');
# update() must not carry the proxy slot into the generated SQL.
# Mark a real column dirty so an UPDATE actually fires, plus the
# proxy slot to verify it gets stripped.
$schema->storage->reset_captured;
$w->{_dirty_columns}{score} = 1;
$w->name('renamed');
lives_ok {
$w->update;
} 'update with dirty proxy slot does not attempt to write it';
my @queries = $schema->storage->captured_queries;
my ($update) = grep { $_->{op} eq 'update' } @queries;
ok($update, 'an UPDATE was issued');
unlike($update->{sql}, qr/\bscore\b/,
'UPDATE SQL does not include the proxy slot');
like($update->{sql}, qr/\bname\b/,
'UPDATE SQL includes the real dirty column');
# copy() path: also must not carry the slot through.
$w->{_column_data}{score} = 77;
my $copy;
lives_ok {
$copy = $w->copy({ name => 'copied' });
} 'copy with a proxy slot value does not die';
};
# --- DateTime snapshot immutability test -----------------------------
subtest 'StorageValues: DateTime clone prevents snapshot corruption' => sub {
eval 'require DateTime; 1' or skip('DateTime not installed', 3);
{
package TestDT::Schema;
use base 'DBIO::Schema';
}
( run in 0.494 second using v1.01-cache-2.11-cpan-6aa56a78535 )