Apache-Session-Browseable

 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 = ();
}



( run in 1.630 second using v1.01-cache-2.11-cpan-85f18b9d64f )