App-Test-Generator

 view release on metacpan or  search on metacpan

t/SchemaExtractor.t  view on Meta::CPAN


# ==================================================================
# _analyze_pod (via extract_all integration)
# --------------------------------------------------
# Tests that POD documentation is correctly parsed
# into parameter specs by running the full extractor
# ==================================================================
subtest '_analyze_pod integration' => sub {
	my $source = <<'PM';
package PodTest;

=head1 METHODS

=head2 greet($name, $age)

Say hello.

=head3 Parameters

$name - string (3-50 chars), the person's name
$age - integer (0-150), the person's age

=cut

sub greet {
	my ($self, $name, $age) = @_;
	return "Hello $name";
}

1;
PM

	my $e = _extractor($source);
	my $schemas = $e->extract_all(no_write => 1);

	ok(exists $schemas->{greet}, 'greet schema extracted');
	my $input = $schemas->{greet}{input};

	# Name parameter should have type and constraints from POD
	ok(exists $input->{name},          'name param extracted from POD');
	is($input->{name}{type}, 'string', 'name type is string');
	is($input->{name}{min},  3,        'name min extracted from POD');
	is($input->{name}{max},  50,       'name max extracted from POD');

	# Age parameter should have integer type from POD
	ok(exists $input->{age},            'age param extracted from POD');
	is($input->{age}{type}, 'integer',  'age type is integer');

	done_testing();
};

# ==================================================================
# _analyze_code (via extract_all integration)
# --------------------------------------------------
# Tests that code patterns are analysed correctly
# ==================================================================
subtest '_analyze_code integration' => sub {
	my $source = <<'PM';
package CodeTest;

use Scalar::Util qw(looks_like_number);

sub add {
	my ($x, $y) = @_;
	die 'not numeric' unless looks_like_number($x);
	return $x + ($y // 0);
}

sub get_name {
	my ($self) = @_;
	return $self->{name};
}

1;
PM

	my $e = _extractor($source, include_private => 0);
	my $schemas = $e->extract_all(no_write => 1);

	# add() schema should be extracted
	ok(exists $schemas->{add}, 'add schema extracted');

	# get_name() should be extracted
	ok(exists $schemas->{get_name}, 'get_name schema extracted');

	# get_name() should ideally be detected as a getter accessor
	if(exists $schemas->{get_name}{accessor}) {
		is($schemas->{get_name}{accessor}{type}, 'getter',
			'get_name detected as getter');
	}

	done_testing();
};

# ==================================================================
# _extract_package_name (via extract_all integration)
# --------------------------------------------------
# Tests that the package name is correctly extracted
# and propagated into all method schemas
# ==================================================================
subtest '_extract_package_name integration' => sub {
	my $source = "package My::Test::Module;\nsub run { return 1; }\n1;\n";
	my $e = _extractor($source);
	my $schemas = $e->extract_all(no_write => 1);

	# The module key in each schema must be the correct package name
	for my $method (keys %{$schemas}) {
		is($schemas->{$method}{module}, 'My::Test::Module',
			"$method schema has correct module name");
	}

	done_testing();
};

# ==================================================================
# extract_all
# --------------------------------------------------
# Integration tests for the main public entry point
# ==================================================================
subtest 'extract_all' => sub {
	my $source = <<'PM';
package ExtractTest;

sub new {
	my ($class, %args) = @_;

t/SchemaExtractor.t  view on Meta::CPAN


	# is_valid should be detected as returning boolean
	if(exists $schemas->{is_valid}) {
		is($schemas->{is_valid}{output}{type}, 'boolean',
			'is_valid output detected as boolean');
	}

	# clone should be detected as returning object
	if(exists $schemas->{clone}) {
		is($schemas->{clone}{output}{type}, 'object',
			'clone output detected as object');
	}

	done_testing();
};

# ==================================================================
# _detect_enum_type (via extract_all integration)
# ==================================================================
subtest '_detect_enum_type integration' => sub {
	my $source = <<'PM';
package EnumTest;

sub set_status {
	my ($self, $status) = @_;
	die "bad status" unless $status =~ /^(active|inactive|pending)$/;
	$self->{status} = $status;
}

1;
PM

	my $e = _extractor($source, include_private => 0);
	my $schemas = $e->extract_all(no_write => 1);

	# set_status should have enum values from the regex alternation
	if(exists $schemas->{set_status} &&
	   exists $schemas->{set_status}{input}{status}) {
		my $status_spec = $schemas->{set_status}{input}{status};
		if(exists $status_spec->{enum}) {
			my %enum_vals = map { $_ => 1 } @{$status_spec->{enum}};
			ok($enum_vals{active},   'active in enum');
			ok($enum_vals{inactive}, 'inactive in enum');
			ok($enum_vals{pending},  'pending in enum');
		}
	}

	done_testing();
};

# ==================================================================
# _yamltest_hints integration
# --------------------------------------------------
# Tests that numeric boundary hints are added for
# methods with numeric intent
# ==================================================================
subtest '_yamltest_hints integration' => sub {
	my $source = <<'PM';
package HintsTest;

use Scalar::Util qw(looks_like_number);

sub scale {
	my ($self, $factor) = @_;
	die 'not numeric' unless looks_like_number($factor);
	die 'negative'    if $factor < 0;
	return $self->{value} * $factor;
}

1;
PM

	my $e = _extractor($source);
	my $schemas = $e->extract_all(no_write => 1);

	# scale() should have boundary values in its _yamltest_hints
	if(exists $schemas->{scale}) {
		my $hints = $schemas->{scale}{_yamltest_hints} // {};
		if(exists $hints->{boundary_values}) {
			ok(scalar @{$hints->{boundary_values}} > 0,
				'boundary values present for numeric method');
		}
	}

	done_testing();
};

done_testing();



( run in 0.785 second using v1.01-cache-2.11-cpan-6aa56a78535 )