App-Project-Doctor
view release on metacpan or search on metacpan
t/integration.t view on Meta::CPAN
#!/usr/bin/perl
use strict;
use warnings;
use Test::Most;
use Test::Returns;
use Test::Mockingbird qw(spy mock_scoped restore_all);
use Readonly;
use File::Temp qw(tempdir);
use File::Spec;
use File::Path qw(make_path);
use File::Basename qw(dirname);
use Scalar::Util qw(blessed);
# ââ Constants âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
Readonly::Scalar my $VALID_VERSION => '1.00';
Readonly::Scalar my $INVALID_VERSION => 'alpha';
Readonly::Scalar my $SOME_MOD => 'Some::Undeclared';
Readonly::Scalar my $ANOTHER_MOD => 'Some::Other';
Readonly::Hash my %MINIMAL_DISTRO => (
'Makefile.PL' => "use ExtUtils::MakeMaker;\n",
'lib/My/Module.pm' => "package My::Module;\nuse strict;\nuse warnings;\nour \$VERSION = '1.00';\n1;\n",
'Changes' => "1.00 2026-06-30\n - Initial release.\n",
'MANIFEST' => "MANIFEST\nMakefile.PL\nlib/My/Module.pm\n",
'README' => "My::Module -- a test module.\n",
);
# ââ Module loading âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
use_ok 'App::Project::Doctor';
use_ok 'App::Project::Doctor::Context';
use_ok 'App::Project::Doctor::Finding';
use_ok 'App::Project::Doctor::Report';
use_ok 'App::Project::Doctor::Fixer';
my $Doctor = 'App::Project::Doctor';
my $Context = 'App::Project::Doctor::Context';
my $Finding = 'App::Project::Doctor::Finding';
my $Report = 'App::Project::Doctor::Report';
my $Fixer = 'App::Project::Doctor::Fixer';
# ââ Shared helpers ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# Build a temp directory tree from a %{ rel_path => content } hash.
sub _make_distro {
my (%files) = @_;
my $dir = tempdir(CLEANUP => 1);
for my $rel (sort keys %files) {
my @parts = split m{/}, $rel;
my $abs = File::Spec->catfile($dir, @parts);
make_path(dirname($abs)) unless -d dirname($abs);
open my $fh, '>', $abs or die "Cannot write $abs: $!";
print $fh $files{$rel};
close $fh;
}
return $dir;
}
# Create a Finding with the given named args.
sub _f {
return $Finding->new(@_);
}
# Build a Report containing $n fixable error findings.
# Each fix coderef appends "fixN" to @{$applied_ref}.
sub _report_with_fixable {
my ($applied_ref, $n) = @_;
my $report = $Report->new;
for my $i (1 .. $n) {
my $idx = $i;
$report->add_findings(_f(
severity => 'error',
message => "Fix me $idx",
check_name => 'Test',
fix => sub { push @{$applied_ref}, "fix$idx" },
));
}
return $report;
}
# Run Fixer interactively, supplying $input via a string-ref STDIN.
# Suppresses Fixer output unless $ENV{TEST_VERBOSE}.
sub _interactive_fixer {
my ($input, $applied_ref) = @_;
my $dir = tempdir(CLEANUP => 1);
my $ctx = $Context->new(root => $dir);
my $report = _report_with_fixable($applied_ref, 3);
my $fixer = $Fixer->new(report => $report, context => $ctx);
my ($count, $out);
{
open(local *STDOUT, '>', \$out) or die "Cannot redirect STDOUT: $!";
open(local *STDIN, '<', \$input) or die "Cannot redirect STDIN: $!";
$count = $fixer->run;
}
diag "Fixer output:\n$out" if $ENV{TEST_VERBOSE} && $out;
return $count;
}
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# 1. FINDING -> REPORT ACCUMULATION
# Strategy: Exercise the full Finding+Report pipeline by creating all four
# severity types and verifying that each filter method is independently correct.
# A passing report and a failing report are tested to ensure exit_code and the
# has_* predicates agree.
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
subtest 'finding-report accumulation across all severity types' => sub {
my $fix_called = 0;
( run in 0.869 second using v1.01-cache-2.11-cpan-af0e5977854 )