test : tests adversariaux InventoryMapper + docblock EarTagSeriesDto (#3)
Some checks failed
Auto Tag Develop / tag (push) Failing after 16s
Some checks failed
Auto Tag Develop / tag (push) Failing after 16s
| Numéro du ticket | Titre du ticket | |------------------|-----------------| | | | ## Description de la PR ## Modification du .env ## Check list - [ ] Pas de régression - [ ] TU/TI/TF rédigée - [ ] TU/TI/TF OK - [ ] CHANGELOG modifié Reviewed-on: #3 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #3.
This commit is contained in:
190
tests/Unit/Bovin/Mapper/BovinNodeMappingTraitTest.php
Normal file
190
tests/Unit/Bovin/Mapper/BovinNodeMappingTraitTest.php
Normal file
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Malio\EdnotifBundle\Tests\Unit\Bovin\Mapper;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Malio\EdnotifBundle\Bovin\Mapper\BovinNodeMappingTrait;
|
||||
use PHPUnit\Framework\Attributes\CoversTrait;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
#[CoversTrait(BovinNodeMappingTrait::class)]
|
||||
final class BovinNodeMappingTraitTest extends TestCase
|
||||
{
|
||||
// ---------- normalizeToList ----------
|
||||
|
||||
public function testNormalizeToListWithNullReturnsEmptyList(): void
|
||||
{
|
||||
self::assertSame([], self::adapter()->normalizeToList(null));
|
||||
}
|
||||
|
||||
public function testNormalizeToListWithScalarWrapsIntoList(): void
|
||||
{
|
||||
self::assertSame(['foo'], self::adapter()->normalizeToList('foo'));
|
||||
self::assertSame([42], self::adapter()->normalizeToList(42));
|
||||
}
|
||||
|
||||
public function testNormalizeToListWithObjectWrapsIntoList(): void
|
||||
{
|
||||
$obj = new stdClass();
|
||||
$result = self::adapter()->normalizeToList($obj);
|
||||
|
||||
self::assertCount(1, $result);
|
||||
self::assertSame($obj, $result[0]);
|
||||
}
|
||||
|
||||
public function testNormalizeToListWithListReturnsListUnchanged(): void
|
||||
{
|
||||
$input = ['a', 'b', 'c'];
|
||||
self::assertSame($input, self::adapter()->normalizeToList($input));
|
||||
}
|
||||
|
||||
public function testNormalizeToListWithAssociativeArrayDiscardsKeys(): void
|
||||
{
|
||||
$input = ['x' => 'a', 'y' => 'b'];
|
||||
$result = self::adapter()->normalizeToList($input);
|
||||
|
||||
self::assertSame(['a', 'b'], $result);
|
||||
}
|
||||
|
||||
#[DataProvider('toNullableStringProvider')]
|
||||
public function testToNullableString(mixed $input, ?string $expected): void
|
||||
{
|
||||
self::assertSame($expected, self::adapter()->toNullableString($input));
|
||||
}
|
||||
|
||||
// ---------- toNullableString ----------
|
||||
|
||||
/** @return iterable<string,array{mixed, ?string}> */
|
||||
public static function toNullableStringProvider(): iterable
|
||||
{
|
||||
yield 'null' => [null, null];
|
||||
|
||||
yield 'empty string' => ['', null];
|
||||
|
||||
yield 'whitespace only' => [' ', null];
|
||||
|
||||
yield 'plain' => ['abc', 'abc'];
|
||||
|
||||
yield 'trimmed' => [' abc ', 'abc'];
|
||||
|
||||
yield 'int coerced to string' => [42, '42'];
|
||||
|
||||
yield 'zero preserved' => ['0', '0'];
|
||||
}
|
||||
|
||||
#[DataProvider('toNullableIntProvider')]
|
||||
public function testToNullableInt(mixed $input, ?int $expected): void
|
||||
{
|
||||
self::assertSame($expected, self::adapter()->toNullableInt($input));
|
||||
}
|
||||
|
||||
// ---------- toNullableInt ----------
|
||||
|
||||
/** @return iterable<string,array{mixed, ?int}> */
|
||||
public static function toNullableIntProvider(): iterable
|
||||
{
|
||||
yield 'null' => [null, null];
|
||||
|
||||
yield 'int passthrough' => [42, 42];
|
||||
|
||||
yield 'zero int' => [0, 0];
|
||||
|
||||
yield 'numeric string' => ['42', 42];
|
||||
|
||||
yield 'negative string' => ['-7', -7];
|
||||
|
||||
yield 'float-like string' => ['3.14', 3];
|
||||
|
||||
yield 'non-numeric' => ['abc', null];
|
||||
|
||||
yield 'empty string' => ['', null];
|
||||
}
|
||||
|
||||
#[DataProvider('toNullableBoolProvider')]
|
||||
public function testToNullableBool(mixed $input, ?bool $expected): void
|
||||
{
|
||||
self::assertSame($expected, self::adapter()->toNullableBool($input));
|
||||
}
|
||||
|
||||
// ---------- toNullableBool ----------
|
||||
|
||||
/** @return iterable<string,array{mixed, ?bool}> */
|
||||
public static function toNullableBoolProvider(): iterable
|
||||
{
|
||||
yield 'null' => [null, null];
|
||||
|
||||
yield 'true' => [true, true];
|
||||
|
||||
yield 'false' => [false, false];
|
||||
|
||||
yield 'string 1' => ['1', true];
|
||||
|
||||
yield 'string 0' => ['0', false];
|
||||
|
||||
yield 'empty string' => ['', false];
|
||||
|
||||
yield 'int 1' => [1, true];
|
||||
|
||||
yield 'int 0' => [0, false];
|
||||
}
|
||||
|
||||
// ---------- toNullableDate ----------
|
||||
|
||||
public function testToNullableDateFromIsoString(): void
|
||||
{
|
||||
$result = self::adapter()->toNullableDate('2026-04-21');
|
||||
self::assertEquals(new DateTimeImmutable('2026-04-21'), $result);
|
||||
}
|
||||
|
||||
public function testToNullableDateFromDateTimeString(): void
|
||||
{
|
||||
$result = self::adapter()->toNullableDate('2026-04-21T10:30:00+02:00');
|
||||
self::assertEquals(new DateTimeImmutable('2026-04-21T10:30:00+02:00'), $result);
|
||||
}
|
||||
|
||||
#[DataProvider('toNullableDateFallbackProvider')]
|
||||
public function testToNullableDateReturnsNullOnInvalidInput(mixed $input): void
|
||||
{
|
||||
self::assertNull(self::adapter()->toNullableDate($input));
|
||||
}
|
||||
|
||||
/** @return iterable<string,array{mixed}> */
|
||||
public static function toNullableDateFallbackProvider(): iterable
|
||||
{
|
||||
yield 'null' => [null];
|
||||
|
||||
yield 'empty string' => [''];
|
||||
|
||||
yield 'whitespace' => [' '];
|
||||
|
||||
yield 'non-string int' => [42];
|
||||
|
||||
yield 'non-string array' => [[]];
|
||||
|
||||
yield 'invalid date string' => ['not-a-date'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter anonyme : re-expose chaque helper protected en public pour le test,
|
||||
* sans jamais instancier un vrai mapper (ceux-ci ont des dépendances DI).
|
||||
*/
|
||||
private static function adapter(): object
|
||||
{
|
||||
return new class {
|
||||
use BovinNodeMappingTrait {
|
||||
normalizeToList as public;
|
||||
toNullableString as public;
|
||||
toNullableInt as public;
|
||||
toNullableBool as public;
|
||||
toNullableDate as public;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,82 @@ final class InventoryMapperTest extends TestCase
|
||||
self::assertNull($inventory->startDate);
|
||||
}
|
||||
|
||||
public function testMapInventoryWithStockBouclesAbsentYieldsNullFlag(): void
|
||||
{
|
||||
$mapper = new InventoryMapper(new AnimalSummaryMapper(), new StandardResponseMapper());
|
||||
|
||||
$message = new stdClass();
|
||||
$message->InformationsMessage = new stdClass();
|
||||
$message->InformationsMessage->DateDebut = '2026-01-01';
|
||||
// StockBoucles deliberately omitted
|
||||
|
||||
$inventory = $mapper->map($this->makeSoapResponse(), $message);
|
||||
|
||||
self::assertNull($inventory->includesEarTagStock);
|
||||
}
|
||||
|
||||
public function testMapInventoryWithStockBouclesZeroYieldsFalseFlag(): void
|
||||
{
|
||||
$mapper = new InventoryMapper(new AnimalSummaryMapper(), new StandardResponseMapper());
|
||||
|
||||
$message = new stdClass();
|
||||
$message->InformationsMessage = new stdClass();
|
||||
$message->InformationsMessage->StockBoucles = '0';
|
||||
|
||||
$inventory = $mapper->map($this->makeSoapResponse(), $message);
|
||||
|
||||
self::assertFalse($inventory->includesEarTagStock);
|
||||
}
|
||||
|
||||
public function testMapInventoryWithDateFinAbsentYieldsNullEndDate(): void
|
||||
{
|
||||
$mapper = new InventoryMapper(new AnimalSummaryMapper(), new StandardResponseMapper());
|
||||
|
||||
$message = new stdClass();
|
||||
$message->InformationsMessage = new stdClass();
|
||||
$message->InformationsMessage->DateDebut = '2026-01-01';
|
||||
// DateFin deliberately omitted
|
||||
|
||||
$inventory = $mapper->map($this->makeSoapResponse(), $message);
|
||||
|
||||
self::assertNull($inventory->endDate);
|
||||
}
|
||||
|
||||
public function testMapInventoryWithSerieBouclesAsListPreservesAllEntries(): void
|
||||
{
|
||||
$mapper = new InventoryMapper(new AnimalSummaryMapper(), new StandardResponseMapper());
|
||||
|
||||
$serie1 = new stdClass();
|
||||
$serie1->NumeroSerieDebut = 'A0001';
|
||||
$serie2 = new stdClass();
|
||||
$serie2->NumeroSerieDebut = 'B0001';
|
||||
|
||||
$message = new stdClass();
|
||||
$message->Boucles = new stdClass();
|
||||
$message->Boucles->SerieBoucles = [$serie1, $serie2];
|
||||
|
||||
$inventory = $mapper->map($this->makeSoapResponse(), $message);
|
||||
|
||||
self::assertCount(2, $inventory->earTagSeries);
|
||||
self::assertSame('A0001', $inventory->earTagSeries[0]->rawNode->NumeroSerieDebut);
|
||||
self::assertSame('B0001', $inventory->earTagSeries[1]->rawNode->NumeroSerieDebut);
|
||||
}
|
||||
|
||||
public function testMapInventoryWithMissingNbBovinsDefaultsToZero(): void
|
||||
{
|
||||
$mapper = new InventoryMapper(new AnimalSummaryMapper(), new StandardResponseMapper());
|
||||
|
||||
$soapResponse = new stdClass();
|
||||
$soapResponse->ReponseStandard = new stdClass();
|
||||
$soapResponse->ReponseStandard->Resultat = true;
|
||||
$soapResponse->ReponseSpecifique = new stdClass();
|
||||
// NbBovins deliberately omitted
|
||||
|
||||
$inventory = $mapper->map($soapResponse, null);
|
||||
|
||||
self::assertSame(0, $inventory->nbBovins);
|
||||
}
|
||||
|
||||
private function makeSoapResponse(): object
|
||||
{
|
||||
$response = new stdClass();
|
||||
|
||||
Reference in New Issue
Block a user