App-Test-Generator

 view release on metacpan or  search on metacpan

t/function.t  view on Meta::CPAN

	is($params{y}{position}, 1, 'y is given position 1, immediately after the skipped self');
};

subtest 'SchemaExtractor::_parse_modern_signature - respects nested brackets when splitting on commas' => sub {
	my $e = bless {}, 'App::Test::Generator::SchemaExtractor';
	my %params;

	$e->_parse_modern_signature(\%params, '$self, $opts = { a => 1, b => 2 }');
	is(scalar(keys %params), 1, 'the internal comma inside the bracketed default value does not split the parameter list');
	ok(exists $params{opts}, 'opts is recognised as a single parameter despite its hashref default');
};

subtest 'SchemaExtractor::_detect_dependencies - detects a dependency only when both message and code patterns match' => sub {
	my $e = bless {}, 'App::Test::Generator::SchemaExtractor';

	my $rels = $e->_detect_dependencies(q{ croak "ssl requires key" if $ssl && !$key; }, ['ssl', 'key']);
	is(scalar(@$rels), 1, 'exactly one dependency relationship is detected');
	is($rels->[0]{type}, 'dependency', '...');
	is($rels->[0]{param}, 'ssl', '...');
	is($rels->[0]{requires}, 'key', '...');

	my $no_code_match = $e->_detect_dependencies(q{ croak "ssl requires key" if $ssl; }, ['ssl', 'key']);
	is_deeply($no_code_match, [], 'no relationship is recorded when the code-condition pattern is absent, even though the message matches');
};

subtest 'SchemaExtractor::_detect_dependencies - return value shape' => sub {
	my $e = bless {}, 'App::Test::Generator::SchemaExtractor';
	my $rels = $e->_detect_dependencies(q{ croak "ssl requires key" if $ssl && !$key; }, ['ssl', 'key']);
	returns_ok($rels, { type => 'arrayref' }, '_detect_dependencies() returns an arrayref');
	memory_cycle_ok($rels, '_detect_dependencies() result is free of circular references');
};

subtest 'SchemaExtractor::_write_schema - croaks when output_dir was not provided to new()' => sub {
	my $e = bless {}, 'App::Test::Generator::SchemaExtractor';

	throws_ok { $e->_write_schema('foo', {}) }
		qr{^App::Test::Generator::SchemaExtractor: output_dir must be provided to new\(\) when writing schema files at },
		'croaks with the exact documented message';
};

subtest 'SchemaExtractor::_write_schema - writes a YAML schema file under output_dir' => sub {
	my $tmpdir = tempdir(CLEANUP => 1);
	my $e = bless { output_dir => $tmpdir }, 'App::Test::Generator::SchemaExtractor';

	$e->_write_schema('greet', {
		input  => { name => { type => 'string', position => 0 } },
		output => { type => 'string' },
	});

	my $file = File::Spec->catfile($tmpdir, 'greet.yml');
	ok(-e $file, 'the schema file is created at output_dir/<method_name>.yml');

	open my $fh, '<', $file or die "Cannot read $file: $!";
	my $contents = do { local $/; <$fh> };
	close $fh;

	like($contents, qr/function: greet/, 'the written YAML includes the function name');
	like($contents, qr/name:/, 'the written YAML includes the input parameter name');
};

subtest 'SchemaExtractor::_check_inheritance_for_constructor - detects use parent and SUPER:: usage' => sub {
	my $src = "package Child;\nuse parent 'Parent';\nsub greet { my \$self = shift; return \$self->SUPER::greet(); }\n1;\n";
	my $e = bless { _document => PPI::Document->new(\$src) }, 'App::Test::Generator::SchemaExtractor';

	my $info = $e->_check_inheritance_for_constructor('Child', 'sub greet { my $self = shift; return $self->SUPER::greet(); }');
	ok($info, 'returns inheritance info when a parent class is declared');
	is_deeply($info->{parent_statements}, ['Parent'], 'the declared parent class is captured');
	ok($info->{uses_super}, 'SUPER:: usage in the method body is detected');
	ok(!$info->{has_own_constructor}, 'Child has no own new() method');
	ok($info->{use_parent_constructor}, 'falls back to the parent constructor since Child has none of its own');
	is($info->{parent_class}, 'Parent', 'the first declared parent is selected as the constructor source');

	returns_ok($info, { type => 'hashref' }, '_check_inheritance_for_constructor() returns a hashref');
	memory_cycle_ok($info, '_check_inheritance_for_constructor() result is free of circular references');
};

