diff --git a/t/cgi.t b/t/cgi.t index 39914dbe08df7c09064db31855a38375bdbeb9a8..93433816cfead116dc770e1d63ccc9965924f428 100755 --- a/t/cgi.t +++ b/t/cgi.t @@ -3,20 +3,56 @@ use strict; use warnings; +use feature qw(state); + use English qw(-no_match_vars); use File::Temp; +use HTML::Tidy5; use IPC::Run qw(run); +use Test::HTML::Tidy5 qw(); use Test::More; +use Test::Mojo::WithRoles 'SubmitForm'; + +plan(skip_all => 'live database required') unless + $ENV{TEST_DB_HOST} && + $ENV{TEST_DB_NAME} && + $ENV{TEST_DB_TYPE} && + $ENV{TEST_DB_USERNAME} && + $ENV{TEST_DB_PASSWORD}; plan tests => 4; -my $log = File::Temp->new(UNLINK => $ENV{TEST_DEBUG} ? 0 : 1); -diag("log file: $log") if $ENV{TEST_DEBUG}; +sub named_subtest { + my ($name, $code, @args) = @_; + + my $tb = Test::More->builder(); + return $tb->subtest($name, $code, @args, $name); +} + +sub setup { + my %args = @_; -my $config = File::Temp->new(UNLINK => $ENV{TEST_DEBUG} ? 0 : 1); -print {$config} <<EOF; + my $name = $args{name}; + my $host = $args{host}; + my $type = $args{type}; + my $username = $args{username}; + my $password = $args{password}; + + system("mysqladmin --host=$host --user=$username --password=$password --force drop $name >/dev/null"); + system("mysqladmin --host=$host --user=$username --password=$password create $name >/dev/null") == 0 + or die "can't run mysqladmin: $CHILD_ERROR\n"; + system("mysql --host=$host --user=$username --password=$password $name < conf/manager.sql") == 0 + or die "can't run mysql: $CHILD_ERROR\n"; + + my $temp_dir = File::Temp->newdir(CLEANUP => $ENV{TEST_DEBUG} ? 0 : 1); + diag("temp dir: $temp_dir") if $ENV{TEST_DEBUG}; + + my $log_file = sprintf("%s/test.log", $temp_dir); + my $conf_file = sprintf("%s/test.conf", $temp_dir); + + open(my $handle, '>', $conf_file); + print {$handle} <<EOF; [setup] -templates_dir = templates templates_theme = edugain [federations] @@ -28,7 +64,7 @@ support_email = support\@my.fqdn name = eduGAIN Access Check [logger] -file = $log +file = $log_file level = debug [mailer] @@ -36,135 +72,127 @@ level = debug [idp] [database] -type = mysql -host = localhost -name = account_manager -username = user -password = password +host = $host +name = $name +type = $type +username = $username +password = $password EOF -$config->flush(); -diag("test configuration: $config") if $ENV{TEST_DEBUG}; - -$ENV{ACCOUNTMANAGER_CONFIG} = $config; - -subtest "index page" => sub { - - plan tests => 4; - - local $ENV{REQUEST_METHOD} = 'GET'; - local $ENV{QUERY_STRING} = ''; - - my ($out, $err, $rc) = run_executable('access-check-manager.cgi'); - like( - $out, - qr{^Content-Type: text/html; charset=utf8\r\n\r\n}m, - 'HTTP headers' - ); - like( - $out, - qr{<title>eduGAIN Access Check</title>}, - 'page title' - ); - like( - $out, - qr{<a href="\?action=select_sp" class="button">Get started</a>}, - 'start button' - ); - is($err, '', 'empty stderr'); -}; + close($handle); + + $ENV{MOJO_CONFIG} = $conf_file; + $ENV{ACCOUNTMANAGER_CONFIG} = $conf_file; + + return $temp_dir; +} + +sub get_test_object { + my %args = @_; + + my $app = $args{app} || 'AccountManager::App'; + + my $t = Test::Mojo::WithRoles->new($app); + my $ua = $t->ua(); + $ua->max_redirects(1); + $ua->on(start => sub { + my (undef, $tx) = @_; + $tx->req()->headers()->from_hash($args{user}) + if $args{user} and !$tx->previous(); + $tx->req()->headers()->accept_language($args{language}) + if $args{language}; + }); + + $t->app()->log()->info(sprintf('new test: %s', $args{test})) + if $args{test}; + + return $t; +} -subtest "SP selection page" => sub { - - plan(skip_all => 'live database required') unless - $ENV{TEST_DB_HOST} && - $ENV{TEST_DB_NAME} && - $ENV{TEST_DB_DRIVER} && - $ENV{TEST_DB_USERNAME} && - $ENV{TEST_DB_PASSWORD}; - - plan tests => 4; - - local $ENV{REQUEST_METHOD} = 'GET'; - local $ENV{QUERY_STRING} = 'action=select_sp'; - - my ($out, $err, $rc) = run_executable('access-check-manager.cgi'); - like( - $out, - qr{^Content-Type: text/html; charset=utf8\r\n\r\n}m, - 'HTTP headers' - ); - like( - $out, - qr{<title>eduGAIN Access Check</title>}, - 'page title' - ); - like( - $out, - qr{<select id="edugain" name="edugain">}, - 'page content contains SP list' - ); - is($err, '', 'empty stderr'); +sub html_ok { + my ($res) = @_; + + state $tidy = HTML::Tidy5->new({ + 'drop-empty-elements' => 0 + }); + $tidy->ignore(type => TIDY_INFO); + $tidy->ignore(text => qr/<img> lacks "alt" attribute/); + # https://github.com/htacg/tidy-html5/issues/611 + $tidy->ignore(text => qr/inserting implicit <table>/); + $tidy->ignore(text => qr/\S+ isn't allowed in <table> elements/); + $tidy->ignore(text => qr/missing \S+/); + $tidy->ignore(text => qr/discarding unexpected \S+/); + local $ENV{LANG} = 'C'; + Test::HTML::Tidy5::html_tidy_ok($tidy, $res->text(), "HTML content OK"); +} + +sub diag_file { + my ($res, $dir) = @_; + + return if !$ENV{TEST_DEBUG}; + + state $count = 1; + my $file = sprintf("%s/test_%i.html", $dir, $count); + diag("content saved as $file"); + + open (my $handle, '>', $file) or die($ERRNO); + print $handle $res->text(); + close $handle; + + $count++; +} + +my $test_dir = setup( + host => $ENV{TEST_DB_HOST}, + name => $ENV{TEST_DB_NAME}, + type => $ENV{TEST_DB_TYPE}, + username => $ENV{TEST_DB_USERNAME}, + password => $ENV{TEST_DB_PASSWORD} +); + +named_subtest "index page" => sub { + my $t = get_test_object(test => $_[0]); + + $t->get_ok('/') + ->status_is(200) + ->text_is('html head title' => 'eduGAIN Access Check') + ->element_exists('a[href=/step1]', 'get started button'); + + my $res = $t->tx()->res(); + html_ok($res) or diag_file($res, $test_dir); }; -subtest "email selection page, missing entityid parameter" => sub { - - plan tests => 4; - - local $ENV{REQUEST_METHOD} = 'GET'; - local $ENV{QUERY_STRING} = 'action=select_email&federation=edugain'; - - my ($out, $err, $rc) = run_executable('access-check-manager.cgi'); - like( - $out, - qr{^Content-Type: text/html; charset=utf8\r\n\r\n}m, - 'HTTP headers' - ); - like( - $out, - qr{<title>eduGAIN Access Check</title>}, - 'page title' - ); - like( - $out, - qr{Error:[\n\s]+missing parameter 'entityid'}, - 'page content contains expected error message' - ); - is($err, '', 'empty stderr'); +named_subtest "SP selection page" => sub { + my $t = get_test_object(test => $_[0]); + + $t->get_ok('/step1') + ->status_is(200) + ->text_is('html head title' => 'eduGAIN Access Check', 'expected title') + ->element_exists('select[id=edugain][name=edugain]', 'SP list'); + + my $res = $t->tx()->res(); + html_ok($res) or diag_file($res, $test_dir); }; -subtest "email selection page, invalid entityid parameter" => sub { - - plan tests => 4; - - local $ENV{REQUEST_METHOD} = 'GET'; - local $ENV{QUERY_STRING} = 'action=select_email&federation=edugain&entityid=foo'; - - my ($out, $err, $rc) = run_executable('access-check-manager.cgi'); - like( - $out, - qr{^Content-Type: text/html; charset=utf8\r\n\r\n}m, - 'HTTP headers' - ); - like( - $out, - qr{<title>eduGAIN Access Check</title>}, - 'page title' - ); - like( - $out, - qr{Error:[\n\s]+format_entityid}, - 'page content contains expected error message' - ); - is($err, '', 'empty stderr'); +named_subtest "email selection page, missing entityid parameter" => sub { + my $t = get_test_object(test => $_[0]); + + $t->get_ok('/step2' => form => {federation => 'edugain'}) + ->status_is(200) + ->text_is('html head title' => 'eduGAIN Access Check', 'expected title') + ->content_like(qr/Error:[\n\s]+missing parameter 'entityid'/, 'expected error message'); + + my $res = $t->tx()->res(); + html_ok($res) or diag_file($res, $test_dir); }; -sub run_executable { - my ($executable, $args) = @_; +named_subtest "email selection page, invalid entityid parameter" => sub { + my $t = get_test_object(test => $_[0]); - my @args = $args ? split(/\s+/, $args) : (); - run( - [ $EXECUTABLE_NAME, '-T', '-I', 'lib', '-I', 't', 'bin/' . $executable, @args ], - \my ($in, $out, $err) - ); - return ($out, $err, $CHILD_ERROR >> 8); -} + $t->get_ok('/step2' => form => {federation => 'edugain', entityid => 'foo'}) + ->status_is(200) + ->text_is('html head title' => 'eduGAIN Access Check', 'expected title') + ->content_like(qr/Error:[\n\s]+format_entityid/, 'expected error message'); + + my $res = $t->tx()->res(); + html_ok($res) or diag_file($res, $test_dir); +};