diff --git a/.github/workflows/perl-tests.yml b/.github/workflows/perl-tests.yml index 5c2bfde6d..9e9fef322 100644 --- a/.github/workflows/perl-tests.yml +++ b/.github/workflows/perl-tests.yml @@ -22,7 +22,7 @@ jobs: # Actions, but not how... -- rjbs, 2023-05-05 run: | apt update - apt install -y rsync + apt install -y rsync default-mysql-server - name: Install prereqs (cpanm, pinned versions) # PAUSE is run (for now?) on v5.16, and the latest versions from the # CPAN don't install on v5.16, so we install these version that do. @@ -35,19 +35,21 @@ jobs: # installing via cpanm that could, instead, be installed from apt. I # may do that later, but for now, it's fine! -- rjbs, 2023-01-07 run: cpanm --notest --installdeps . - - name: Install yath and JUnit renderer - run: cpanm --notest Test2::Harness Test2::Harness::Renderer::JUnit - name: Run the tests - # We disable Test::Perl::Critic because of a bug in - # Test2::Harness::Renderer::JUnit. See: - # https://github.com/cpanel/Test2-Harness-Renderer-JUnit/issues/16 - # -- rjbs, 2023-05-05 - run: | - NO_PERL_CRITIC=1 JUNIT_TEST_FILE="/tmp/test-output.xml" ALLOW_PASSING_TODOS=1 yath test --renderer=Formatter --renderer=JUnit -D - - name: Publish test report - uses: mikepenz/action-junit-report@v3 - if: always() # always run even if the previous step fails - with: - check_name: JUnit Report - report_paths: /tmp/test-output.xml + run: prove -lr -j4 t +# - name: Install yath and JUnit renderer +# run: cpanm --notest Test2::Harness Test2::Harness::Renderer::JUnit +# - name: Run the tests +# # We disable Test::Perl::Critic because of a bug in +# # Test2::Harness::Renderer::JUnit. See: +# # https://github.com/cpanel/Test2-Harness-Renderer-JUnit/issues/16 +# # -- rjbs, 2023-05-05 +# run: | +# NO_PERL_CRITIC=1 JUNIT_TEST_FILE="/tmp/test-output.xml" ALLOW_PASSING_TODOS=1 yath test --renderer=Formatter --renderer=JUnit -D +# - name: Publish test report +# uses: mikepenz/action-junit-report@v3 +# if: always() # always run even if the previous step fails +# with: +# check_name: JUnit Report +# report_paths: /tmp/test-output.xml diff --git a/cpanfile b/cpanfile index 035055580..665e4084f 100644 --- a/cpanfile +++ b/cpanfile @@ -7,7 +7,7 @@ requires 'Class::Singleton'; requires 'Crypt::Eksblowfish::Bcrypt'; requires 'DB_File'; requires 'DBI'; -requires 'DBD::mysql'; +requires 'DBD::mysql', '== 4.050'; requires 'DBD::SQLite'; requires 'Digest::SHA1'; requires 'Email::Address'; diff --git a/t/pause_2017/lib/Test/PAUSE/MySQL.pm b/t/pause_2017/lib/Test/PAUSE/MySQL.pm index b94786565..6eab707ec 100644 --- a/t/pause_2017/lib/Test/PAUSE/MySQL.pm +++ b/t/pause_2017/lib/Test/PAUSE/MySQL.pm @@ -2,9 +2,10 @@ package Test::PAUSE::MySQL; use Test::Builder (); use Test::Requires qw(Test::mysqld); +use Test::Requires qw(File::Which); BEGIN { - unless (-e '/usr/local/mysql/bin/mysql') { + unless (File::Which::which 'mysql') { Test::Builder->new->skip_all("no mysql found, needed for this test") } } diff --git a/t/run_test_class_tests.t b/t/run_test_class_tests.t deleted file mode 100644 index 156e1caee..000000000 --- a/t/run_test_class_tests.t +++ /dev/null @@ -1,24 +0,0 @@ -#!perl - -use strict; -use warnings; - -use Test::Requires qw(Test::mysqld); - -BEGIN { - unless (-e '/usr/local/mysql/bin/mysql') { - Test::Builder->new->skip_all("no mysql found, needed for this test") - } -} - -use lib 't/lib'; -use TestSetup; - -use Test::Class::Load 't/test_classes/'; - -if (@ARGV) { - Test::Class->runtests(@ARGV); -} else { - Test::Class->runtests; -} - diff --git a/t/test_classes/Test/Pause99/Base.pm b/t/test_classes/Test/Pause99/Base.pm deleted file mode 100644 index 38bab24d1..000000000 --- a/t/test_classes/Test/Pause99/Base.pm +++ /dev/null @@ -1,30 +0,0 @@ -package Test::Pause99::Base; - -use strict; -use warnings; - -use base 'Test::Class'; -use Test::More; -use pause_1999::Test::Environment; - -sub warble : Test(startup) { - note ref shift(); -} - -sub new_environment { - my ( $self, %options ) = @_; - # ($env, $author) - return pause_1999::Test::Environment->new_with_author( - %options - ); -} - -sub user_data { - my ( $self, $env, $user ) = @_; - my $user_data_st - = $env->authen_dbh->prepare("SELECT * FROM usertable WHERE user = ?"); - $user_data_st->execute($user); - return $user_data_st->fetchrow_hashref; -} - -1; \ No newline at end of file diff --git a/t/test_classes/Test/Pause99/Web/Auth.pm b/t/test_classes/Test/Pause99/Web/Auth.pm deleted file mode 100644 index 3b57165e1..000000000 --- a/t/test_classes/Test/Pause99/Web/Auth.pm +++ /dev/null @@ -1,96 +0,0 @@ -package Test::Pause99::Web::Auth; - -use strict; -use warnings; - -use Time::Local qw/timelocal/; -use HTTP::Request::Common; -use pause_1999::Test::Environment; -use PAUSE::Crypt; - -use Test::More; -use base 'Test::Pause99::Web::Base'; - -sub test_disabled_account : Tests(2) { - my $t = shift; - my ( $env, $author, $m ) = $t->new_andreas(); - - $env->mod_dbh->prepare( " - UPDATE users SET ustatus = 'nologin' WHERE userid = ? - " )->execute( $author->username ); - - $m->set_user( $author ); - - $m->homepage; - - is $m->mech->status, 200, "Code matches"; - $m->mech->content_like( qr/Many users with an insecure password/, - "Content matches" ); - -} - -sub test_basic : Tests(12) { - my $t = shift; - my ( $env, $author, $m ) = $t->new_andreas(); - - my $author_fullname = $author->fullname; - - # Before logging in we should have no last-seen, and the password should be - # old-style crypted - { - my $user_data = $t->user_data( $env, $author->username ); - is( $user_data->{'lastvisit'}, undef, "User has never been seen" ); - is( $user_data->{'password'}, - $author->password_crypted, "Oldstyle crypt() password" ); - } - - # Test basic authentication - my @no_auth = ( 401 => qr/Authorization required/ ); - for ( - [ undef, @no_auth, 'No username or password' ], - [ [ foo => 'foo' ], @no_auth, 'Unknown user' ], - [ [ $author->username, 'foo' ], @no_auth, 'Wrong password' ], - [ [ $author->username, $author->password ], - 200 => qr($author_fullname), - 'Correct credentials' - ], - ) - { - my ( $credentials, $code, $content, $name ) = @$_; - - if ($credentials) { - $m->set_user( - { username => $credentials->[0], - password => $credentials->[1] - } - ); - } - else { - $m->clear_user; - } - - $m->homepage; - - is $m->mech->status, $code, "$name: Code matches"; - $m->mech->content_like( $content, "$name: Content matches" ); - } - - # Get the user's data from auth database - - { - my $user_data = $t->user_data( $env, $author->username ); - my @time_pieces = reverse split( /\D/, $user_data->{'lastvisit'} ); - $time_pieces[4] -= 1; - my $last_seen_epoch = timelocal(@time_pieces); - my $ago = time - $last_seen_epoch; - ok( $ago < 120, "User has now been seen today ($ago seconds ago)" ); - ok( PAUSE::Crypt::password_verify( - $author->password, $user_data->{'password'} - ), - "Password updated to bcrypt" - ); - } - -} - -1; diff --git a/t/test_classes/Test/Pause99/Web/Base.pm b/t/test_classes/Test/Pause99/Web/Base.pm deleted file mode 100644 index 14707a647..000000000 --- a/t/test_classes/Test/Pause99/Web/Base.pm +++ /dev/null @@ -1,26 +0,0 @@ -package Test::Pause99::Web::Base; - -use strict; -use warnings; - -use base 'Test::Pause99::Base'; -use Test::More; - -sub new_environment { - my ( $self, %options ) = @_; - - my ( $env, $author ) = $self->SUPER::new_environment( %options ); - my $site_model = $env->site_model($author); - - return ( $env, $author, $site_model ); -} - -sub new_andreas { - my $self = shift; - return $self->new_environment( - username => 'ANDK', - asciiname => 'Andreas K', - ); -} - -1; \ No newline at end of file diff --git a/t/test_classes/Test/Pause99/Web/ChangePassword.pm b/t/test_classes/Test/Pause99/Web/ChangePassword.pm deleted file mode 100644 index 493d87079..000000000 --- a/t/test_classes/Test/Pause99/Web/ChangePassword.pm +++ /dev/null @@ -1,39 +0,0 @@ -package Test::Pause99::Web::ChangePassword; - -use strict; -use warnings; - -use Test::More; -use base 'Test::Pause99::Web::Base'; - -sub test_basic : Tests(5) { - my $t = shift; - my ( $env, $author, $m ) = $t->new_andreas(); - - # Try some invalid form submissions - for ( - [ '', '', "Empty", qr/Please fill in the form/ ], - [ 'foo', 'moo', "Non-matching", qr/passwords didn't match/ ], - ) - { - my ( $pw1, $pw2, $name, $match ) = @$_; - $m->change_passwd->change_passwd__submit( $pw1, $pw2 ); - $m->mech->content_like( $match, "$name passwords caught" ); - } - - # Now try a matching password - $m->change_passwd->change_passwd__submit( 'moo', 'moo' ); - $m->mech->content_like( qr/New password stored and enabled/, - "New password message shown" ); - - # 401 if we re-use old credentials - $m->homepage; - is( $m->mech->status, 401, "Old auth fails now we have a new password" ); - - # New credentials work - $m->mech->credentials( $author->username, 'moo' ); - $m->homepage; - is( $m->mech->status, 200, "New password authenticates successfully" ); -} - -1; \ No newline at end of file diff --git a/t/test_classes/Test/Pause99/Web/ReadOnlyPages.pm b/t/test_classes/Test/Pause99/Web/ReadOnlyPages.pm deleted file mode 100644 index 69ee32fba..000000000 --- a/t/test_classes/Test/Pause99/Web/ReadOnlyPages.pm +++ /dev/null @@ -1,40 +0,0 @@ -package Test::Pause99::Web::ReadOnlyPages; - -use strict; -use warnings; -use File::Temp qw/tempdir/; -use Path::Class qw/dir file/; - -use Test::More; -use base 'Test::Pause99::Web::Base'; - -sub test_basic : Tests(4) { - my $t = shift; - my ( $env, $author, $m ) = $t->new_andreas(); - -# File list - - # Create some files in a directory - my $root_dir = tempdir( CLEANUP => 0 ); - my $dir = dir("$root_dir/A/AN/ANDK"); - $dir->mkpath(); - - $dir->file('foo')->spew('0'); - $dir->file('bar')->spew('0123456789'); - - my $expected_files = { foo => 1, bar => 10 }; - - local $PAUSE::Config->{MLROOT} = $root_dir; - - for my $query (qw/show_files delete_files/) { - my $data = $m->$query->parse(); - my %files = map { @$_{qw/filename size/} } @{ $data->{'file_list'} }; - is_deeply( \%files, $expected_files, - "$query: File list finds two author files" ); - is( $data->{'author_directory'}, - 'authors/id/A/AN/ANDK', - "$query: Author directory looks sensible" ); - } -} - -1; \ No newline at end of file diff --git a/t/test_classes/Test/Pause99/Web/ShowEmailForAuthor.pm b/t/test_classes/Test/Pause99/Web/ShowEmailForAuthor.pm deleted file mode 100644 index 7b68c3bcd..000000000 --- a/t/test_classes/Test/Pause99/Web/ShowEmailForAuthor.pm +++ /dev/null @@ -1,91 +0,0 @@ -package Test::Pause99::Web::ShowEmailForAuthor; - -use strict; -use warnings; - -use Test::More; -use base 'Test::Pause99::Web::Base'; - -use pause_1999::Test::Environment; -use pause_1999::Test::Fixtures::Author; - -sub test_basic : Tests(3) { - my $t = shift; - - my $env = pause_1999::Test::Environment->new(); - - # Need an admin user who can actually view this - my $admin = pause_1999::Test::Fixtures::Author->new( - environment => $env, - username => 'ADMIN', - asciiname => 'Admin user', - ugroup => [qw/admin/], - ); - - # Public email is in mod:users.email - # If mod:users.cpan_mail_alias is publ, we always use that - # If mod:users.cpan_mail_alias is secr, we use authen.secretemail - # If there's nothing in either, we don't show an entry - # - # So the logic table looks something like - - my %expected = ( ADMIN => 'ADMIN@example.com', ); - - for ( - #username public secretemail cpan_mail_alias shouldshow - [ user1 => 'public@user1', 'secret@user1', 'publ', 'public@user1' ], - [ user2 => 'public@user2', 'secret@user2', 'secr', 'secret@user2' ], - [ user3 => 'public@user3', 'secret@user3', 'none', undef ], - [ user4 => 'public@user4', undef, 'secr', [] ], - ) - { - my ($username, $public_email, $secret_email, - $cpan_mail_alias, $should_show - ) = @$_; - - # Add to the database - $env->authen_dbh->prepare( " - INSERT INTO usertable ( user ) VALUES ( ? ) - " )->execute($username); - $env->authen_dbh->prepare( " - UPDATE usertable SET secretemail = ? WHERE user = ? - " )->execute( $secret_email, $username ) if defined $secret_email; - - $env->mod_dbh->prepare( " - INSERT INTO users ( userid, email, cpan_mail_alias ) VALUES ( ?, ?, ? ) - " )->execute( $username, $public_email, $cpan_mail_alias ); - - if ( defined $should_show ) { - if ( ref $should_show ) { - $expected{$username} = undef; - } - else { - $expected{$username} = $should_show; - } - } - } - - my $m = $env->site_model($admin); - - my $received = $m->email_for_admin->parse()->{'email_for_admin'}; - - my $yaml_received = $m->email_for_admin__yaml->parse()->{'yaml'}; - - is_deeply( $received, \%expected, "Correct data in email_for_admin" ); - is_deeply( $yaml_received, \%expected, - "Correct data in the YAML version" ); - - # If we're a non-admin user, we should get nothing at all - my $non_admin = pause_1999::Test::Fixtures::Author->new( - environment => $env, - username => 'NONADMIN', - asciiname => 'Not an admin user', - ); - - $m->set_user($non_admin); - $m->email_for_admin; - $m->mech->title_is( 'PAUSE: menu', - "No email_for_admin view for non-admins" ); -} - -1; diff --git a/t/test_classes/Test/Pause99/Web/SimplePageTitle.pm b/t/test_classes/Test/Pause99/Web/SimplePageTitle.pm deleted file mode 100644 index 7e68095d1..000000000 --- a/t/test_classes/Test/Pause99/Web/SimplePageTitle.pm +++ /dev/null @@ -1,176 +0,0 @@ -package Test::Pause99::Web::SimplePageTitle; - -use strict; -use warnings; -use pause_1999::config; - -use Storable qw/dclone/; -use Test::More; -use Test::Differences; -use base 'Test::Pause99::Web::Base'; - -my @groups = qw/mlrepr admin user public/; -my @groups_p = grep { $_ ne 'public' } @groups; - -# Runs through the list of expected menu items, checks they appear -# for the three user types, and check they're only available for -# the relevant users -sub test_get_pages : Tests(248) { - my $t = shift; - - my %user_actions = %{$pause_1999::config::DEFAULT_USER_ACTIONS}; - - # Remove known 500s on the test box - delete $user_actions{$_} for qw/check_xhtml index_users/; - - # What actions do we expect, based on the permissions - my %expected; - my %defaults = ( - map { - $_ => { map { $_ => { status => 'no auth', auth_fail => 'soft' } } - qw/with_auth without_auth/ } - } @groups - ); - - # You should be prompted to auth if you're not, and you need to be - $defaults{'public'}{'with_auth'} - = { status => 'no auth', auth_fail => 'hard' }; - - for my $action ( sort keys %user_actions ) { - my $page_data = { %{ $user_actions{$action} } }; - - my $node = $expected{$action} = dclone \%defaults; - - # mlrepr is a fake group that admin is also part of - if ( $page_data->{'priv'} eq 'admin' ) { - $node->{'admin'}{'with_auth'} - = { status => 'auth', header => $page_data->{'verb'} }; - $node->{'mlrepr'}{'with_auth'} - = { status => 'auth', header => $page_data->{'verb'} }; - } - - elsif ( $page_data->{'priv'} eq 'mlrepr' ) { - $node->{'mlrepr'}{'with_auth'} - = { status => 'auth', header => $page_data->{'verb'} }; - } - - elsif ( $page_data->{'priv'} eq 'user' ) { - $node->{$_}{'with_auth'} - = { status => 'auth', header => $page_data->{'verb'} } - for @groups_p; - } - - elsif ( $page_data->{'priv'} eq 'public' ) { - for my $permission (@groups) { - for my $type (qw/with_auth without_auth/) { - $node->{$permission}->{$type} - = { status => 'auth', header => $page_data->{'verb'} } - unless ( $permission eq 'public' - && $type eq 'with_auth' ); - } - } - } - } - - # Special case: mailpw is a noop if you hit it via authen_query - $expected{'mailpw'}{$_}{'with_auth'} - = { status => 'no auth', auth_fail => 'soft' } - for @groups_p; - - # Special case: normal admin can show_ml_repr - $expected{'show_ml_repr'}{'admin'} = $expected{'show_ml_repr'}{'mlrepr'}; - - # Special case: admins can nuke the server(!!!) - $expected{'coredump'}{'admin'}{'with_auth'} = $expected{'coredump'}{'mlrepr'}{'with_auth'} - = { status => 500 }; - - - my %results; - - # Test for each user permission level - for my $permission (@groups) { - my ( $env, $author, $m ) = $t->new_environment( - username => 'ANDK', - asciiname => 'blah', - ugroup => [ $permission eq 'mlrepr' ? 'admin' : $permission ], - ); - - if ( $permission ne 'public' ) { - $m->set_user($author); - } - else { - $m->clear_user; - } - - # mlrepr is a lie -- it's an admin who's in list2user - if ( $permission eq 'mlrepr' ) { - $env->mod_dbh->do( - "INSERT INTO list2user VALUES ('ANDK','ANDK');"); - $env->mod_dbh->do( - "UPDATE users SET isa_list = 'y' WHERE userid = 'ANDK';"); - } - - for my $action ( sort keys %user_actions ) { - my $data = $user_actions{$action}; - - my $auth_url = $m->url($action); - my $unauth_url = $auth_url; - $unauth_url =~ s/authen//; - - for ( - [ with_auth => $auth_url ], - [ without_auth => $unauth_url ] - ) - { - my ( $type, $url ) = @$_; - - note "[$permission] [$type] [$url]"; - if ( $action eq 'tail_logfile' ) { - local $PAUSE::Config->{PAUSE_LOG} = '/dev/null'; - $m->mech->get($url); - } else { - $m->mech->get($url); - } - - my $result = $results{$action}->{$permission}->{$type} ||= {}; - - if ( $m->mech->success() ) { - my $title = eval { $m->parse('title_only')->{'title'} } - || warn $@; - my $header = eval { $m->parse('title_only')->{'header'} } - || warn $@; - - if ( $title eq 'PAUSE: ' . $action ) { - $result->{'status'} = 'auth'; - $result->{'header'} = $header; - - } - elsif ( $title =~ m/^PAUSE: menu/ ) { - $result->{'status'} = 'no auth'; - $result->{'auth_fail'} = 'soft'; - } - else { - $result->{'status'} = 'unknown'; - } - } - elsif ( $m->mech->status == 401 ) { - $result->{'status'} = 'no auth'; - $result->{'auth_fail'} = 'hard'; - } - else { - $result->{'status'} = $m->mech->status; - } - - eq_or_diff( - $result, - $expected{$action}->{$permission}->{$type}, - "Action[$action] Permission[$permission] Type[$type] as expected" - ); - - } - } - } - -} - -1; diff --git a/t/test_classes/Test/Pause99/Web/StaticPage.pm b/t/test_classes/Test/Pause99/Web/StaticPage.pm deleted file mode 100644 index edfcad89a..000000000 --- a/t/test_classes/Test/Pause99/Web/StaticPage.pm +++ /dev/null @@ -1,33 +0,0 @@ -package Test::Pause99::Web::StaticPage; - -use strict; -use warnings; - -use Time::Local qw/timelocal/; -use HTTP::Request::Common; -use pause_1999::Test::Environment; -use PAUSE::Crypt; - -use Test::More; -use base 'Test::Pause99::Web::Base'; - -sub test_static : Tests(5) { - my $t = shift; - my ( $env, $author, $m ) = $t->new_andreas(); - - $m->pausecss; - is $m->mech->status, 200, "Code matches"; - $m->mech->content_like( qr/actionresponse/, - "Content matches" ); - - $m->unknownpath; - is $m->mech->status, 404, "Code matches"; - - $m->challengereadme; - is $m->mech->status, 200, "Code matches"; - $m->mech->content_like( qr/Letsencrypt/, - "Content matches" ); - -} - -1;