subtest 'SchemaExtractor::_check_inheritance_for_constructor - returns undef when there is no _document' => sub {
	my $e = bless {}, 'App::Test::Generator::SchemaExtractor';
	is($e->_check_inheritance_for_constructor('Foo', ''), undef, '...');
};

subtest 'SchemaExtractor::_detect_constructor_requirements - returns a minimal hashref for an external target package' => sub {
	my $e = bless { _document => PPI::Document->new(\"package Foo;\n1;\n") }, 'App::Test::Generator::SchemaExtractor';

	is_deeply(
		$e->_detect_constructor_requirements('Foo', 'Bar::External'),
		{
			external_class => 1,
			package => 'Bar::External',
			note => "Constructor for external class Bar::External - parameters unknown",
		},
		'a target package different from the current one is treated as external, with no source to analyse'
	);
};

subtest 'SchemaExtractor::_detect_constructor_requirements - extracts parameters, required flags, and defaults from new()' => sub {
	my $src = "package Foo;\nsub new {\n\tmy (\$class, \$host, \$port) = \@_;\n\tcroak \"host required\" unless defined \$host;\n\t\$port = \$port || 8080;\n\treturn bless { host => \$host, port => \$port }, \$class;\n}\n1;\n";
	my $e = bless { _document => PPI::Document->new(\$src) }, 'App::Test::Generator::SchemaExtractor';

	my $req = $e->_detect_constructor_requirements('Foo', 'Foo');
	is_deeply($req->{parameters}, ['host', 'port'], 'positional parameters are extracted from the my (...) = @_ pattern');
	is($req->{parameter_count}, 2, 'parameter_count matches the number of extracted parameters');
	is_deeply($req->{required_parameters}, ['host'], 'the croak-guarded parameter is flagged as required');
	is_deeply($req->{optional_parameters}, ['port'], 'the parameter with a default assignment is flagged optional');
	is($req->{default_values}{port}, 8080, 'the default value is captured via _extract_default_value');
};

subtest 'SchemaExtractor::_detect_constructor_requirements - returns undef when there is no new() method' => sub {
	my $e = bless { _document => PPI::Document->new(\"package Foo;\nsub bar { return 1 }\n1;\n") }, 'App::Test::Generator::SchemaExtractor';
	is($e->_detect_constructor_requirements('Foo', 'Foo'), undef, '...');
};

subtest 'SchemaExtractor::_detect_external_object_dependency - detects ->new()/->create() calls on other classes' => sub {
	my $e = bless {}, 'App::Test::Generator::SchemaExtractor';

	my $info = $e->_detect_external_object_dependency(q{ my $obj = Foo::Bar->new(); return $obj; });
	is_deeply($info->{creates_objects}, ['Foo::Bar'], 'the instantiated class is recorded');
	is($info->{package}, 'Foo::Bar', 'the first created class becomes the primary dependency package');
};

subtest 'SchemaExtractor::_detect_external_object_dependency - detects method calls on a typed object variable' => sub {
	my $e = bless {}, 'App::Test::Generator::SchemaExtractor';

	my $info = $e->_detect_external_object_dependency(q{ my $ua = LWP::UserAgent->new; $ua->get($url); });
	is_deeply($info->{uses_objects}, ['LWP::UserAgent'], 'the class of the object variable is inferred from its assignment');
};

subtest 'SchemaExtractor::_detect_external_object_dependency - returns undef for an undef or dependency-free body' => sub {



( run in 3.495 seconds using v1.01-cache-2.11-cpan-3c2a17b8caa )