App-GHGen
view release on metacpan or search on metacpan
t/integration.t view on Meta::CPAN
my @trig_b = grep { $_->{type} eq 'cost' && $_->{message} =~ /triggers/ } @issues_b;
ok(@trig_a > 0, 'Problem workflow (A) has broad-trigger issue');
is(scalar @trig_b, 0, 'Clean workflow (B) has no broad-trigger issue');
# Clean workflow must report zero issues in total.
is(scalar @issues_b, 0, 'Clean workflow (B) has zero issues overall');
diag("Issues A: " . scalar(@issues_a) . " Issues B: " . scalar(@issues_b))
if $ENV{TEST_VERBOSE};
};
# ---------------------------------------------------------------------------
# Subtest 14: Reporter::estimate_savings vs CostEstimator::estimate_savings
# Both functions share a name but document different return-value contracts.
# Verify each conforms to its own POD spec when given the same input.
subtest 'Reporter vs CostEstimator estimate_savings: distinct return contracts' => sub {
my @issues = (
{
type => 'performance',
severity => 'medium',
message => 'No dependency caching found - increases build times and costs',
},
{
type => 'cost',
severity => 'low',
message => 'No concurrency group - old runs continue when superseded',
},
);
# Reporter::estimate_savings: { minutes => Int, cost => Int }
my $reporter_s = App::GHGen::Reporter::estimate_savings(\@issues);
ok(defined $reporter_s->{minutes}, 'Reporter: minutes key present');
ok(defined $reporter_s->{cost}, 'Reporter: cost key present');
ok(!exists $reporter_s->{details}, 'Reporter: no details key (simpler contract)');
ok(!exists $reporter_s->{percentage}, 'Reporter: no percentage key');
cmp_ok($reporter_s->{minutes}, '>', 0, 'Reporter: nonzero savings for caching issue');
# CostEstimator::estimate_savings: { minutes, cost (Str), percentage, details ArrayRef }
my $estimator_s = App::GHGen::CostEstimator::estimate_savings(\@issues);
ok(defined $estimator_s->{minutes}, 'CostEstimator: minutes key present');
ok(defined $estimator_s->{cost}, 'CostEstimator: cost key present');
ok(exists $estimator_s->{details}, 'CostEstimator: details key present');
ok(ref $estimator_s->{details} eq 'ARRAY', 'CostEstimator: details is an ArrayRef');
ok(exists $estimator_s->{percentage}, 'CostEstimator: percentage key present');
cmp_ok($estimator_s->{minutes}, '>', 0, 'CostEstimator: nonzero savings');
diag("Reporter: $reporter_s->{minutes} min CostEstimator: $estimator_s->{minutes} min")
if $ENV{TEST_VERBOSE};
};
# ---------------------------------------------------------------------------
# Subtest 15: get_cache_suggestion per ecosystem (Analyzer cross-module check)
# For each ecosystem the Analyzer understands, build a minimal workflow that
# triggers that detection and verify the cache path in the suggestion.
subtest 'Analyzer::get_cache_suggestion: correct cache path per ecosystem' => sub {
# These constants come directly from the FORMAL SPECIFICATION in the POD.
my @cases = (
{
ecosystem => 'npm',
steps => [{ run => 'npm install' }],
path_frag => '~/.npm',
},
{
ecosystem => 'pip',
steps => [{ run => 'pip install -r requirements.txt' }],
path_frag => '~/.cache/pip',
},
{
ecosystem => 'cargo',
steps => [{ run => 'cargo build' }],
path_frag => '~/.cargo',
},
{
ecosystem => 'bundler',
steps => [{ run => 'bundle install' }],
path_frag => 'vendor/bundle',
},
);
for my $case (@cases) {
my $wf = { jobs => { build => { steps => $case->{steps} } } };
my $suggestion = get_cache_suggestion($wf);
like($suggestion, qr/\Q$case->{path_frag}\E/,
"$case->{ecosystem}: suggestion contains path '$case->{path_frag}'");
diag("$case->{ecosystem}: $suggestion") if $ENV{TEST_VERBOSE};
}
# Unknown ecosystem (make install) must return the generic guidance message.
my $unk_wf = { jobs => { build => { steps => [{ run => 'make install' }] } } };
my $unk_hint = get_cache_suggestion($unk_wf);
like($unk_hint, qr/dependency manager/i,
'Unknown ecosystem returns generic guidance per POD');
};
# ---------------------------------------------------------------------------
# Subtest 16: Test::Without::Module -- YAML::XS absence causes load error in Fixer
# Fixer has a hard `use YAML::XS` dependency. Blocking it in a child process
# must produce a non-zero exit and a recognizable "Can't locate" error.
subtest 'Test::Without::Module: YAML::XS absence causes load error for Fixer' => sub {
my $script = q(
use Test::Without::Module 'YAML::XS';
require App::GHGen::Fixer;
);
my ($out, $err);
run3([$^X, '-Ilib', '-e', $script], \undef, \$out, \$err);
my $exit = $? >> 8;
cmp_ok($exit, '!=', 0, 'Child process exits non-zero when YAML::XS is blocked');
like("$out$err", qr/Can't locate YAML/i,
'Error output mentions the missing YAML::XS module');
};
# ---------------------------------------------------------------------------
# Subtest 17: Test::Without::Module -- Path::Tiny absence causes load error in Analyzer
# Analyzer has a hard `use Path::Tiny` dependency. Blocking it must fail.
subtest 'Test::Without::Module: Path::Tiny absence causes load error for Analyzer' => sub {
my $script = q(
use Test::Without::Module 'Path::Tiny';
require App::GHGen::Analyzer;
);
my ($out, $err);
run3([$^X, '-Ilib', '-e', $script], \undef, \$out, \$err);
my $exit = $? >> 8;
cmp_ok($exit, '!=', 0, 'Child process exits non-zero when Path::Tiny is blocked');
like("$out$err", qr/Can't locate Path/i,
'Error output mentions the missing Path::Tiny module');
};
done_testing();
( run in 2.500 seconds using v1.01-cache-2.11-cpan-b16cb0d3907 )