App-Test-Generator

 view release on metacpan or  search on metacpan

t/edge_cases.t  view on Meta::CPAN

		eval { App::Test::Generator->generate($path) };
	});
	is($@, '', 'special character schema does not croak');
};

subtest 'Generator: zero iterations produces minimal output' => sub {
	my ($fh, $path) = tempfile(SUFFIX => '.yml', UNLINK => 1);
	print $fh "module: builtin\nfunction: abs\n";
	print $fh "input:\n  type: number\noutput:\n  type: number\n";
	print $fh "iterations: 0\n";
	close $fh;
	my ($out) = capture(sub {
		eval { App::Test::Generator->generate($path) };
	});
	is($@, '', 'zero iterations does not croak');
	like($out, qr/done_testing/, 'done_testing present with zero iterations');
};

subtest 'Generator: very large iterations value' => sub {
	my ($fh, $path) = tempfile(SUFFIX => '.yml', UNLINK => 1);
	print $fh "module: builtin\nfunction: abs\n";
	print $fh "input:\n  type: number\noutput:\n  type: number\n";
	print $fh "iterations: 999999\n";
	close $fh;
	my ($out) = capture(sub {
		eval { App::Test::Generator->generate($path) };
	});
	# Should not OOM or hang — just produce output with large iteration count
	is($@, '', 'very large iterations value does not croak');
};

subtest 'Generator: nonexistent schema file croaks' => sub {
	throws_ok(
		sub { App::Test::Generator->generate('/no/such/file.yml') },
		qr/No such|not found|Cannot|read/i,
		'nonexistent schema file croaks',
	);
};

subtest 'Generator: undef schema file croaks' => sub {
	throws_ok(
		sub { App::Test::Generator->generate(undef) },
		qr/Usage|required|defined/i,
		'undef schema file croaks',
	);
};

subtest 'Generator: schema with min > max for integer input' => sub {
	my ($fh, $path) = tempfile(SUFFIX => '.yml', UNLINK => 1);
	print $fh "module: builtin\nfunction: my_func\n";
	print $fh "input:\n  x:\n    type: integer\n    min: 100\n    max: 1\n";
	print $fh "output:\n  type: integer\n";
	close $fh;
	# Should not crash even with inverted constraints
	lives_ok(
		sub { capture(sub { App::Test::Generator->generate($path) }) },
		'inverted min/max does not crash',
	);
};

subtest 'Generator: schema with unicode function name' => sub {
	my ($fh, $path) = tempfile(SUFFIX => '.yml', UNLINK => 1);
	binmode $fh, ':utf8';
	print $fh "module: builtin\nfunction: my_func\n";
	print $fh "input:\n  type: string\noutput:\n  type: string\n";
	close $fh;
	lives_ok(
		sub { capture(sub { App::Test::Generator->generate($path) }) },
		'unicode in schema file does not crash',
	);
};

# ==================================================================
# Generator — render helpers with pathological inputs
# ==================================================================

subtest 'perl_quote: handles undef' => sub {
	is(App::Test::Generator::perl_quote(undef), 'undef',
		'undef -> literal undef string');
};

subtest 'perl_quote: handles empty string' => sub {
	is(App::Test::Generator::perl_quote(''), "''", 'empty string -> empty quotes');
};

subtest 'perl_quote: handles string with single quotes' => sub {
	my $result = App::Test::Generator::perl_quote("it's");
	ok(defined $result, 'string with single quote handled');
	like($result, qr/it/, 'original content preserved');
};

subtest 'perl_quote: handles NUL bytes' => sub {
	my $result = App::Test::Generator::perl_quote("a\0b");
	ok(defined $result, 'NUL byte handled');
};

subtest 'perl_quote: handles very long string' => sub {
	my $long = 'x' x 10_000;
	my $result = App::Test::Generator::perl_quote($long);
	ok(defined $result, '10000-char string handled');
	ok(length($result) > 0, 'non-empty result');
};

