App-GHGen
view release on metacpan or search on metacpan
t/function.t view on Meta::CPAN
is(
$cost->{minutes_per_month},
$cost->{runs_per_month} * $cost->{minutes_per_run},
'minutes_per_month equals runs x minutes_per_run',
);
memory_cycle_ok($cost, 'workflow cost result has no circular references');
diag(explain($cost)) if $ENV{TEST_VERBOSE};
};
subtest 'CostEstimator::estimate_savings - quantifies potential reductions' => sub {
# Caching issue saves CI minutes.
my @caching_issues = ({
type => 'performance',
message => 'No dependency caching found - increases build times and costs',
});
my $savings = App::GHGen::CostEstimator::estimate_savings(\@caching_issues);
ok($savings->{minutes} > 0, 'caching fix yields positive minute savings');
ok($savings->{cost} >= 0, 'savings cost field is non-negative');
ok(defined $savings->{details}, 'savings includes details array');
# No issues -> no savings.
my $zero = App::GHGen::CostEstimator::estimate_savings([]);
is($zero->{minutes}, 0, 'no issues produces zero minute savings');
memory_cycle_ok($savings, 'savings result has no circular references');
};
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# 3. App::GHGen::Detector
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
subtest 'Detector::get_project_indicators - returns structured indicator lists' => sub {
my $perl_indicators = get_project_indicators('perl');
ok(ref $perl_indicators eq 'ARRAY', 'perl indicators are an arrayref');
ok(scalar @$perl_indicators > 0, 'perl indicator list is non-empty');
ok((grep { /cpanfile/ } @$perl_indicators), 'cpanfile listed as perl indicator');
# Requesting all indicators returns a hashref keyed by language.
my $all = get_project_indicators();
ok(ref $all eq 'HASH', 'no-arg call returns a hashref');
ok(exists $all->{perl}, 'all-indicators hash includes perl');
ok(exists $all->{node}, 'all-indicators hash includes node');
# Unknown type returns undef.
my $unknown = get_project_indicators('cobol');
ok(!defined $unknown, 'unknown type returns undef');
};
subtest 'Detector::_detect_perl - scores current repo as a Perl project' => sub {
# We are running inside App-GHGen which is a Perl project (has cpanfile,
# Makefile.PL, lib/, t/). The detector should give a positive score.
my $score = App::GHGen::Detector::_detect_perl();
ok($score > 0, "Perl repo scores positively ($score)");
diag("_detect_perl score: $score") if $ENV{TEST_VERBOSE};
};
subtest 'Detector::_detect_node - scores zero in a Perl-only repo' => sub {
my $score = App::GHGen::Detector::_detect_node();
# The App-GHGen repo has no package.json, so the score must be zero (or low).
# We cannot assert zero because the repo may contain incidental node files,
# but we can assert it is lower than the Perl score.
my $perl_score = App::GHGen::Detector::_detect_perl();
ok($score < $perl_score, 'node score is lower than perl score in a Perl repo');
};
subtest 'Detector::detect_project_type - returns "perl" for this repository' => sub {
# Running from the App-GHGen directory, which is definitively a Perl project.
my $type = detect_project_type();
is($type, 'perl', 'detect_project_type returns "perl" for App-GHGen repo');
# In wantarray context it returns a ranked list of detections.
my @ranked = detect_project_type();
ok(scalar @ranked > 0, 'wantarray context returns non-empty list');
is($ranked[0]->{type}, 'perl', 'highest-ranked type is still perl');
};
subtest 'Detector::detect_project_type - returns undef in an empty directory' => sub {
in_tempdir(sub {
my $type = detect_project_type();
ok(!defined $type, 'empty directory returns undef');
});
};
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# 4. App::GHGen::Fixer
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
subtest 'Fixer::can_auto_fix - accepts all four fixable types' => sub {
for my $type (qw(performance security cost maintenance)) {
ok(
can_auto_fix({ type => $type }),
"can_auto_fix returns true for '$type'",
);
}
ok(
!can_auto_fix({ type => 'unknown_type' }),
'can_auto_fix returns false for unknown type',
);
};
subtest 'Fixer::get_latest_version - maps known actions to current versions' => sub {
is(
App::GHGen::Fixer::get_latest_version('actions/checkout'),
'v6',
'actions/checkout maps to v6',
);
is(
App::GHGen::Fixer::get_latest_version('actions/cache'),
'v5',
'actions/cache maps to v5',
);
# Unknown action falls back to a sensible default rather than dying.
my $fallback = App::GHGen::Fixer::get_latest_version('unknown/action');
ok(defined $fallback, 'unknown action returns a defined fallback version');
};
subtest 'Fixer::detect_and_create_cache_step - creates ecosystem-correct steps' => sub {
my $npm_steps = [{ run => 'npm ci' }];
my $npm_step = App::GHGen::Fixer::detect_and_create_cache_step($npm_steps);
ok(defined $npm_step, 'npm step detected');
( run in 1.050 second using v1.01-cache-2.11-cpan-4ac696b4eb4 )