App-Test-Generator
view release on metacpan or search on metacpan
t/edge_cases.t view on Meta::CPAN
my $dir = tempdir(CLEANUP => 1);
my $file = File::Spec->catfile($dir, 'circular.yml');
lives_ok {
$exporter->export(\%plan, $file);
} 'export() does not crash or hang on a circular-reference plan';
ok(-s $file, 'a non-empty YAML file was written for the circular structure');
};
# ==================================================================
# TestStrategy::generate_plan -- malformed per-method schema entries
#
# generate_plan() has no schema-shape validation of its own, unlike
# every Planner::* submodule (which all croak on a non-hashref $schema
# argument). Probe both ends of that gap: an undef per-method entry is
# safe (a chained rvalue dereference degrades to defaults), while a
# non-hashref entry such as a plain string is unsafe (dereferencing a
# string as a hashref dies under "strict refs"). Both behaviours were
# verified directly against the running code before being asserted
# here.
# ==================================================================
subtest 'TestStrategy::generate_plan: undef per-method entry degrades to the basic_test fallback' => sub {
my $strategy = App::Test::Generator::TestStrategy->new(
schema => { mystery_method => undef },
);
my $plan;
lives_ok {
$plan = $strategy->generate_plan;
} 'generate_plan() lives when a per-method schema entry is undef';
is_deeply(
$plan->{mystery_method},
{ basic_test => 1 },
'an undef per-method entry degrades to the basic_test-only fallback plan',
);
};
subtest 'TestStrategy::generate_plan: non-hashref per-method entry dies with a clear type error' => sub {
my $strategy = App::Test::Generator::TestStrategy->new(
schema => { broken_method => 'not a hashref' },
);
throws_ok {
$strategy->generate_plan;
} qr/HASH ref/,
'a non-hashref per-method schema entry dies clearly rather than silently misbehaving';
};
# ==================================================================
# Generator::generate() -- end-to-end injection attempt via a
# schema-derived function name
#
# _assert_identifier() itself is already unit-tested directly
# elsewhere (t/function.t). This instead exercises the *full*
# generate() pipeline end to end: a function name shaped like a Perl
# statement-injection payload (semicolon-separated, containing a
# system() call) must be rejected before it ever reaches the point of
# being spliced unescaped into generated test source, and critically,
# before any output file is created on disk.
# ==================================================================
subtest 'Generator::generate(): statement-injection-shaped function name is rejected before any file is written' => sub {
my $dir = tempdir(CLEANUP => 1);
my $outfile = File::Spec->catfile($dir, 'out.t');
my $schema = {
function => 'evil; system("touch /tmp/pwned"); 1',
input => { number => { type => 'number', position => 0 } },
output => { type => 'number' },
};
throws_ok {
App::Test::Generator->generate(schema => $schema, output_file => $outfile);
} qr/not a valid Perl identifier/,
'a statement-injection-shaped function name is rejected';
ok(!-e $outfile, 'no output file was written once the malicious identifier was rejected');
};
subtest '_compile_signature_isolated: pathological subprocess outputs are handled gracefully' => sub {
# Covers three hostile subprocess scenarios:
# 1. SIGKILL before any write â stdout empty, stderr empty
# 2. Corrupt/truncated output â stdout non-JSON, stderr empty
# 3. Only stderr output â stdout empty, stderr has message
#
# In every case the parent must return undef rather than croaking with
# "malformed JSON string" (regression: the croak used to propagate through
# Test::Builder's subtest boundary and exit the process with code 255).
#
# Strategy: mock open3 in the consuming namespace so no real subprocess
# is spawned; waitpid($$, 0) on our own PID returns -1 harmlessly.
my $make_extractor = sub {
bless { allow_signature_exec => 1, verbose => 0 },
'App::Test::Generator::SchemaExtractor';
};
my $mock_open3 = sub {
my ($stdout_content, $stderr_content) = @_;
open my $fake_rdr, '<', \$stdout_content or die;
open my $fake_err, '<', \$stderr_content or die;
no warnings 'redefine';
local *App::Test::Generator::SchemaExtractor::open3 = sub {
$_[0] = do { open my $fh, '>', \my $buf; $fh };
$_[1] = $fake_rdr;
$_[2] = $fake_err;
return $$;
};
return;
};
{
my $empty = '';
open my $fake_rdr, '<', \$empty or die;
open my $fake_err, '<', \$empty or die;
no warnings 'redefine';
local *App::Test::Generator::SchemaExtractor::open3 = sub {
$_[0] = do { open my $fh, '>', \my $buf; $fh };
$_[1] = $fake_rdr;
( run in 1.654 second using v1.01-cache-2.11-cpan-788537b7465 )