view release on metacpan or search on metacpan
t/Apache-Session-Browseable-Store-Patroni.t view on Meta::CPAN
my $ds = 'dbi:Pg:dbname=testdb';
my $store = $package->new;
$store->{_originalDataSource} = $ds;
# No cache - should return 0
%Apache::Session::Browseable::Store::Patroni::patroniCache = ();
my $args = { DataSource => $ds, PatroniCacheTTL => 60 };
# Capture STDERR
my $stderr = '';
{
local *STDERR;
open STDERR, '>', \$stderr;
my $result = $store->_useCachedLeader( $args, $ds, "Test reason" );
is( $result, 0, '_useCachedLeader: returns 0 when no cache' );
}
# With valid cache
$Apache::Session::Browseable::Store::Patroni::patroniCache{$ds} = {
leader => {
host => '10.0.0.5',
port => 5432,
time => time() - 10 # 10 seconds ago
}
};
$stderr = '';
{
local *STDERR;
open STDERR, '>', \$stderr;
my $result = $store->_useCachedLeader( $args, $ds, "Test reason" );
is( $result, 1, '_useCachedLeader: returns 1 when cache valid' );
}
like( $args->{DataSource}, qr/host=10\.0\.0\.5/,
'_useCachedLeader: updates DataSource with cached host' );
like( $args->{DataSource}, qr/port=5432/,
'_useCachedLeader: updates DataSource with cached port' );
# With expired cache
$Apache::Session::Browseable::Store::Patroni::patroniCache{$ds} = {
leader => {
host => '10.0.0.6',
port => 5433,
time => time() - 120 # 2 minutes ago, expired
}
};
$args = { DataSource => $ds, PatroniCacheTTL => 60 };
$stderr = '';
{
local *STDERR;
open STDERR, '>', \$stderr;
my $result = $store->_useCachedLeader( $args, $ds, "Test reason" );
is( $result, 0, '_useCachedLeader: returns 0 when cache expired' );
}
# Clear cache
%Apache::Session::Browseable::Store::Patroni::patroniCache = ();
}
# Test circuit breaker logic in checkMaster (without actual HTTP calls)
{
t/Apache-Session-Browseable-Store-Patroni.t view on Meta::CPAN
my $args = {
DataSource => $ds,
PatroniUrl => 'http://fake.patroni:8008/cluster',
PatroniCircuitBreakerDelay => 30,
PatroniCacheTTL => 60,
};
# Circuit breaker should be active (failed 10s ago, delay is 30s)
# It should use cached leader instead of calling API
my $stderr = '';
{
local *STDERR;
open STDERR, '>', \$stderr;
my $result = $store->checkMaster($args);
is( $result, 1, 'Circuit breaker: uses cached leader when active' );
}
like(
$stderr,
qr/Circuit breaker active/,
'Circuit breaker: prints appropriate message'
);
like( $args->{DataSource}, qr/host=10\.0\.0\.7/,
'Circuit breaker: uses cached host' );
# Clear cache
%Apache::Session::Browseable::Store::Patroni::patroniCache = ();
}
t/Apache-Session-Browseable-Store-Patroni.t view on Meta::CPAN
my $args = {
DataSource => $ds,
PatroniUrl => 'http://nonexistent.host:8008/cluster',
PatroniCircuitBreakerDelay => 30, # Expired (60 > 30)
PatroniCacheTTL => 120,
};
# Circuit breaker should NOT be active (failed 60s ago, delay is 30s)
# It will try to call API (which will fail) then use cache
my $stderr = '';
{
local *STDERR;
open STDERR, '>', \$stderr;
# This will try HTTP (fail) then fallback to cache
my $result = $store->checkMaster($args);
is( $result, 1,
'Circuit breaker expired: uses cache after API failure' );
}
like(
$stderr,
qr/Patroni API unavailable/,
'Circuit breaker expired: API was tried'
);
# Clear cache
%Apache::Session::Browseable::Store::Patroni::patroniCache = ();
}
# Test default values
{
t/Apache-Session-Browseable-Store-Patroni.t view on Meta::CPAN
$store->{_originalDataSource} = $ds;
# Test with minimal args
my $args = {
DataSource => $ds,
PatroniUrl => 'http://fake:8008/cluster',
};
# Without cache, checkMaster will try HTTP and fail
# This tests that defaults are applied correctly
my $stderr = '';
{
local *STDERR;
open STDERR, '>', \$stderr;
# Store the time before calling checkMaster
my $before = time();
my $result = $store->checkMaster($args);
# Verify failure was recorded with correct circuit breaker
my $cache =
$Apache::Session::Browseable::Store::Patroni::patroniCache{$ds};
ok( $cache->{lastFailure} >= $before,
'Default circuit breaker delay: failure recorded' );
t/Apache-Session-Browseable-Store-Patroni.t view on Meta::CPAN
port => 5432,
time => time() - 45 # 45 seconds ago
}
};
my $store = $package->new;
$store->{_originalDataSource} = $ds;
# With 30s TTL, cache should be expired
my $args1 = { DataSource => $ds, PatroniCacheTTL => 30 };
my $stderr = '';
{
local *STDERR;
open STDERR, '>', \$stderr;
my $result = $store->_useCachedLeader( $args1, $ds, "Test" );
is( $result, 0, 'Custom TTL 30s: cache expired at 45s' );
}
# With 60s TTL, cache should be valid
my $args2 = { DataSource => $ds, PatroniCacheTTL => 60 };
$stderr = '';
{
local *STDERR;
open STDERR, '>', \$stderr;
my $result = $store->_useCachedLeader( $args2, $ds, "Test" );
is( $result, 1, 'Custom TTL 60s: cache valid at 45s' );
}
# Clear cache
%Apache::Session::Browseable::Store::Patroni::patroniCache = ();
}
# Test module can be loaded
use_ok('Apache::Session::Browseable::Patroni');
t/Apache-Session-Browseable-Store-Patroni.t view on Meta::CPAN
)
);
my $store = $package->new;
$store->{_originalDataSource} = $ds;
my $args = {
DataSource => $ds,
PatroniUrl => 'http://mock:8008/cluster'
};
my $stderr = '';
my $result;
{
local *STDERR;
open STDERR, '>', \$stderr;
$result = $store->checkMaster($args);
}
is( $result, 1, 'Valid leader: checkMaster returns 1' );
like( $args->{DataSource}, qr/host=10\.0\.0\.100/,
'Valid leader: correct host in DataSource' );
}
# Test 2: Split-brain detection (multiple leaders)
{
%Apache::Session::Browseable::Store::Patroni::patroniCache = ();
t/Apache-Session-Browseable-Store-Patroni.t view on Meta::CPAN
)
);
my $store = $package->new;
$store->{_originalDataSource} = $ds;
my $args = {
DataSource => $ds,
PatroniUrl => 'http://mock:8008/cluster'
};
my $stderr = '';
my $result;
{
local *STDERR;
open STDERR, '>', \$stderr;
$result = $store->checkMaster($args);
}
is( $result, 0, 'Split-brain: checkMaster returns 0' );
like(
$stderr,
qr/Multiple leaders detected/,
'Split-brain: warning message'
);
}
# Test 3: Leader not in running state
{
%Apache::Session::Browseable::Store::Patroni::patroniCache = ();
my $guard = LWP::Protocol::PSGI->register(
t/Apache-Session-Browseable-Store-Patroni.t view on Meta::CPAN
)
);
my $store = $package->new;
$store->{_originalDataSource} = $ds;
my $args = {
DataSource => $ds,
PatroniUrl => 'http://mock:8008/cluster'
};
my $stderr = '';
my $result;
{
local *STDERR;
open STDERR, '>', \$stderr;
$result = $store->checkMaster($args);
}
is( $result, 0, 'Leader starting: checkMaster returns 0' );
like(
$stderr,
qr/not in running state/,
'Leader starting: warning message'
);
}
# Test 4: Leader missing host
{
%Apache::Session::Browseable::Store::Patroni::patroniCache = ();
my $guard = LWP::Protocol::PSGI->register(
t/Apache-Session-Browseable-Store-Patroni.t view on Meta::CPAN
)
);
my $store = $package->new;
$store->{_originalDataSource} = $ds;
my $args = {
DataSource => $ds,
PatroniUrl => 'http://mock:8008/cluster'
};
my $stderr = '';
my $result;
{
local *STDERR;
open STDERR, '>', \$stderr;
$result = $store->checkMaster($args);
}
is( $result, 0, 'Leader missing host: checkMaster returns 0' );
like(
$stderr,
qr/missing host or port/,
'Leader missing host: warning message'
);
}
# Test 5: No leader found
{
%Apache::Session::Browseable::Store::Patroni::patroniCache = ();
my $guard = LWP::Protocol::PSGI->register(
t/Apache-Session-Browseable-Store-Patroni.t view on Meta::CPAN
)
);
my $store = $package->new;
$store->{_originalDataSource} = $ds;
my $args = {
DataSource => $ds,
PatroniUrl => 'http://mock:8008/cluster'
};
my $stderr = '';
my $result;
{
local *STDERR;
open STDERR, '>', \$stderr;
$result = $store->checkMaster($args);
}
is( $result, 0, 'No leader: checkMaster returns 0' );
like( $stderr, qr/No leader found/, 'No leader: warning message' );
}
# Clear cache
%Apache::Session::Browseable::Store::Patroni::patroniCache = ();
}