The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use strict;
use warnings;
use Test::More;
use Plack::Test;
use Plack::Builder;
use HTTP::Request::Common;
use Redis;

my $default_server = $ENV{REDIS_SERVER} || '127.0.0.1:6379';

my $redis = eval { Redis->new(server => $default_server, debug => 0) };
plan skip_all => "Redis-server needs to be running on '$default_server' for tests" unless $redis;

$redis->select(0);
$redis->flushdb;

# simple application
my $app = sub { [200, [ 'Content-Type' => 'text/html' ], [ '<html><body>OK</body></html>' ]] };

# fixed REMOTE_USER bundle
my $appx = sub {
    my ($limits) = @_;

    builder {
        enable 'Throttle::Lite', limits => $limits, routes => '/api/user',
            backend => [ 'Redis' => { instance => $default_server } ];
        $app;
    };
};

# faked REMOTE_USER bundle
my $appy = sub {
    my ($user) = @_;

    builder {
        enable sub {
            my ($app) = @_;
            sub {
                my ($env) = @_;
                $env->{REMOTE_USER} = $user;
                $app->($env);
            };
        };
        $appx->('5 req/hour');
    };
};

my @samples = (
    #   code  used    expire     content              mime
    1,  200,    1,      '',     'OK',              'text/html',
    2,  200,    2,      '',     'OK',              'text/html',
    3,  200,    3,      '',     'OK',              'text/html',
    4,  200,    4,      '',     'OK',              'text/html',
    5,  200,    5,      '1',    'OK',              'text/html',
    6,  429,    5,      '1',    'Limit Exceeded',  'text/plain',
    7,  429,    5,      '1',    'Limit Exceeded',  'text/plain',
);

my $checks = sub {
    my ($cb, $key, $samples) = @_;

    while (my ($num, $code, $used, $expire_in, $content, $type) = splice(@$samples, 0, 6)) {
        my $reqno = 'Request (' . $num . ') [' . $key . ']';
        my $res = $cb->(GET '/api/user/login');
        is $res->code,                                      $code,          $reqno . ' code';
        is $res->header('X-Throttle-Lite-Used'),            $used,          $reqno . ' used header';
        is defined($res->header('X-Throttle-Lite-Expire')), $expire_in,     $reqno . ' expire-in header';
        is defined($res->header('Retry-After')),            $expire_in,     $reqno . ' retry-after header';
        like $res->content,                                 qr/$content/,   $reqno . ' content';
        is $res->content_type,                              $type,          $reqno . ' content type';
    }
};

# username fixed to 'nobody', limits - vary
my @limits  = ('5 req/hour', '5 req/day');
my %by_limits = map { $_ => [ @samples ] } @limits;
test_psgi $appx->($_), sub { my ($cb) = @_; $checks->($cb, $_, $by_limits{$_}) } for @limits;

# limits fixed to '5 req/hour', users - vary
my @users = qw(Tom Dick Harry);
my %by_users = map { $_ => [ @samples ] } @users;
test_psgi $appy->($_), sub { my ($cb) = @_; $checks->($cb, $_, $by_users{$_}) } for @users;

done_testing();