Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add mapping errors check method for consistency between tests #586

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions tests/Integration/IntegrationTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@

use CuyZ\Valinor\Cache\FileSystemCache;
use CuyZ\Valinor\Mapper\MappingError;
use CuyZ\Valinor\Mapper\Tree\Message\Messages;
use CuyZ\Valinor\Mapper\Tree\Node;
use CuyZ\Valinor\MapperBuilder;
use CuyZ\Valinor\Tests\Integration\Mapping\Namespace\NamespacedInterfaceInferringTest;
use PHPUnit\Framework\TestCase;
use Psr\SimpleCache\CacheInterface;

use function array_keys;
use function bin2hex;
use function implode;
use function iterator_to_array;
Expand Down Expand Up @@ -65,6 +67,37 @@ protected function mapperBuilder(): MapperBuilder
return $builder;
}

/**
* @param non-empty-array<non-empty-string> $expected
*/
protected function assertMappingErrors(MappingError $error, array $expected): void
{
$errors = [];

foreach (Messages::flattenFromNode($error->node()) as $message) {
$errors[$message->node()->path()] = $message;
}

$remainingErrors = $errors;

foreach ($expected as $path => $message) {
self::assertArrayHasKey($path, $remainingErrors, "Error path `$path` not found in error messages, the following path(s) were found: `" . implode('`, `', array_keys($errors)) . '`.');

if (! preg_match('/^\[([^]]+)] (.*)/', $message, $matches)) {
self::fail('Incorrect error message format. Expected format: `[code] message`.');
}

self::assertSame($matches[1], $remainingErrors[$path]->code());
self::assertSame($matches[2], $remainingErrors[$path]->toString());

unset($remainingErrors[$path]);
}

if ($remainingErrors !== []) {
self::fail('Untested error messages at path(s): `' . implode('`, `', array_keys($remainingErrors)) . '`');
}
}

protected function mappingFail(MappingError $error): never
{
$errorFinder = static function (Node $node, callable $errorFinder) {
Expand Down
10 changes: 7 additions & 3 deletions tests/Integration/Mapping/Closure/ArgumentsMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,10 @@ public function test_invalid_source_with_one_error_throws_mapping_error(): void
} catch (MappingError $exception) {
self::assertMatchesRegularExpression('/Could not map arguments of `[^`]+` with value array{foo: false, bar: false}. A total of 2 errors were encountered./', $exception->getMessage());

self::assertSame('Value false is not a valid string.', (string)$exception->node()->children()['foo']->messages()[0]);
self::assertSame('Value false is not a valid integer.', (string)$exception->node()->children()['bar']->messages()[0]);
self::assertMappingErrors($exception, [
'foo' => '[unknown] Value false is not a valid string.',
'bar' => '[unknown] Value false is not a valid integer.',
]);
}
}

Expand All @@ -97,7 +99,9 @@ public function test_invalid_source_with_two_errors_throws_mapping_error(): void
} catch (MappingError $exception) {
self::assertMatchesRegularExpression('/Could not map arguments of `[^`]+`. An error occurred at path foo: Value false is not a valid string./', $exception->getMessage());

self::assertSame('Value false is not a valid string.', (string)$exception->node()->children()['foo']->messages()[0]);
self::assertMappingErrors($exception, [
'foo' => '[unknown] Value false is not a valid string.',
]);
}
}
}
Expand Down
14 changes: 6 additions & 8 deletions tests/Integration/Mapping/ConstructorRegistrationMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -673,10 +673,9 @@ public function test_source_not_matching_registered_constructors_throws_exceptio
->mapper()
->map(stdClass::class, []);
} catch (MappingError $exception) {
$error = $exception->node()->messages()[0];

self::assertSame('1642183169', $error->code());
self::assertSame('Value array (empty) does not match any of `string`, `array{bar: int, baz?: float}`.', (string)$error);
self::assertMappingErrors($exception, [
'*root*' => "[1642183169] Value array (empty) does not match any of `string`, `array{bar: int, baz?: float}`.",
]);
}
}

Expand Down Expand Up @@ -813,10 +812,9 @@ public function test_registered_constructor_throwing_exception_fails_mapping_wit

self::fail('No mapping error when one was expected');
} catch (MappingError $exception) {
$error = $exception->node()->messages()[0];

self::assertSame('1656076090', $error->code());
self::assertSame('some error message', (string)$error);
self::assertMappingErrors($exception, [
'*root*' => "[1656076090] some error message",
]);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,9 @@ public function test_register_internal_from_constructor_is_overridden_by_library
->mapper()
->map(BackedStringEnum::class, 'fiz');
} catch (MappingError $exception) {
$error = $exception->node()->messages()[0];

self::assertSame('1607027306', $error->code());
self::assertSame("Value 'fiz' does not match any of 'foo', 'bar', 'baz'.", (string)$error);
self::assertMappingErrors($exception, [
'*root*' => "[1607027306] Value 'fiz' does not match any of 'foo', 'bar', 'baz'.",
]);
}
}

