App-GHGen

 view release on metacpan or  search on metacpan

t/extended_tests.t  view on Meta::CPAN

	my $step = App::GHGen::Fixer::detect_and_create_cache_step($steps);
	ok(defined $step,                         'cache step created for Go project');
	like($step->{with}{path}, qr|go/pkg/mod|, 'Go cache uses ~/go/pkg/mod path');
	like($step->{with}{key},  qr/go\.sum/,    'Go cache key hashes go.sum');
};

subtest 'Fixer::detect_and_create_cache_step - setup-go action triggers Go cache' => sub {
	my $steps = [{ uses => 'actions/setup-go@v5' }];
	my $step = App::GHGen::Fixer::detect_and_create_cache_step($steps);
	ok(defined $step, 'setup-go action triggers Go cache creation');
	like($step->{with}{path}, qr|go/pkg/mod|, 'correct path for setup-go');
};

subtest 'Fixer::detect_and_create_cache_step - no matching steps returns undef' => sub {
	# Branch: none of the known patterns → return undef.
	my $steps = [{ run => 'echo hello' }];
	my $step = App::GHGen::Fixer::detect_and_create_cache_step($steps);
	is($step, undef, 'unrecognised steps produce no cache step');
};

subtest 'Fixer::add_caching - job already with cache step is skipped' => sub {
	# LCSAJ: $has_cache is true → next → $modified stays 0.
	my $wf = {
		jobs => {
			build => {
				steps => [
					{ uses => "$CACHE_ACTION\@v5" },
					{ run  => 'npm ci'             },
				],
			},
		},
	};
	my $result = App::GHGen::Fixer::add_caching($wf);
	is($result, 0, 'job already with cache skipped; count stays 0');
};

subtest 'Fixer::add_trigger_filters - HASH on with push as bare string (truthy scalar)' => sub {
	# Branch: ref $on->{push} eq '' with a truthy value → expand push to branches filter.
	# Note: the implementation tests $on->{push} && ref $on->{push} eq '', so the
	# scalar must be truthy (empty string is falsy and falls through).
	my $wf = { on => { push => '*' }, jobs => {} };
	my $n = App::GHGen::Fixer::add_trigger_filters($wf);
	is($n, 1, 'truthy scalar push value gets expanded');
	is(ref $wf->{on}{push}, 'HASH', 'push is now a HASH with filters');
	is_deeply($wf->{on}{push}{branches}, ['main', 'master'], 'default branches added');
};

subtest 'Fixer::add_trigger_filters - no on key returns 0' => sub {
	my $wf = { jobs => {} };
	my $n = App::GHGen::Fixer::add_trigger_filters($wf);
	is($n, 0, 'missing on key returns 0 modifications');
};

subtest 'Fixer::update_runners - windows-2016 upgraded to windows-latest' => sub {
	my $wf = {
		jobs => {
			build => { 'runs-on' => 'windows-2016', steps => [] },
		},
	};
	my $n = App::GHGen::Fixer::update_runners($wf);
	is($n, 1,                'one runner updated');
	is($wf->{jobs}{build}{'runs-on'}, 'windows-latest', 'windows-2016 → windows-latest');
};

subtest 'Fixer::update_runners - runner not in update table is unchanged' => sub {
	my $wf = { jobs => { build => { 'runs-on' => 'ubuntu-latest', steps => [] } } };
	my $n = App::GHGen::Fixer::update_runners($wf);
	is($n, 0, 'current runner string produces no modifications');
};

subtest 'Fixer::get_latest_version - unknown action returns fallback v4' => sub {
	# POD: unknown action → 'v4' fallback.
	my $ver = App::GHGen::Fixer::get_latest_version('some/unknown-action');
	is($ver, 'v4', 'unknown action returns v4 fallback');
};

subtest 'Fixer::apply_fixes - security/permissions branch fires add_permissions' => sub {
	# Route to add_permissions via apply_fixes.
	my $wf     = { jobs => { build => { steps => [] } } };
	my @issues = ({ type => $ISSUE_TYPE{SEC}, message => 'Missing permissions block' });
	my $n      = apply_fixes($wf, \@issues);
	is($n, 1,                      'one fix applied');
	ok(exists $wf->{permissions},  'permissions key inserted');
	is($wf->{permissions}{contents}, 'read', 'contents: read set');
};

subtest 'Fixer::apply_fixes - maintenance/outdated action branch fires update_actions' => sub {
	my $wf = {
		jobs => {
			build => {
				steps => [{ uses => 'actions/cache@v3' }],
			},
		},
	};
	my @issues = ({ type => $ISSUE_TYPE{MAINT}, message => '1 outdated action(s)' });
	my $n = apply_fixes($wf, \@issues);
	is($n, 1, 'one action updated');
	is($wf->{jobs}{build}{steps}[0]{uses}, 'actions/cache@v5', 'cache upgraded to v5');
};

subtest 'Fixer::apply_fixes - performance/missing timeout fires add_missing_timeout' => sub {
	my $wf = {
		jobs => {
			build => { 'runs-on' => 'ubuntu-latest', steps => [] },
		},
	};
	my @issues = ({
		type    => $ISSUE_TYPE{PERF},
		message => "Job 'build' is missing timeout-minutes",
	});
	my $n = apply_fixes($wf, \@issues);
	is($n, 1,  'timeout fix applied');
	is($wf->{jobs}{build}{'timeout-minutes'}, 30, 'default 30-minute timeout inserted');
};

subtest 'Fixer::apply_fixes - unfixable issue type is skipped silently' => sub {
	# can_auto_fix returns 0 for unknown types; apply_fixes skips them.
	my $wf     = { jobs => {} };
	my @issues = ({ type => 'unknown_type', message => 'something weird' });
	my $n      = apply_fixes($wf, \@issues);
	is($n, 0, 'unfixable issue produces zero fixes');
};

# =============================================================================
# 5.  App::GHGen::Generator — content of non-Perl workflow generators
# =============================================================================

subtest 'Generator::generate_workflow - node workflow has expected structure' => sub {
	my $yaml = generate_workflow('node');
	ok(defined $yaml,                      'node workflow generated');
	like($yaml, qr/^---/m,                 'starts with YAML header');
	like($yaml, qr/Node\.js CI/,           'contains workflow name');
	like($yaml, qr/node-version/,          'uses node-version matrix');
	like($yaml, qr/actions\/setup-node/,   'includes setup-node action');
	like($yaml, qr/npm/,                   'includes npm commands');
};

subtest 'Generator::generate_workflow - python workflow has expected structure' => sub {
	my $yaml = generate_workflow('python');
	like($yaml, qr/Python CI/,             'python workflow named correctly');
	like($yaml, qr/python-version/,        'uses python-version matrix');
	like($yaml, qr/setup-python/,          'includes setup-python action');
	like($yaml, qr/pytest/,               'includes pytest command');
};

subtest 'Generator::generate_workflow - rust workflow has expected structure' => sub {
	my $yaml = generate_workflow('rust');
	like($yaml, qr/Rust CI/,              'rust workflow named correctly');
	like($yaml, qr/cargo/,               'includes cargo commands');
	like($yaml, qr/clippy/,              'includes clippy linting');
};

subtest 'Generator::generate_workflow - go workflow has expected structure' => sub {
	my $yaml = generate_workflow('go');
	like($yaml, qr/Go CI/,               'go workflow named correctly');
	like($yaml, qr/setup-go/,            'includes setup-go action');
	like($yaml, qr/go test/,             'includes go test command');



( run in 0.849 second using v1.01-cache-2.11-cpan-81fc1098f69 )