makeBovinNode(); $summary = new AnimalSummaryMapper()->map($node); self::assertInstanceOf(AnimalSummaryDto::class, $summary); self::assertNotNull($summary->identification); self::assertSame('FR1234567890', $summary->identification->bovin?->nationalNumber); self::assertSame('F', $summary->identification->sex); self::assertCount(2, $summary->presencePeriods); } public function testMapHandlesMissingOptionalNodes(): void { $summary = new AnimalSummaryMapper()->map(new stdClass()); self::assertNull($summary->identification); self::assertSame([], $summary->presencePeriods); } private function makeBovinNode(): object { $node = new stdClass(); $node->IdentiteBovin = new stdClass(); $node->IdentiteBovin->Sexe = 'F'; $node->IdentiteBovin->Bovin = new stdClass(); $node->IdentiteBovin->Bovin->CodePays = 'FR'; $node->IdentiteBovin->Bovin->NumeroNational = 'FR1234567890'; $node->PeriodesPresences = new stdClass(); $node->PeriodesPresences->PeriodePresence = [ $this->makePresencePeriod('2024-01-10', '2024-06-01'), $this->makePresencePeriod('2024-06-02', null), ]; return $node; } private function makePresencePeriod(string $entryDate, ?string $exitDate): object { $period = new stdClass(); $period->Entree = new stdClass(); $period->Entree->DateEntree = $entryDate; if (null !== $exitDate) { $period->Sortie = new stdClass(); $period->Sortie->DateSortie = $exitDate; } return $period; } }