diff --git a/t/lib/PAUSE/TestPAUSE.pm b/t/lib/PAUSE/TestPAUSE.pm index e423f6bff..ce747c193 100644 --- a/t/lib/PAUSE/TestPAUSE.pm +++ b/t/lib/PAUSE/TestPAUSE.pm @@ -2,6 +2,7 @@ package PAUSE::TestPAUSE; use Moose; use MooseX::StrictConstructor; +use v5.36.0; use autodie; use DBI; @@ -13,6 +14,7 @@ use File::pushd; use File::Temp (); use File::Which; use Path::Class; +use Process::Status; # This one, we don't expect to be used. In a weird world, we'd mark it fatal # or something so we could say "nothing should log outside of test code." @@ -235,6 +237,28 @@ sub upload_author_file { return File::Spec->catfile($author_dir, $file); } +sub upload_author_garbage { + my ($self, $author, $file) = @_; + + $author = uc $author; + my $cpan_root = File::Spec->catdir($self->tmpdir, 'cpan'); + my $author_dir = File::Spec->catdir( + $cpan_root, + qw(authors id), + (substr $author, 0, 1), + (substr $author, 0, 2), + $author, + ); + + make_path( $author_dir ); + my $target = File::Spec->catfile($author_dir, $file); + system('dd', 'if=/dev/random', "of=$target", "count=20", "status=none"); # write 20k + + Process::Status->assert_ok("dd from /dev/random to $target"); + + return $target; +} + has pause_config_overrides => ( is => 'ro', isa => 'HashRef', @@ -349,12 +373,24 @@ sub test_reindex { die "stray mail in test mail trap before reindex" if @stray_mail; + my $existing_log_events = $self->logger->events->@*; + if ($arg->{pick}) { my $dbh = PAUSE::dbh(); $dbh->do("DELETE FROM distmtimes WHERE dist = ?", undef, $_) for @{ $arg->{pick} }; } + my sub filestate ($file) { + return ';;' unless -e $file; + my @stat = stat $file; + return join q{;}, @stat[0,1,7]; # dev, ino, size + } + + my $package_file = $self->tmpdir->file(qw(cpan modules 02packages.details.txt.gz)); + + my $old_package_state = filestate($package_file); + PAUSE::mldistwatch->new({ sleep => 0, ($arg->{pick} ? (pick => $arg->{pick}) : ()), @@ -362,87 +398,29 @@ sub test_reindex { $arg->{after}->($self->tmpdir) if $arg->{after}; + # The first $existing_log_events were already there. We only care about + # once added during the indexer run. + my @log_events = $self->logger->events->@*; + splice @log_events, 0, $existing_log_events; + my @deliveries = Email::Sender::Simple->default_transport->deliveries; Email::Sender::Simple->default_transport->clear_deliveries; + my $new_package_state = filestate($package_file); + return PAUSE::TestPAUSE::Result->new({ tmpdir => $self->tmpdir, config_overrides => $self->pause_config_overrides, authen_db_file => File::Spec->catfile($self->db_root, 'authen.sqlite'), mod_db_file => File::Spec->catfile($self->db_root, 'mod.sqlite'), deliveries => \@deliveries, + log_events => \@log_events, + updated_02packages => $old_package_state ne $new_package_state, }); }); } -has _file_index => ( - is => 'ro', - default => sub { {} }, -); - -sub file_updated_ok { - my ($self, $filename, $desc) = @_; - $desc = defined $desc ? "$desc: " : q{}; - - local $Test::Builder::Level = $Test::Builder::Level + 1; - - my $tmpdir = $self->tmpdir . ""; - my $prettyname = $filename =~ s/\Q$tmpdir/\${TEST}/r; - - unless (-e $filename) { - return Test::More::fail("$desc$prettyname not updated"); - } - - my ($dev, $ino) = stat $filename; - - my $old = $self->_file_index->{ $filename }; - - unless (defined $old) { - $self->_file_index->{$filename} = "$dev,$ino"; - return Test::More::pass("$desc$prettyname updated (created)"); - } - - my $ok = Test::More::ok( - $old ne "$dev,$ino", - "$desc$prettyname updated", - ); - - $self->_file_index->{$filename} = "$dev,$ino"; - return $ok; -} - -sub file_not_updated_ok { - my ($self, $filename, $desc) = @_; - $desc = defined $desc ? "$desc: " : q{}; - - local $Test::Builder::Level = $Test::Builder::Level + 1; - - my $old = $self->_file_index->{ $filename }; - - my $tmpdir = $self->tmpdir . ""; - my $prettyname = $filename =~ s/\Q$tmpdir/\${TEST}/r; - - unless (-e $filename) { - return Test::More::fail("$desc$prettyname deleted") if $old; - return Test::More::pass("$desc$prettyname not created (thus not updated)"); - } - - my ($dev, $ino) = stat $filename; - - unless (defined $old) { - $self->_file_index->{$filename} = "$dev,$ino"; - return Test::More::fail("$desc$prettyname updated (created)"); - } - - my $ok = Test::More::ok( - $old eq "$dev,$ino", - "$desc$prettyname not updated", - ); - - return $ok; -} - sub run_shell { my ($self) = @_; diff --git a/t/lib/PAUSE/TestPAUSE/Result.pm b/t/lib/PAUSE/TestPAUSE/Result.pm index 999382fb3..007ae0714 100644 --- a/t/lib/PAUSE/TestPAUSE/Result.pm +++ b/t/lib/PAUSE/TestPAUSE/Result.pm @@ -2,6 +2,8 @@ package PAUSE::TestPAUSE::Result; use Moose; use MooseX::StrictConstructor; +use v5.36.0; + use DBI; use Parse::CPAN::Packages; use Test::Deep qw(cmp_deeply superhashof methods); @@ -112,7 +114,7 @@ sub perm_list_ok { ->file(qw(06perms.txt.gz)); our $GZIP = $PAUSE::Config->{GZIP_PATH}; - open my $fh, "$GZIP --stdout --uncompress $index_06|" + open my $fh, "-|", "$GZIP --stdout --uncompress $index_06" or die "can't open $index_06 for reading with gip: $!"; my (@header, @data); @@ -130,6 +132,8 @@ sub perm_list_ok { } } + close($fh) or die "error reading $index_06: $!"; + is_deeply(\%permissions, $want, "permissions look correct in 06perms") or diag explain(\%permissions); } @@ -172,4 +176,42 @@ sub email_ok { } } +has updated_02packages => ( + is => 'ro', + isa => 'Bool', + required => 1, +); + +sub assert_index_updated ($self, $desc = "02packages was changed") { + local $Test::Builder::Level = $Test::Builder::Level + 1; + ok($self->updated_02packages, $desc); +} + +sub assert_index_not_updated ($self, $desc = "02packages was not changed") { + local $Test::Builder::Level = $Test::Builder::Level + 1; + ok(!$self->updated_02packages, $desc); +} + +has log_events => ( + isa => 'ArrayRef', + required => 1, + traits => [ 'Array' ], + handles => { log_events => 'elements' }, +); + +sub logged_event_like ($self, $qr, $desc = "found matching log line") { + local $Test::Builder::Level = $Test::Builder::Level + 1; + + ok( + (grep {; $_->{message} =~ $qr } $self->log_events), + $desc, + ); +} + +sub diag_log_messages ($self) { + local $Test::Builder::Level = $Test::Builder::Level + 1; + + diag($_->{message}) for $self->log_events; +} + 1; diff --git a/t/mldistwatch-big.t b/t/mldistwatch-big.t index c468d9cdb..e85cb8db2 100644 --- a/t/mldistwatch-big.t +++ b/t/mldistwatch-big.t @@ -11,23 +11,36 @@ use PAUSE::TestPAUSE; use Test::More; +subtest "the simplest thing that could possibly work" => sub { + for my $ext (qw( tar.gz zip )) { + my $pause = PAUSE::TestPAUSE->init_new; + $pause->upload_author_fake(AIDEN => "Shoe-Keeper-1.23.$ext"); + + my $result = $pause->test_reindex; + + $result->diag_log_messages; + + $result->assert_index_updated; + + $result->package_list_ok( + [ { package => 'Shoe::Keeper', version => '1.23' } ], + ); + + $result->perm_list_ok({ 'Shoe::Keeper' => { f => 'AIDEN' } }); + + $result->email_ok( + [ { subject => "PAUSE indexer report AIDEN/Shoe-Keeper-1.23.$ext" } ], + ); + } +}; + subtest "first indexing" => sub { my $pause = PAUSE::TestPAUSE->init_new; $pause->import_author_root('corpus/mld/001/authors'); my $result = $pause->test_reindex; - $pause->file_updated_ok( - $result->tmpdir - ->file(qw(cpan modules 02packages.details.txt.gz)), - "our indexer indexed", - ); - - $pause->file_updated_ok( - $result->tmpdir - ->file(qw(cpan modules 03modlist.data.gz)), - "our indexer indexed", - ); + $result->assert_index_updated; $result->package_list_ok( [ @@ -97,11 +110,7 @@ for my $uploader (qw(FCOME CMAINT)) { { my $result = $pause->test_reindex; - $pause->file_updated_ok( - $result->tmpdir - ->file(qw(cpan modules 02packages.details.txt.gz)), - "our indexer indexed", - ); + $result->assert_index_updated; $result->package_list_ok( [ @@ -222,11 +231,7 @@ subtest "case mismatch, authorized for original" => sub { my $result = $pause->test_reindex; - $pause->file_updated_ok( - $result->tmpdir - ->file(qw(cpan modules 02packages.details.txt.gz)), - "our indexer indexed", - ); + $result->assert_index_updated; $result->package_list_ok( [ diff --git a/t/mldistwatch-db.t b/t/mldistwatch-db.t index bc85f45eb..e99f0d291 100644 --- a/t/mldistwatch-db.t +++ b/t/mldistwatch-db.t @@ -57,10 +57,7 @@ subtest "retry indexing on db failure, only three times" => sub { is($x, 3, "we tried three times, and no more"); - $pause->file_not_updated_ok( - $result->tmpdir->file(qw(cpan modules 02packages.details.txt.gz)), - "did not reindex", - ); + $result->assert_index_not_updated; $result->email_ok( [ diff --git a/t/mldistwatch-misc.t b/t/mldistwatch-misc.t index f75a4d290..8b0ae6acb 100644 --- a/t/mldistwatch-misc.t +++ b/t/mldistwatch-misc.t @@ -1,13 +1,14 @@ use strict; use warnings; -use 5.10.1; +use v5.36.0; use lib 't/lib'; use lib 't/privatelib'; # Stub PrivatePAUSE use File::Spec; use PAUSE; use PAUSE::TestPAUSE; +use Process::Status; use Test::More; @@ -17,10 +18,7 @@ subtest "do not index bare .pm but report rejection" => sub { my $result = $pause->test_reindex; - $pause->file_not_updated_ok( - $result->tmpdir->file(qw(cpan modules 02packages.details.txt.gz)), - "did not reindex", - ); + $result->assert_index_not_updated; $result->email_ok( [ @@ -29,48 +27,6 @@ subtest "do not index bare .pm but report rejection" => sub { ); }; -subtest "perl-\\d should not get indexed" => sub { - my $pause = PAUSE::TestPAUSE->init_new; - - $pause->upload_author_fake(PLUGH => 'Soft-Ware-2'); - - $pause->upload_author_fake(PLUGH => { - name => 'perl', - version => 6, - packages => [ 'perl::rocks' ], - }); - - my $result = $pause->test_reindex; - - $result->package_list_ok( - [ - { package => 'Soft::Ware', version => '2' }, - ], - ); - - # TODO: send a report saying 'no perl-X allowed' -}; - -subtest "should index single-life dev vers. modules in perl dist" => sub { - plan skip_all => "this test only run when perl-5.20.2.tar.gz found" - unless -e 'perl-5.20.2.tar.gz'; - - my $pause = PAUSE::TestPAUSE->init_new; - - my $initial_result = $pause->test_reindex; - - my $dbh = $initial_result->connect_authen_db; - die "couldn't make OPRIME a pumpking" - unless $dbh->do("INSERT INTO grouptable (user, ugroup) VALUES ('OPRIME', 'pumpking')"); - - $pause->upload_author_file('OPRIME', 'perl-5.20.2.tar.gz'); - - my $result = $pause->test_reindex; - - my $packages = $result->packages_data; - ok($packages->package("POSIX"), "we index POSIX in a dev version"); -}; - sub refused_index_test { my ($arg) = @_; @@ -156,6 +112,7 @@ subtest "do not index if meta has release_status <> stable" => sub { { my $result = $pause->test_reindex; + $result->assert_index_updated; $result->email_ok([ { subject => 'PAUSE indexer report OPRIME/Pie-Eater-1.23.tar.gz' }, @@ -175,6 +132,7 @@ subtest "do not index if meta has release_status <> stable" => sub { ); my $result = $pause->test_reindex; + $result->assert_index_updated; $result->package_list_ok([ { package => 'Pie::Eater', version => '1.23' }, @@ -213,6 +171,7 @@ subtest "warn when pkg and module match only case insensitively" => sub { }); my $result = $pause->test_reindex; + $result->assert_index_updated; $result->package_list_ok( [ @@ -259,6 +218,7 @@ subtest "(package NAME VERSION BLOCK) and (package NAME BLOCK)" => sub { }); my $result = $pause->test_reindex; + $result->assert_index_updated; $result->package_list_ok( [ @@ -291,12 +251,7 @@ subtest "check various forms of version" => sub { }); my $result = $pause->test_reindex; - - $pause->file_updated_ok( - $result->tmpdir - ->file(qw(cpan modules 02packages.details.txt.gz)), - "our indexer indexed", - ); + $result->assert_index_updated; # VVVVVV - just fine! index it # VVVVVV::Bogus - utterly busted, give up @@ -339,6 +294,7 @@ EOT }); my $result = $pause->test_reindex; + $result->assert_index_updated; $result->package_list_ok([ { package => 'Globby::Version', version => '1.234' }, @@ -360,12 +316,7 @@ subtest "check overlong versions" => sub { }); my $result = $pause->test_reindex; - - $pause->file_not_updated_ok( - $result->tmpdir - ->file(qw(cpan modules 02packages.details.txt.gz)), - "there were no things to update", - ); + $result->assert_index_not_updated; my $etoolong = sub { like( @@ -389,6 +340,35 @@ subtest "check overlong versions" => sub { ); }; +subtest "version is not a lax version string" => sub { + my $pause = PAUSE::TestPAUSE->init_new; + $pause->upload_author_fake(MONSTER => 'Hex-Version-1.234.tar.gz', { + append => [ + { + file => "lib/Hex/Version/NoJoke.pm", + content => <<'EOT', +use strict; +use warnings; +package Hex::Version::NoJoke; +our $VERSION = '0x1p-1'; +1; +EOT + } + ], + }); + + my $result = $pause->test_reindex; + + $result->package_list_ok([ + { package => 'Hex::Version', version => '1.234' }, + ]); + + $result->logged_event_like( + qr/error with version/, + "0x1p-1 is a bad version", + ); +}; + subtest "case-changing imports" => sub { my $pause = PAUSE::TestPAUSE->init_new; @@ -406,6 +386,7 @@ subtest "case-changing imports" => sub { }); my $result = $pause->test_reindex; + $result->assert_index_updated; $result->package_list_ok([ { package => 'Foo::Bar', version => '0.001' }, @@ -426,6 +407,7 @@ subtest "case-changing imports" => sub { }); my $result = $pause->test_reindex; + $result->assert_index_updated; $result->package_list_ok([ { package => 'foo::bar', version => '0.002' }, @@ -445,6 +427,8 @@ subtest "check perl6 distribution indexing" => sub { $pause->import_author_root('corpus/mld/perl6/authors'); my $result = $pause->test_reindex; + $result->assert_index_not_updated("p5 index not updated on p6 upload"); + $result->p6dists_ok( [ { name => 'Inline', ver => '1.1' }, @@ -504,6 +488,7 @@ EOT }); my $result = $pause->test_reindex; + $result->assert_index_updated; $result->package_list_ok([ { package => 'Version::Cmp', version => '1.234' }, @@ -539,6 +524,7 @@ EOT }); my $result = $pause->test_reindex; + $result->assert_index_updated; $result->package_list_ok([ { package => 'Lingua::JA::Numbers', version => '0.05' }, @@ -556,12 +542,7 @@ subtest "do not index dists without META file" => sub { }); my $result = $pause->test_reindex; - - $pause->file_not_updated_ok( - $result->tmpdir - ->file(qw(cpan modules 02packages.details.txt.gz)), - "there were no things to update", - ); + $result->assert_index_not_updated; my $nometa = sub { like( @@ -581,6 +562,113 @@ subtest "do not index dists without META file" => sub { ); }; +subtest "do not index dists without trial versions" => sub { + for my $test ( + { desc => "low line in version", munger => sub { $_[0] =~ s/22/2_2/r } }, + { desc => "TRIAL in version", munger => sub { $_[0] =~ s/22/22-TRIAL/r } }, + ) { + subtest $test->{desc} => sub { + my $pause = PAUSE::TestPAUSE->init_new; + + # Module::Faker will give us a bit of grief for uploading a file called + # 1.2_2 because it wants to set META release status to stable, and the + # low line is in conflict with that. Rather than muck about making this + # permissible, I'm going to write out the archive, then rename it before + # indexing. + my $old_name = $pause->upload_author_fake(PERSON => 'Just-Testing-1.22.tar.gz'); + my $new_name = $test->{munger}->($old_name); + + rename($old_name, $new_name) or die "can't rename $old_name: $!"; + + my $result = $pause->test_reindex; + + $result->assert_index_not_updated; + + $result->logged_event_like( + qr{\Qdist is a developer release}, + "we do not index trial-like filenames", + ); + }; + } +}; + +subtest "updates to existing packages " => sub { + my $pause = PAUSE::TestPAUSE->init_new; + + subtest "first version" => sub { + $pause->upload_author_fake(MTRON => 'Eye-Meeter-1.234.tar.gz'); + + my $result = $pause->test_reindex; + + $result->assert_index_updated; + + $result->package_list_ok([ + { package => 'Eye::Meeter', version => '1.234' }, + ]); + }; + + subtest "second version" => sub { + $pause->upload_author_fake(MTRON => 'Eye-Meeter-1.235.tar.gz'); + + my $result = $pause->test_reindex; + + $result->assert_index_updated; + + $result->package_list_ok([ + { package => 'Eye::Meeter', version => '1.235' }, + ]); + }; +}; + +subtest "the notorious version zero" => sub { + # When we've got two dists both of which provide version 0 (or version + # undef), we pick the winner by mtime. I hate this, but I will write a test + # so that I can someday more confidently forbid anything like this. Good + # grief. -- rjbs, 2024-05-11 + my $pause = PAUSE::TestPAUSE->init_new; + + my sub touch_file ($filename, $i) { + my $secs = sprintf '%02i', $i; + system("touch", '-m', '-d', "2020-01-02T03:04:${secs}Z", $filename); + Process::Status->assert_ok("touching $filename"); + } + + # First, a zero version package. + # Then, again, but file mtime is older. We keep the package on file. + # Then, again, but file mtime is newer. We switch to the new package. + my @tests = ( + { desc => "first upload", offset => 10, dist_v => '1.0', want => '1.0' }, + { desc => "second upload", offset => 5, dist_v => '1.1', want => '1.0' }, + { desc => "third upload", offset => 15, dist_v => '1.2', want => '1.2' }, + ); + + for my $test (@tests) { + subtest $test->{desc} => sub { + my $file = $pause->upload_author_fake(CIPHER => { + name => 'Zero', + version => $test->{dist_v}, + packages => [ + 'Zero' => { version => '0.000' }, + ], + }); + + touch_file($file, $test->{offset}); + + my $result = $pause->test_reindex; + + $result->package_list_ok([ + { package => 'Zero', version => '0.000' }, + ]); + + my $p = $result->packages_data; + + my $dist = $p->latest_distribution("Zero"); + is($dist->prefix, "C/CI/CIPHER/Zero-$test->{want}.tar.gz", "right dist indexed"); + is($dist->version, $test->{want}, "with the right version"); + }; + } +}; + done_testing; # Local Variables: diff --git a/t/mldistwatch-perl.t b/t/mldistwatch-perl.t new file mode 100644 index 000000000..ef0a0f064 --- /dev/null +++ b/t/mldistwatch-perl.t @@ -0,0 +1,272 @@ +use strict; +use warnings; + +use 5.10.1; +use lib 't/lib'; +use lib 't/privatelib'; # Stub PrivatePAUSE + +use File::Spec; +use PAUSE; +use PAUSE::TestPAUSE; + +use Test::More; + +subtest "perl-\\d should not get indexed (not really perl)" => sub { + my $pause = PAUSE::TestPAUSE->init_new; + + $pause->upload_author_fake(PLUGH => 'Soft-Ware-2'); + + $pause->upload_author_fake(PLUGH => { + name => 'perl', + version => 6, + packages => [ 'perl::rocks' ], + }); + + my $result = $pause->test_reindex; + + $result->package_list_ok( + [ + { package => 'Soft::Ware', version => '2' }, + ], + ); + + # TODO: send a report saying 'no perl-X allowed' + + $result->logged_event_like( + qr{dist is an unofficial perl-like release}, + "perl-6.tar.gz is not a really perl-like file", + ); +}; + +subtest "should index single-life dev vers. modules in perl dist" => sub { + plan skip_all => "this test only run when perl-5.20.2.tar.gz found" + unless -e 'perl-5.20.2.tar.gz'; + + my $pause = PAUSE::TestPAUSE->init_new; + + my $initial_result = $pause->test_reindex; + + my $dbh = $initial_result->connect_authen_db; + die "couldn't make OPRIME a pumpking" + unless $dbh->do("INSERT INTO grouptable (user, ugroup) VALUES ('OPRIME', 'pumpking')"); + + $pause->upload_author_file('OPRIME', 'perl-5.20.2.tar.gz'); + + my $result = $pause->test_reindex; + + my $packages = $result->packages_data; + ok($packages->package("POSIX"), "we index POSIX in a dev version"); +}; + +subtest "reject perl by unauthorized user" => sub { + my $pause = PAUSE::TestPAUSE->init_new; + + my $initial_result = $pause->test_reindex; + my $dbh = $initial_result->connect_authen_db; + + $pause->upload_author_fake(OPRIME => { + name => 'perl', + version => '5.56.55', + packages => [ 'Perl::Core' ], + packages => [ + 'Perl::Core' => { version => '1.002' }, + ], + }); + + my $result = $pause->test_reindex; + + $result->assert_index_not_updated; + + $result->logged_event_like( + qr{\Qperl dist O/OP/OPRIME/perl-5.56.55.tar.gz from untrusted user OPRIME}, + "rejected because user it not trusted to upload perl", + ); +}; + +subtest "indexing a new perl and then another one" => sub { + my $pause = PAUSE::TestPAUSE->init_new; + + my $initial_result = $pause->test_reindex; + my $dbh = $initial_result->connect_authen_db; + + die "couldn't make OPRIME a pumpking" + unless $dbh->do("INSERT INTO grouptable (user, ugroup) VALUES ('OPRIME', 'pumpking')"); + + subtest "first version of perl" => sub { + $pause->upload_author_fake(OPRIME => { + name => 'perl', + version => '5.56.55', + packages => [ + 'Perl::Core' => { version => '1.002' }, + 'Little::Buddy' => { version => '2.003' }, + ], + }); + + my $result = $pause->test_reindex; + + $result->package_list_ok( + [ + { package => 'Little::Buddy', version => '2.003' }, + { package => 'Perl::Core', version => '1.002' }, + ], + ); + }; + + subtest "re-upload that same version again" => sub { + $pause->upload_author_fake(OPRIME => { + name => 'perl', + version => '5.56.55', + packages => [ + 'Perl::Core' => { version => '1.002' }, + 'Little::Buddy' => { version => '2.003' }, + ], + }); + + my $result = $pause->test_reindex; + + $result->package_list_ok( + [ + { package => 'Little::Buddy', version => '2.003' }, + { package => 'Perl::Core', version => '1.002' }, + ], + ); + }; + + subtest "actual next version of perl" => sub { + $pause->upload_author_fake(OPRIME => { + name => 'perl', + version => '5.56.56', + packages => [ + 'Little::Buddy' => { version => '2.345' }, + 'Newly::Added' => { version => '3.000' }, + 'Perl::Core' => { version => '1.002' }, + ], + }); + + my $result = $pause->test_reindex; + + $result->package_list_ok( + [ + { package => 'Little::Buddy', version => '2.345' }, + { package => 'Newly::Added', version => '3.000' }, + { package => 'Perl::Core', version => '1.002' }, + ], + ); + }; +}; + +subtest "indexing a new perl, but file is not a proper tar.gz" => sub { + my $pause = PAUSE::TestPAUSE->init_new; + + my $initial_result = $pause->test_reindex; + my $dbh = $initial_result->connect_authen_db; + + die "couldn't make OPRIME a pumpking" + unless $dbh->do("INSERT INTO grouptable (user, ugroup) VALUES ('OPRIME', 'pumpking')"); + + $pause->upload_author_garbage(OPRIME => "perl-5.56.55.tar.gz"); + + my $result = $pause->test_reindex; + + $result->assert_index_not_updated; + + $result->logged_event_like( + qr{\Qcould not untar O/OP/OPRIME/perl-5.56.55.tar.gz}, + "you can't index what you can't extract", + ); +}; + +subtest "perl uploads do not unseat dual-life modules" => sub { + # When a perl dist upload includes a new version of something currently + # indexed in a non-perl dist, we do not replace it in the index. This test + # is for that rule. + my $pause = PAUSE::TestPAUSE->init_new; + + my $initial_result = $pause->test_reindex; + my $dbh = $initial_result->connect_authen_db; + + die "couldn't make OPRIME a pumpking" + unless $dbh->do("INSERT INTO grouptable (user, ugroup) VALUES ('OPRIME', 'pumpking')"); + + subtest "upload the standalone version of a dual-life dist" => sub { + $pause->upload_author_fake(OPRIME => "Little-Buddy-1.000.tar.gz"); + my $result = $pause->test_reindex; + + $result->package_list_ok( + [ + { package => 'Little::Buddy', version => '1.000' }, + ], + ); + }; + + subtest "upload the in-core version of a dual-life dist" => sub { + $pause->upload_author_fake(OPRIME => { + name => 'perl', + version => '5.56.55', + packages => [ + 'Perl::Core' => { version => '1.002' }, + 'Little::Buddy' => { version => '2.003' }, + ], + }); + + my $result = $pause->test_reindex; + + $result->package_list_ok( + [ + { package => 'Little::Buddy', version => '1.000' }, + { package => 'Perl::Core', version => '1.002' }, + ], + ); + }; +}; + +subtest "non-perl can replace perl versions" => sub { + # The "separate then in-core" rule which says that the core won't shadow a + # separate upload only goes in one direction. If a library is first found in + # core, and then later uploaded outside, the newly uploaded package may + # become indexed in that new dist. + my $pause = PAUSE::TestPAUSE->init_new; + + my $initial_result = $pause->test_reindex; + my $dbh = $initial_result->connect_authen_db; + + die "couldn't make OPRIME a pumpking" + unless $dbh->do("INSERT INTO grouptable (user, ugroup) VALUES ('OPRIME', 'pumpking')"); + + my $perl_dist = 'O/OP/OPRIME/perl-5.56.55.tar.gz'; + my $other_dist = 'O/OP/OPRIME/Little-Buddy-3.000.tar.gz'; + + subtest "upload the in-core version of a dual-life dist" => sub { + $pause->upload_author_fake(OPRIME => { + name => 'perl', + version => '5.56.55', + packages => [ + 'Little::Buddy' => { version => '2.000' }, + 'Perl::Core' => { version => '1.002' }, + ], + }); + + my $result = $pause->test_reindex; + + $result->package_list_ok( + [ + { package => 'Little::Buddy', version => '2.000' }, + { package => 'Perl::Core', version => '1.002' }, + ], + ); + }; + + subtest "upload the standalone version of a dual-life dist" => sub { + $pause->upload_author_fake(OPRIME => "Little-Buddy-3.000.tar.gz"); + my $result = $pause->test_reindex; + + $result->package_list_ok( + [ + { package => 'Little::Buddy', version => '3.000' }, + { package => 'Perl::Core', version => '1.002' }, + ], + ); + }; +}; + +done_testing;