RSS Git Download  Clone
Raw Blame History
use 5.24.0;

# tests Starman with SessionTest app; use 'PLACK_ENV=test' to load test.yml to
# suppress debug output, or omit for verbose output; call test script as:
#  env [PLACK_ENV=test] PLACK_TEST_IMPL=Server PLACK_SERVER=Starman \
#  prove -lv t/

# see "Plack::Test test_psgi and Test::WWW::Mechanize" test:
# Plack::Test test_psgi test IS using Starman as intended, with 5 workers, but
# behaviour is different to application, delayed form submission does not cause
# CSRF token validation failure

use Test::More;
use Plack::Test;
use HTTP::Cookies;
use Data::Printer;
use Plack::Request;
# use Time::HiRes qw//;
use Plack::Test::Agent;
use LWP::Protocol::PSGI;
use Test::WWW::Mechanize;
use HTTP::Request::Common;
use Term::ANSIColor::Simple;
use Test::WWW::Mechanize::PSGI;

use constant TESTS => 33;

no warnings 'uninitialized';
warn '=' x 76 . "\n"; # unlike 'say', warn outputs even without -v flag
if ( $ENV{PLACK_SERVER} eq 'Starman' ) {
    warn 'runs tests using Starman server; outputs server start up and shut down '
    . "messages; sleeps for\n";
    warn "a few seconds between some tests\n";

}
else {
	die q!usage: env [PLACK_ENV=test] PLACK_TEST_IMPL=Server !
      . qq!PLACK_SERVER=Starman prove -l t/session.t\n!;
}
warn '=' x 91 . "\n";
use warnings 'uninitialized';

#-------------------------------------------------------------------------------
sub debug {
    my $response = shift; # p $response;
    p $response->content;
    # print $fh $response->content;
}
sub test_title { say color("\n" . '-' x 10 . ' ' . $_[0])->cyan } # colors don't work
sub _sleep { my $t = shift; say ".....sleeping for $t seconds"; sleep $t; }
#-------------------------------------------------------------------------------

use_ok('SessionTest'); # p %ENV; exit;

my $app = SessionTest->to_app;

my $csrf_tkn_str = 'CSRF token: {SSHA}';
my $csrf_tkn_re  = 'CSRF token: \{SSHA\}'; # escaped for regex for perl >= 5.30
my $success_msg  = 'SUCCESS: valid CSRF token';
my $failure_msg  = 'ERROR: invalid CSRF token';

