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 Test::WWW::Mechanize::PSGI; use Term::ANSIColor qw(:constants); use constant TESTS => 33; warn '=' x 76 . "\n"; # unlike 'say', warn outputs even without -v flag if ( $ENV{PLACK_SERVER} && $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"; #------------------------------------------------------------------------------- sub debug { my $response = shift; # p $response; p $response->content; # print $fh $response->content; } sub test_title { say BRIGHT_CYAN, "\n" . '-' x 10 . ' ' . $_[0], RESET } 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('MyApp using SessionTest logic'); { package MyApp; use Dancer2; use Data::Printer; get '/' => sub { # same logic as sessiontest.pl my $s = session(); # p $s; my $testcounter = session 'testcounter' || 0; $testcounter++; # info 'SESSION COUNTER = ' . $testcounter; session testcounter => $testcounter; # p $s; my $pid = $$; 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 '/' ); # p $res->content; ok( $res->is_success, 'successful request' ); # this should increment but doesn't, PID is different for each request: like $res->content, qr/counter: 1/, sprintf 'expected content [%s]', $res->content; } } done_testing(TESTS);