subtest 'perl_quote: handles arrayref' => sub {
	my $result = App::Test::Generator::perl_quote([1, 2, 3]);
	like($result, qr/\[.*1.*2.*3/s, 'arrayref rendered');
};

subtest 'perl_quote: handles nested arrayref' => sub {
	my $result = App::Test::Generator::perl_quote([[1, 2], [3, 4]]);
	ok(defined $result, 'nested arrayref handled');
};

subtest 'q_wrap: handles string containing all bracket pairs' => sub {
	my $result = App::Test::Generator::q_wrap('{}()[]<>');
	ok(defined $result, 'string with all brackets handled');
	ok(length($result) > 0, 'non-empty result');
};

subtest 'q_wrap: handles empty string' => sub {
	my $result = App::Test::Generator::q_wrap('');
	ok(defined $result, 'empty string handled');
};

subtest 'q_wrap: handles undef' => sub {
	my $result = App::Test::Generator::q_wrap(undef);
	ok(defined $result, 'undef handled');
};

t/edge_cases.t  view on Meta::CPAN

	my $schemas;
	lives_ok(
		sub {
			my $e = App::Test::Generator::SchemaExtractor->new(
				input_file => $pm,
			);
			$schemas = $e->extract_all(no_write => 1);
		},
		'100-method module does not crash',
	);
	ok(scalar keys %{$schemas} > 0, 'schemas extracted from large module');
};

subtest 'SchemaExtractor: source with deeply nested anonymous subs' => sub {
	my ($fh, $pm) = tempfile(SUFFIX => '.pm', UNLINK => 1);
	print $fh "package Nested;\n";
	print $fh "sub outer {\n";
	print $fh "\tmy \$x = sub { my \$y = sub { return 1; }; return \$y; };\n";
	print $fh "\treturn \$x;\n";
	print $fh "}\n1;\n";
	close $fh;
	lives_ok(
		sub {
			my $e = App::Test::Generator::SchemaExtractor->new(
				input_file => $pm
			);
			$e->extract_all(no_write => 1);
		},
		'nested anonymous subs do not crash',
	);
};

subtest 'SchemaExtractor: source with only comments and POD' => sub {
	my ($fh, $pm) = tempfile(SUFFIX => '.pm', UNLINK => 1);
	print $fh "# This is just a comment\n\n=head1 NAME\n\nFoo\n\n=cut\n\n1;\n";
	close $fh;
	lives_ok(
		sub {
			my $e = App::Test::Generator::SchemaExtractor->new(
				input_file => $pm
			);
			my $schemas = $e->extract_all(no_write => 1);
			is(scalar keys %{$schemas}, 0, 'no schemas from comment-only file');
		},
		'comment-only source does not crash',
	);
};

subtest 'SchemaExtractor: source with Unicode identifiers' => sub {
	my ($fh, $pm) = tempfile(SUFFIX => '.pm', UNLINK => 1);
	binmode $fh, ':utf8';
	print $fh "package Unicode;\nuse utf8;\nsub greet { return 'héllo'; }\n1;\n";
	close $fh;
	lives_ok(
		sub {
			my $e = App::Test::Generator::SchemaExtractor->new(
				input_file => $pm
			);
			$e->extract_all(no_write => 1);
		},
		'unicode source does not crash',
	);
};

subtest 'SchemaExtractor: confidence_threshold at extremes' => sub {
	my ($fh, $pm) = tempfile(SUFFIX => '.pm', UNLINK => 1);
	print $fh "package Foo;\nsub bar { return 1; }\n1;\n";
	close $fh;

	for my $thresh (0.0, 0.5, 1.0) {
		lives_ok(
			sub {
				my $e = App::Test::Generator::SchemaExtractor->new(
					input_file           => $pm,
					confidence_threshold => $thresh,
				);
				$e->extract_all(no_write => 1);
			},
			"confidence_threshold=$thresh does not crash",
		);
	}
};

# ==================================================================
# Mutant — boundary conditions
# ==================================================================

subtest 'Mutant: line number 0' => sub {
	lives_ok(
		sub {
			App::Test::Generator::Mutant->new(
				id          => 'TEST',
				description => 'test',
				original    => 'x',
				line        => 0,
				transform   => sub { 1 },
			)
		},
		'line number 0 accepted',
	);
};

subtest 'Mutant: very large line number' => sub {
	lives_ok(
		sub {
			App::Test::Generator::Mutant->new(
				id          => 'TEST',
				description => 'test',
				original    => 'x',
				line        => 999_999_999,
				transform   => sub { 1 },
			)
		},
		'very large line number accepted',
	);
};

subtest 'Mutant: empty string id' => sub {
	lives_ok(
		sub {
			App::Test::Generator::Mutant->new(



( run in 0.774 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )