Class-Simple-Cached
view release on metacpan or search on metacpan
t/integration.t view on Meta::CPAN
package t::Int::CachedA;
our @ISA = ('Class::Simple::Cached');
}
{
# CSC subclass B â its keys are prefixed with 't::Int::CachedB:'
package t::Int::CachedB;
our @ISA = ('Class::Simple::Cached');
}
{
# An inner object that can respond to can() and isa() for delegation tests.
package t::Int::Delegating;
sub new { bless {}, shift }
sub greet { 'hello' }
sub can {
my ($self, $m) = @_;
return ($m eq 'greet') ? \&greet : undef;
}
sub isa {
my ($self, $class) = @_;
return ($class eq 't::Int::Delegating') ? 1 : 0;
}
}
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# Workflow 1 â Hash-ref backend complete lifecycle
#
# Strategy: exercise new â setter â repeated getter (cache hits) â DESTROY.
# After DESTROY we inspect the hash ref directly to confirm keys are gone.
# This proves the full round-trip without any CHI involvement.
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
subtest 'workflow:hash:lifecycle' => sub {
# Strategy: construct with a hash ref, set a value, read it back twice
# (second read must be a cache hit), then let DESTROY run and verify
# the hash ref is emptied.
my %store;
my $inner = t::Int::Inner->new();
my $obj = Class::Simple::Cached->new(cache => \%store, object => $inner);
# Setter stores value in both the inner object and the hash ref.
$obj->colour('red');
is($obj->colour(), 'red',
'hash:lifecycle: getter returns value after setter');
# The cache entry should be in the hash under the prefixed key.
ok(exists $store{"${PREFIX}colour"},
'hash:lifecycle: cache key exists in the hash ref');
# Second getter â inner object must NOT be called again (cache hit).
my $pre_count = $inner->call_count();
$obj->colour();
is($inner->call_count(), $pre_count,
'hash:lifecycle: second getter does not invoke the inner object');
diag sprintf('[hash:lifecycle] store keys: %s', join(', ', sort keys %store))
if $ENV{TEST_VERBOSE};
# DESTROY fires when $obj goes out of scope; use a block to force it.
{ my $tmp = $obj; } # keeps $obj alive; we destroy via explicit undef
$obj = undef;
is(scalar(keys %store), 0,
'hash:lifecycle: DESTROY removes all cache entries from the hash ref');
done_testing();
};
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# Workflow 2 â Optional CHI dependency
#
# Strategy: block CHI with Test::Without::Module to prove that CSC itself
# never issues a `require CHI` â CHI is a purely caller-supplied runtime
# dependency. The module is already loaded and continues to work with a
# hash-ref cache even when CHI cannot be required.
#
# After the subtest, CHI is unblocked so subsequent subtests can load it.
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
subtest 'workflow:optional-chi:hash-backend-works-without-chi' => sub {
# Block CHI from loading to simulate an environment where it is not installed.
Test::Without::Module->import('CHI');
# Verify the block is effective for new require calls.
eval { require CHI };
ok($@, 'optional-chi: CHI cannot be required when blocked by Test::Without::Module');
diag "[optional-chi] require CHI error: $@" if $ENV{TEST_VERBOSE} && $@;
# CSC itself never calls require CHI â it only interacts with whatever object
# the caller passes as cache =>. A hash ref works with no CHI in the process.
my $obj = Class::Simple::Cached->new(cache => {});
$obj->field('present');
is($obj->field(), 'present',
'optional-chi: hash-ref backend operates correctly without CHI');
# Likewise, a duck-typed CHI-compatible object works without the CHI module.
my $fake_chi = t::Int::CHI->new();
my $obj2 = Class::Simple::Cached->new(cache => $fake_chi, object => t::Int::Inner->new());
$obj2->colour('blue');
is($obj2->colour(), 'blue',
'optional-chi: duck-typed CHI-compatible object works without CHI in process');
Test::Without::Module->unimport('CHI');
done_testing();
};
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# Workflow 3 â CHI backend complete lifecycle with spy verification
#
# Strategy: construct with a t::Int::CHI mock, call a setter then a getter,
# and inspect the call log to prove:
# (a) set() was called with the 'never' expiry token
# (b) the second get() did NOT re-hit the inner object (cache hit)
# DESTROY on $obj should call purge() on the CHI mock.
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
SKIP: {
eval { require CHI };
( run in 0.749 second using v1.01-cache-2.11-cpan-14f38c9f855 )