Expand All @@ -137,10 +136,9 @@ public function test_register_internal_try_from_constructor_is_overridden_by_lib
->mapper()
->map(BackedStringEnum::class, 'fiz');
} catch (MappingError $exception) {
$error = $exception->node()->messages()[0];

self::assertSame('1607027306', $error->code());
self::assertSame("Value 'fiz' does not match any of 'foo', 'bar', 'baz'.", (string)$error);
self::assertMappingErrors($exception, [
'*root*' => "[1607027306] Value 'fiz' does not match any of 'foo', 'bar', 'baz'.",
]);
}
}
}
5 changes: 3 additions & 2 deletions tests/Integration/Mapping/EnumValueOfMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ public function test_array_keys_using_value_of_error(): void
->mapper()
->map('array<value-of<' . SomeStringEnumForValueOf::class . '>, string>', ['oof' => 'foo']);
} catch (MappingError $exception) {
$error = $exception->node()->children()['oof']->messages()[0];
self::assertSame("Key 'oof' does not match type `'FOO'|'FOZ'|'BAZ'`.", (string)$error);
self::assertMappingErrors($exception, [
'oof' => "[1630946163] Key 'oof' does not match type `'FOO'|'FOZ'|'BAZ'`.",
]);
}
}
}
Expand Down
7 changes: 3 additions & 4 deletions tests/Integration/Mapping/ExceptionFilteringTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,9 @@ public function test_userland_exception_filtered_is_caught_and_added_to_mapping_
->mapper()
->map(ClassThatThrowsExceptionIfInvalidValue::class, 'bar');
} catch (MappingError $exception) {
$error = $exception->node()->messages()[0];

self::assertSame('1657197780', $error->code());
self::assertSame('some error message', (string)$error);
self::assertMappingErrors($exception, [
'*root*' => "[1657197780] some error message",
]);
}
}
}
Expand Down
20 changes: 9 additions & 11 deletions tests/Integration/Mapping/InterfaceInferringMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -351,10 +351,9 @@ public function test_invalid_source_throws_exception(): void
->mapper()
->map(SomeInterface::class, 42);
} catch (MappingError $exception) {
$error = $exception->node()->messages()[0];

self::assertSame('1632903281', $error->code());
self::assertSame('Value 42 does not match type `array{type: string, key: int}`.', (string)$error);
self::assertMappingErrors($exception, [
'*root*' => "[1632903281] Value 42 does not match type `array{type: string, key: int}`.",
]);
}
}

Expand All @@ -370,9 +369,9 @@ public function test_invalid_source_value_throws_exception(): void
->mapper()
->map(SomeInterface::class, 'foo');
} catch (MappingError $exception) {
$error = $exception->node()->children()['key']->messages()[0];

self::assertSame("Value 'foo' is not a valid integer.", (string)$error);
self::assertMappingErrors($exception, [
'key' => "[unknown] Value 'foo' is not a valid integer.",
]);
}
}

Expand All @@ -391,10 +390,9 @@ public function test_superfluous_values_throws_exception(): void
'superfluousValue' => 'bar',
]);
} catch (MappingError $exception) {
$error = $exception->node()->messages()[0];

self::assertSame('1655117782', $error->code());
self::assertSame('Unexpected key(s) `superfluousValue`, expected `valueA`.', (string)$error);
self::assertMappingErrors($exception, [
'*root*' => "[1655117782] Unexpected key(s) `superfluousValue`, expected `valueA`.",
]);
}
}
}
Expand Down
21 changes: 8 additions & 13 deletions tests/Integration/Mapping/Object/ArrayValuesMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,27 +69,22 @@ public function test_values_are_mapped_properly(): void
public function test_empty_array_in_non_empty_array_throws_exception(): void
{
try {
$this->mapperBuilder()->mapper()->map(ArrayValues::class, [
'nonEmptyArraysOfStrings' => [],
]);
$this->mapperBuilder()->mapper()->map('non-empty-array<string>', []);
} catch (MappingError $exception) {
$error = $exception->node()->children()['nonEmptyArraysOfStrings']->messages()[0];

self::assertSame('1630678334', $error->code());
self::assertSame('Value array (empty) does not match type `non-empty-array<string>`.', (string)$error);
self::assertMappingErrors($exception, [
'*root*' => '[1630678334] Value array (empty) does not match type `non-empty-array<string>`.',
]);
}
}

