toBeInstanceOf(IncoCompo::class) ->and($compo)->toBeInstanceOf(Dto::class) ->and($compo->id)->toBeInstanceOf(IncoCompoId::class) ->and($compo->id->value)->toBe('10') ->and($compo->idInco)->toBeInstanceOf(IncoId::class) ->and($compo->idInco->value)->toBe('20'); // Verify all 14 parts are instances of AbstractIncoCompoPartDto expect($compo->identification)->toBeInstanceOf(Identification::class) ->and($compo->identification)->toBeInstanceOf(AbstractIncoCompoPartDto::class) ->and($compo->identification->nom)->toBe('Composition Poulet') ->and($compo->identification->numero)->toBe('COMP-001') ->and($compo->identification->source)->toBe('Laboratoire LDC'); expect($compo->energie)->toBeInstanceOf(Energie::class) ->and($compo->energie)->toBeInstanceOf(AbstractIncoCompoPartDto::class) ->and($compo->energie->valEnergKcalReference)->toBe('155') ->and($compo->energie->valEnergKcalPortion)->toBe('232.5'); expect($compo->macronutrimentsRef)->toBeInstanceOf(MacronutrimentsRef::class) ->and($compo->macronutrimentsRef)->toBeInstanceOf(AbstractIncoCompoPartDto::class) ->and($compo->macronutrimentsRef->proteineReference)->toBe('22.0'); expect($compo->macronutrimentsPortion)->toBeInstanceOf(MacronutrimentsPortion::class) ->and($compo->macronutrimentsPortion)->toBeInstanceOf(AbstractIncoCompoPartDto::class) ->and($compo->macronutrimentsPortion->proteinePortion)->toBe('33.0'); expect($compo->fnl)->toBeInstanceOf(FruitLegumeNoix::class) ->and($compo->fnl)->toBeInstanceOf(AbstractIncoCompoPartDto::class); expect($compo->apportsReference)->toBeInstanceOf(ApportsReference::class) ->and($compo->apportsReference)->toBeInstanceOf(AbstractIncoCompoPartDto::class) ->and($compo->apportsReference->pSurL)->toBe('2.1'); expect($compo->nutriScore)->toBeInstanceOf(NutriScore::class) ->and($compo->nutriScore)->toBeInstanceOf(AbstractIncoCompoPartDto::class) ->and($compo->nutriScore->classement)->toBe('B'); expect($compo->pointsNutriScore)->toBeInstanceOf(PointsNutriScore::class) ->and($compo->pointsNutriScore)->toBeInstanceOf(AbstractIncoCompoPartDto::class) ->and($compo->pointsNutriScore->pointsProteines)->toBe('4'); expect($compo->mineraux)->toBeInstanceOf(Mineraux::class) ->and($compo->mineraux)->toBeInstanceOf(AbstractIncoCompoPartDto::class) ->and($compo->mineraux->nutCa)->toBe('12'); expect($compo->vitamines)->toBeInstanceOf(Vitamines::class) ->and($compo->vitamines)->toBeInstanceOf(AbstractIncoCompoPartDto::class) ->and($compo->vitamines->nutVita)->toBe('20'); expect($compo->glucidesDetail)->toBeInstanceOf(GlucidesDetail::class) ->and($compo->glucidesDetail)->toBeInstanceOf(AbstractIncoCompoPartDto::class) ->and($compo->glucidesDetail->nutStarch)->toBe('0.5'); expect($compo->acideGras)->toBeInstanceOf(AcideGras::class) ->and($compo->acideGras)->toBeInstanceOf(AbstractIncoCompoPartDto::class) ->and($compo->acideGras->nutFd)->toBe('10.5') ->and($compo->acideGras->nutChol)->toBe('60'); expect($compo->acidesAmines)->toBeInstanceOf(AcidesAmines::class) ->and($compo->acidesAmines)->toBeInstanceOf(AbstractIncoCompoPartDto::class) ->and($compo->acidesAmines->nutAae)->toBe('15'); expect($compo->composesSpeciaux)->toBeInstanceOf(ComposesSpeciaux::class) ->and($compo->composesSpeciaux)->toBeInstanceOf(AbstractIncoCompoPartDto::class) ->and($compo->composesSpeciaux->nutCholn)->toBe('30'); }); it('can jsonSerialize complete IncoCompo graph recursively', function (): void { // GIVEN $compo = createCompleteIncoCompo('10', '20'); // WHEN $serialized = $compo->jsonSerialize(); // THEN expect($serialized)->toBeArray() ->and($serialized['id'])->toBe('10') ->and($serialized['idInco'])->toBe('20') ->and($serialized['identification'])->toBe($compo->identification) ->and($serialized['energie'])->toBe($compo->energie) ->and($serialized['macronutrimentsRef'])->toBe($compo->macronutrimentsRef) ->and($serialized['macronutrimentsPortion'])->toBe($compo->macronutrimentsPortion) ->and($serialized['fnl'])->toBe($compo->fnl) ->and($serialized['apportsReference'])->toBe($compo->apportsReference) ->and($serialized['nutriScore'])->toBe($compo->nutriScore) ->and($serialized['pointsNutriScore'])->toBe($compo->pointsNutriScore) ->and($serialized['mineraux'])->toBe($compo->mineraux) ->and($serialized['vitamines'])->toBe($compo->vitamines) ->and($serialized['glucidesDetail'])->toBe($compo->glucidesDetail) ->and($serialized['acideGras'])->toBe($compo->acideGras) ->and($serialized['acidesAmines'])->toBe($compo->acidesAmines) ->and($serialized['composesSpeciaux'])->toBe($compo->composesSpeciaux); // Recursive json_encode check $jsonString = json_encode($compo); expect($jsonString)->toBeString(); $decoded = json_decode((string) $jsonString, true); expect($decoded)->toBeArray() ->and($decoded['id'])->toBe('10') ->and($decoded['idInco'])->toBe('20') ->and($decoded['identification']['nom'])->toBe('Composition Poulet') ->and($decoded['energie']['valEnergKcalReference'])->toBe('155') ->and($decoded['macronutrimentsRef']['proteineReference'])->toBe('22.0') ->and($decoded['nutriScore']['classement'])->toBe('B') ->and($decoded['mineraux']['nutCa'])->toBe('12') ->and($decoded['vitamines']['nutVita'])->toBe('20') ->and($decoded['acideGras']['nutChol'])->toBe('60') ->and($decoded['acidesAmines']['nutAae'])->toBe('15') ->and($decoded['composesSpeciaux']['nutCholn'])->toBe('30'); }); it('exercises humanReadableValues and units across compo parts', function (): void { // GIVEN $compo = createCompleteIncoCompo(); // WHEN & THEN: Energie $energyValues = $compo->energie->humanReadableValues(); expect($energyValues)->toBeArray() ->and($energyValues['Taille de référence'])->toBe('100g') ->and($energyValues['Énergie (kJ) / 100g'])->toBe('650') ->and($energyValues['Énergie (kcal) / 100g'])->toBe('155'); $energyUnits = Energie::units(); expect($energyUnits['valEnergKjReference'])->toBe('kJ') ->and($energyUnits['valEnergKcalReference'])->toBe('kcal'); // Energie string cast $energyString = (string) $compo->energie; expect($energyString)->toContain('Taille de référence : 100g') ->and($energyString)->toContain('Énergie (kcal) / 100g : 155 kcal'); // Identification human readable $identValues = $compo->identification->humanReadableValues(); expect($identValues['Nom'])->toBe('Composition Poulet') ->and($identValues['Numéro'])->toBe('COMP-001') ->and($identValues['Source'])->toBe('Laboratoire LDC'); // NutriScore human readable $nsValues = $compo->nutriScore->humanReadableValues(); expect($nsValues['Classement (A-E)'])->toBe('B'); // AcideGras units $agUnits = AcideGras::units(); expect($agUnits['nutChol'])->toBe('mg') ->and($agUnits['nutFd'])->toBe('g'); }); it('covers fieldLabels, fieldUnits, humanReadableValues, units, humanReadableUnits, and string cast for all 14 nutritional parts', function ( string $partClass, string $property ): void { // GIVEN $compo = createCompleteIncoCompo(); /** @var AbstractIncoCompoPartDto $part */ $part = $compo->{$property}; // WHEN $humanValues = $part->humanReadableValues(); $arrayValues = $part->toArray(); $units = $partClass::units(); $humanUnits = $partClass::humanReadableUnits(); $asString = (string) $part; // THEN expect($humanValues)->toBeArray()->not->toBeEmpty() ->and($arrayValues)->toBeArray()->not->toBeEmpty() ->and($units)->toBeArray()->not->toBeEmpty() ->and($humanUnits)->toBeArray()->not->toBeEmpty() ->and($asString)->toBeString()->not->toBeEmpty(); })->with([ 'Identification' => [Identification::class, 'identification'], 'Energie' => [Energie::class, 'energie'], 'MacronutrimentsRef' => [MacronutrimentsRef::class, 'macronutrimentsRef'], 'MacronutrimentsPortion' => [MacronutrimentsPortion::class, 'macronutrimentsPortion'], 'FruitLegumeNoix' => [FruitLegumeNoix::class, 'fnl'], 'ApportsReference' => [ApportsReference::class, 'apportsReference'], 'NutriScore' => [NutriScore::class, 'nutriScore'], 'PointsNutriScore' => [PointsNutriScore::class, 'pointsNutriScore'], 'Mineraux' => [Mineraux::class, 'mineraux'], 'Vitamines' => [Vitamines::class, 'vitamines'], 'GlucidesDetail' => [GlucidesDetail::class, 'glucidesDetail'], 'AcideGras' => [AcideGras::class, 'acideGras'], 'AcidesAmines' => [AcidesAmines::class, 'acidesAmines'], 'ComposesSpeciaux' => [ComposesSpeciaux::class, 'composesSpeciaux'], ]); });