Skip to content

Commit

Permalink
test: add test for non-intersecting hashmap type constructors
Browse files Browse the repository at this point in the history
In this test, Valinor considers two disjoint input types as colliding.

Similar to #487, this scenario was
detected while trying to map multiple constructors for XML structures
that may present different data depending on singular/plural entries
found:

```xml
<Invoices>
  <Invoice>
    <!-- `Item` is singular, and needs to be handled as `Item` -->
    <Item><Price>123</Price></Item>
  </Invoice>
  <Invoice>
    <!-- `Item` is plural, and needs to be handled as `list<Item>` -->
    <Item><Price>456</Price></Item>
    <Item><Price>789</Price></Item>
  </Invoice>
</Invoices>
```

In #487, we attempted to map a single constructor using
`array{foo: T|list<T>}`, while in this patch, we found the issue because
we attempted to attack the problem by declaring separate constructors
that would work on `array{foo: T}` and `array{foo: list<T>}` disjointly,
but failed to do so due to aggressive collision detection logic.

Initially discovered by @Tigerman55
  • Loading branch information
Ocramius committed Sep 2, 2024
1 parent 2150dca commit e1142fe
Showing 1 changed file with 27 additions and 0 deletions.
27 changes: 27 additions & 0 deletions tests/Integration/Mapping/ConstructorRegistrationMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,33 @@ public function test_identical_registered_constructors_with_several_argument_thr
->map(stdClass::class, []);
}

public function test_non_intersecting_hashmap_type_constructors_do_not_lead_to_collisions(): void
{
$mapper = $this->mapperBuilder()
->registerConstructor(
/** @param array{key: SimpleObject} $input */
static fn (array $input): stdClass => (object)['single-item' => $input],
/** @param array{key: list<SimpleObject>} $input */
static fn (array $input): stdClass => (object)['multiple-items' => $input],
)
->mapper();

$hello = new SimpleObject();
$world = new SimpleObject();

$hello->value = 'hello';
$world->value = 'world';

try {
self::assertEquals(
(object) ['multiple-items' => ['key' => [$hello, $world]]],
$mapper->map(stdClass::class, ['key' => ['hello', 'world']])
);
} catch (MappingError $error) {
$this->mappingFail($error);
}
}

public function test_source_not_matching_registered_constructors_throws_exception(): void
{
try {
Expand Down

0 comments on commit e1142fe

Please sign in to comment.