public function test_value_with_invalid_type_throws_exception(): void
{
try {
$this->mapperBuilder()->mapper()->map(ArrayValues::class, [
'integers' => ['foo'],
]);
$this->mapperBuilder()->mapper()->map('array<int>', ['foo']);
} catch (MappingError $exception) {
$error = $exception->node()->children()['integers']->children()[0]->messages()[0];

self::assertSame("Value 'foo' is not a valid integer.", (string)$error);
self::assertMappingErrors($exception, [
'0' => "[unknown] Value 'foo' is not a valid integer.",
]);
}
}
}
Expand Down
14 changes: 6 additions & 8 deletions tests/Integration/Mapping/Object/ConstantValuesMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,9 @@ public function test_private_constant_cannot_be_mapped(): void
->mapper()
->map(ObjectWithConstants::class . '::CONST_WITH_STRING_*', 'some private string value');
} catch (MappingError $exception) {
$error = $exception->node()->messages()[0];

self::assertSame('1607027306', $error->code());
self::assertSame("Value 'some private string value' does not match any of 'some string value', 'another string value'.", (string)$error);
self::assertMappingErrors($exception, [
'*root*' => "[1607027306] Value 'some private string value' does not match any of 'some string value', 'another string value'.",
]);
}
}

Expand All @@ -84,10 +83,9 @@ public function test_constant_not_matching_pattern_cannot_be_mapped(): void
->mapper()
->map(ObjectWithConstants::class . '::CONST_WITH_STRING_*', 'some prefixed string value');
} catch (MappingError $exception) {
$error = $exception->node()->messages()[0];

self::assertSame('1607027306', $error->code());
self::assertSame("Value 'some prefixed string value' does not match any of 'some string value', 'another string value'.", (string)$error);
self::assertMappingErrors($exception, [
'*root*' => "[1607027306] Value 'some prefixed string value' does not match any of 'some string value', 'another string value'.",
]);
}
}
}
Expand Down
27 changes: 12 additions & 15 deletions tests/Integration/Mapping/Object/DateTimeMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ public function test_default_datetime_constructor_cannot_be_used(): void
->mapper()
->map(DateTimeInterface::class, ['datetime' => '2022/08/05', 'timezone' => 'Europe/Paris']);
} catch (MappingError $exception) {
$error = $exception->node()->messages()[0];

self::assertSame('1607027306', $error->code());
self::assertMappingErrors($exception, [
'*root*' => "[1607027306] Value array{datetime: '2022/08/05', timezone: 'Europe/Paris'} does not match any of `non-empty-string`, `int`, `float`.",
]);
}
}

Expand Down Expand Up @@ -139,10 +139,9 @@ public function test_default_date_constructor_with_invalid_source_throws_excepti
->mapper()
->map(DateTimeInterface::class, 'invalid datetime');
} catch (MappingError $exception) {
$error = $exception->node()->messages()[0];

self::assertSame('1630686564', $error->code());
self::assertSame("Value 'invalid datetime' does not match any of the following formats: `Y-m-d\TH:i:sP`, `Y-m-d\TH:i:s.uP`, `U`, `U.u`.", (string)$error);
self::assertMappingErrors($exception, [
'*root*' => "[1630686564] Value 'invalid datetime' does not match any of the following formats: `Y-m-d\TH:i:sP`, `Y-m-d\TH:i:s.uP`, `U`, `U.u`.",
]);
}
}

Expand All @@ -154,10 +153,9 @@ public function test_registered_date_constructor_with_invalid_source_throws_exce
->mapper()
->map(DateTimeInterface::class, 'invalid datetime');
} catch (MappingError $exception) {
$error = $exception->node()->messages()[0];

self::assertSame('1630686564', $error->code());
self::assertSame("Value 'invalid datetime' does not match any of the following formats: `Y/m/d`.", (string)$error);
self::assertMappingErrors($exception, [
'*root*' => "[1630686564] Value 'invalid datetime' does not match any of the following formats: `Y/m/d`.",
]);
}
}

Expand All @@ -170,10 +168,9 @@ public function test_date_constructor_with_overridden_format_source_throws_excep
->mapper()
->map(DateTimeInterface::class, '1971-11-08');
} catch (MappingError $exception) {
$error = $exception->node()->messages()[0];

self::assertSame('1630686564', $error->code());
self::assertSame("Value '1971-11-08' does not match any of the following formats: `d/m/Y`.", (string)$error);
self::assertMappingErrors($exception, [
'*root*' => "[1630686564] Value '1971-11-08' does not match any of the following formats: `d/m/Y`.",
]);
}
}
}
6 changes: 3 additions & 3 deletions tests/Integration/Mapping/Object/DateTimeZoneMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ public function test_invalid_timezone_throws_exception(): void
try {
$this->mapperBuilder()->mapper()->map(DateTimeZone::class, 'Jupiter/Europa');
} catch (MappingError $exception) {
$error = $exception->node()->messages()[0];

self::assertSame("Value 'Jupiter/Europa' is not a valid timezone.", $error->toString());
self::assertMappingErrors($exception, [
'*root*' => "[unknown] Value 'Jupiter/Europa' is not a valid timezone.",
]);
}
}
}
Loading
Loading