{
    test_title('Plack::Test::Agent');
    my $local_agent  = Plack::Test::Agent->new(app => $app);
    my $server_agent = Plack::Test::Agent->new(
        app    => $app,
        server => 'HTTP::Server::PSGI'
    );

    my $local_res  = $local_agent->get( '/sessiontest' );
    my $server_res = $server_agent->get( '/sessiontest' );

    ok $local_res->is_success,  'local GET /sessiontest should succeed';
    ok $server_res->is_success, 'server GET /sessiontest should succeed';
}
{ # HTTP::Server::PSGI - works, Starman & Twiggy don't
    test_title('Plack::Test::Agent with HTTP::Server::PSGI & agent->get_mech');
    my $agent = Plack::Test::Agent->new(
        app    => $app,
        server => 'HTTP::Server::PSGI'
    );
    my $mech = $agent->get_mech;         # debug($mech);
    $mech->get_ok('/sessiontest');          # debug($mech);
    $mech->content_contains($csrf_tkn_str);
    # p $mech->response->header('Server');
    $mech->submit;                       # debug($mech);
    $mech->text_contains($success_msg, 'OK: valid CSRF token');
    _sleep(3);
    $mech->submit;                       # debug($mech);
    # this should fail:
    $mech->text_contains($success_msg, 'OK: valid CSRF token');
}
{ # HTTP::Server::PSGI - works, Starman & Twiggy don't
    test_title('Plack::Test::Agent with Starman and agent->get_mech');
    my $agent = Plack::Test::Agent->new(
        app    => $app,
        server => 'Starman', # causes following msg to appear:
        # Passed serialize value of none is incompatible with multiple
        # ports - using default serialize
    );
    my $mech = $agent->get_mech;           # debug($mech);
    $mech->get_ok('/sessiontest');         # debug($mech);
    $mech->content_contains($csrf_tkn_str);
    # p $mech->response->header('Server');
    $mech->submit;                        # debug($mech); exit;
    SKIP: {
        skip( q!this fails valid CSRF token test!, 1 );
        $mech->text_contains($success_msg, 'OK: valid CSRF token');
    }
    _sleep(3);
    $mech->submit;                       # debug($mech);
    # this should fail:
    SKIP: {
        skip( q!this fails valid CSRF token test!, 1 );
        $mech->text_contains($success_msg, 'OK: valid CSRF token');
    }
}
{
    test_title('LWP::Protocol::PSGI and Test::WWW::Mechanize');
    my $psgi_app = SessionTest::runner()->psgi_app;
    LWP::Protocol::PSGI->register($psgi_app);
    my $mech = Test::WWW::Mechanize->new;
    $mech->get_ok('http://localhost/sessiontest'); # debug($mech); exit;
    $mech->content_contains($csrf_tkn_str);
    # p $mech->response->header('Server');
    _sleep(3);
    $mech->submit;                       # debug($mech);
    # this should fail:
    # $mech->text_contains($failure_msg, 'OK: invalid CSRF token');
    $mech->text_contains($success_msg, 'OK: valid CSRF token');
}
{
    test_title('Plack::Test simple OO interface');
    my $test = Plack::Test->create($app);
    my $res = $test->request(GET '/sessiontest'); # p $res->request;
    like $res->content, qr/$csrf_tkn_re/;
    # how to submit form ?
}
{
    test_title('Test::WWW::Mechanize::PSGI');
    my $mech = Test::WWW::Mechanize::PSGI->new(app => $app);
    $mech->get_ok('/sessiontest');
    # p $mech->response->header('Server');
    is( $mech->ct, 'text/html', 'Is text/html' );
    $mech->content_contains($csrf_tkn_str);
    $mech->submit;                       # p $mech->content;
    $mech->text_contains($success_msg, 'OK: valid CSRF token');
    _sleep(3);
    # this should fail:
    # $mech->text_contains($failure_msg, 'OK: invalid CSRF token');
    $mech->text_contains($success_msg, 'OK: valid CSRF token');
}
{ ####### uses Starman from command-line env
    test_title('Plack::Test test_psgi');

    test_psgi $app, sub {
        my $cb  = shift; # p $cb;
        my $res = $cb->(GET '/sessiontest'); # p $res;
        like $res->content, qr/$csrf_tkn_re/;
        # how to submit form ?
    };
}
{ ####### uses Starman from command-line env
    test_title('Plack::Test test_psgi and Test::WWW::Mechanize');

    my $mech = Test::WWW::Mechanize->new;

    test_psgi $app, sub {
        my $cb  = shift; # p $cb;
        $mech->get('http://localhost/sessiontest'); # p $mech->content;
        # p $mech->response->header('Server');
        $mech->content_contains($csrf_tkn_str, 'OK: valid CSRF token');
    ### proves starman is running multiple workers:
        # _sleep(30); # now run 'ps aux | grep starman' - shows 5 workers !!
        _sleep(3);
        $mech->submit; # p $mech->content;
        # this should fail:
        # $mech->text_contains($failure_msg, 'OK: invalid CSRF token');
        $mech->text_contains($success_msg, 'OK: valid CSRF token');
    };
    # now run 'ps aux | grep starman' - shows 0 workers
}
{
    test_title('Integrated app using sessiontest.pl logic');
    {
        package MyApp;
        use Dancer2;
        use Data::Printer;
        use Term::ANSIColor::Simple;

        my $c = config;
		$c->{engines}->{session}->{Cookie}->{secret_key} = 'blabenshpule'; # p $c;
		# set session => 'Cookie'; # doesn't fix failure to increment
        set session => 'PSGI';     # doesn't fix failure to increment

        get '/' => sub { # same logic as sessiontest.pl
            my $session = session(); # p $session;
            my $testcounter = session 'testcounter' || 0;
            $testcounter++;
          # this should increment but doesn't:
            say color("SESSION COUNTER = $testcounter")->yellow;
            session testcounter => $testcounter; # p $session;
            my $pid = $$; # p my $c = config; p $c;
            return "counter: $testcounter; pid: $pid";
        };
    }

    my $test = Plack::Test->create( MyApp->to_app );
    # this gets a new session on each request - but needs to retain same one:
    for (1 .. 5) {
        my $res = $test->request( GET '/' );
        ok( $res->is_success, 'successful request' );
      # this should increment but doesn't, PID is different for each request:
        like $res->content, qr/counter: 1/, # qr/counter: $_/,
            sprintf 'expected content [%s]', $res->content;
    }
}

done_testing(TESTS);