App-Test-Generator
view release on metacpan or search on metacpan
t/function.t view on Meta::CPAN
# t/Generator_unit.t) exercises this function at all, so this section
# is full by-value coverage rather than a value-add narrowing.
# ==================================================================
subtest 'Generator::_assert_identifier - accepts well-formed identifiers' => sub {
my $fn = \&App::Test::Generator::_assert_identifier;
is($fn->('foo', 'name'), 'foo', 'a plain bareword identifier is returned unchanged');
is($fn->('_private', 'name'), '_private', 'a leading underscore is accepted');
is($fn->('Foo123', 'name'), 'Foo123', 'letters and digits after the first character are accepted');
};
subtest 'Generator::_assert_identifier - package => 1 additionally allows "::" separators' => sub {
my $fn = \&App::Test::Generator::_assert_identifier;
is($fn->('Foo::Bar', 'module', package => 1), 'Foo::Bar',
'a "::"-separated package name is accepted when package => 1');
is($fn->('DB::DB', 'function', package => 1), 'DB::DB',
'a fully-qualified sub name such as DB::DB is accepted when package => 1');
throws_ok { $fn->('Foo::Bar', 'module') } qr/^App::Test::Generator: module 'Foo::Bar' is not a valid Perl identifier at /,
'without package => 1, a "::"-separated name is rejected with the exact documented message';
};
subtest 'Generator::_assert_identifier - croaks with the exact message for missing or empty names' => sub {
my $fn = \&App::Test::Generator::_assert_identifier;
throws_ok { $fn->(undef, 'module') } qr/^App::Test::Generator: module is missing or empty at /,
'undef name croaks with the exact documented message';
throws_ok { $fn->('', 'function') } qr/^App::Test::Generator: function is missing or empty at /,
'empty-string name croaks with the exact documented message';
};
subtest 'Generator::_assert_identifier - rejects injection payloads even with package => 1' => sub {
my $fn = \&App::Test::Generator::_assert_identifier;
for my $payload ("Foo'; system('rm -rf /'); '", 'Foo::Bar()', 'Foo::Bar; 1', 'Foo Bar', '1Foo') {
throws_ok { $fn->($payload, 'module', package => 1) }
qr/^App::Test::Generator: module '\Q$payload\E' is not a valid Perl identifier at /,
"payload '$payload' is rejected even with package => 1";
}
};
subtest 'Generator::_assert_identifier - return value shape' => sub {
my $fn = \&App::Test::Generator::_assert_identifier;
returns_ok($fn->('foo', 'name'), { type => 'string' }, '_assert_identifier() returns a plain string on success');
};
# ==================================================================
# App::Test::Generator::_perl_quote - circular-reference depth guard
#
# The depth > 100 croak in _perl_quote() is unreachable through any
# single-level call from the public perl_quote() wrapper (which always
# starts at depth 0), but is reachable legitimately by feeding
# perl_quote() a sufficiently deep arrayref-of-arrayrefs -- no need to
# call the private _perl_quote() directly with a fabricated depth.
# ==================================================================
subtest 'Generator::perl_quote - croaks on a structure nested past the recursion limit' => sub {
Readonly my $DEPTH_BEYOND_LIMIT => 101;
my $deeply_nested = [];
my $cursor = $deeply_nested;
for (1 .. $DEPTH_BEYOND_LIMIT) {
push @{$cursor}, [];
$cursor = $cursor->[0];
}
throws_ok { App::Test::Generator::perl_quote($deeply_nested) }
qr/^perl_quote: structure too deeply nested \(circular reference\?\) at /,
'an arrayref nested past the recursion limit croaks with the exact documented message';
};
# ==================================================================
# App::Test::Generator::_validate_config - dispatch isolation
#
# t/Generator.t already covers _validate_config() exhaustively by
# value. The one mandated technique missing there is mocking the four
# delegate validators to pin down exactly which ones run under which
# input shape, independent of what each delegate actually does.
# ==================================================================
subtest 'Generator::_validate_config - dispatches to input validators only when input is defined' => sub {
my %calls;
for my $sub (qw(_validate_input_params _validate_input_positions _validate_input_semantics _validate_transform_properties)) {
Test::Mockingbird::mock(
'App::Test::Generator',
$sub,
sub { $calls{$sub}++; return; },
);
}
%calls = ();
App::Test::Generator::_validate_config({ function => 'foo' });
ok(!exists $calls{_validate_input_params}, 'no input section means _validate_input_params is never dispatched');
%calls = ();
App::Test::Generator::_validate_config({ function => 'foo', input => { x => { type => 'string' } } });
is($calls{_validate_input_params}, 1, 'a defined input section dispatches to _validate_input_params exactly once');
is($calls{_validate_input_positions}, 1, 'a defined input section dispatches to _validate_input_positions exactly once');
is($calls{_validate_input_semantics}, 1, 'a defined input section dispatches to _validate_input_semantics exactly once');
%calls = ();
App::Test::Generator::_validate_config({
function => 'foo',
transforms => { t1 => { properties => ['length'] } },
});
is($calls{_validate_transform_properties}, 1,
'a hashref transforms section dispatches to _validate_transform_properties exactly once');
for my $sub (qw(_validate_input_params _validate_input_positions _validate_input_semantics _validate_transform_properties)) {
Test::Mockingbird::unmock('App::Test::Generator', $sub);
}
};
# ==================================================================
# App::Test::Generator::SchemaExtractor
#
# t/SchemaExtractor.t, t/SchemaExtractor_unit.t, t/SchemaExtractor_function.t,
# and t/SchemaExtractor_signature_exec.t already cover the public API and
# most internal helpers by value via extract_all(). The subtests below
# target the handful of internal helpers those files never reach at all:
# _analysis_error, _ppi, _extract_type_params_schema, _extract_function_name,
# _map_formal_input_type, _analyze_output, _extract_defaults_from_pod,
# _analyze_advanced_types, _parse_modern_signature, _detect_dependencies,
# _write_schema, _check_inheritance_for_constructor,
# _detect_constructor_requirements, _detect_external_object_dependency,
( run in 0.437 second using v1.01-cache-2.11-cpan-2aafcb1aa8b )