Files
svc-essensplanung/tests/Unit/Logic/Essen/Query/MahlzeitInspirationQueryTest.php

147 lines
5.7 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace App\Tests\Unit\Logic\Essen\Query;
use App\Logic\Common\ClockInterface;
use App\Logic\Essen\Manager\MahlzeitManagerInterface;
use App\Logic\Essen\Model\Mahlzeit;
use App\Logic\Essen\Model\MahlzeitStatus;
use App\Logic\Essen\Model\Wochentag;
use App\Logic\Essen\Query\MahlzeitInspirationQuery;
use PHPUnit\Framework\TestCase;
final class MahlzeitInspirationQueryTest extends TestCase
{
private \DateTimeImmutable $jetzt;
protected function setUp(): void
{
$this->jetzt = new \DateTimeImmutable('2026-09-18 00:00:00');
}
private function clock(): ClockInterface
{
$clock = $this->createStub(ClockInterface::class);
$clock->method('now')->willReturn($this->jetzt);
return $clock;
}
/** @param list<Mahlzeit> $mahlzeiten */
private function manager(array $mahlzeiten): MahlzeitManagerInterface
{
$manager = $this->createMock(MahlzeitManagerInterface::class);
$manager->expects(self::once())->method('list')->with(null)->willReturn($mahlzeiten);
return $manager;
}
private function gegessen(int $id, string $name, \DateTimeImmutable $gegessenAm): Mahlzeit
{
return new Mahlzeit($id, $name, '', [Wochentag::Mo], 1, MahlzeitStatus::Gegessen, $this->jetzt, $this->jetzt, $gegessenAm);
}
private function geplant(int $id, string $name): Mahlzeit
{
return new Mahlzeit($id, $name, '', [Wochentag::Mo], 1, MahlzeitStatus::Geplant, $this->jetzt, $this->jetzt, null);
}
public function testExecuteLeereListeLiefertKeineVorschlaege(): void
{
$query = new MahlzeitInspirationQuery($this->manager([]), $this->clock());
self::assertSame([], $query->execute());
}
public function testExecuteOhneArchivEintraegeKeineVorschlaege(): void
{
// Nur `geplant` – kein Gericht im Archiv.
$query = new MahlzeitInspirationQuery($this->manager([
$this->geplant(1, 'Bolognese'),
$this->geplant(2, 'Curry'),
]), $this->clock());
self::assertSame([], $query->execute());
}
public function testExecuteSortiertAbsteigendNachScoreUndWaehltRepraesentanten(): void
{
// "Bolognese": gegessen vor 10 Tagen (id 1) + 1 geplanter Eintrag → auftraete 2,
// repraesentant = id 1.
// "Curry": gegessen vor 3 Tagen (id 3), auftraete 1.
$query = new MahlzeitInspirationQuery($this->manager([
$this->gegessen(1, 'Bolognese', new \DateTimeImmutable('2026-09-08 00:00:00')),
$this->geplant(2, 'Bolognese'),
$this->gegessen(3, 'Curry', new \DateTimeImmutable('2026-09-15 00:00:00')),
]), $this->clock());
$vorschlaege = $query->execute();
self::assertCount(2, $vorschlaege);
self::assertSame(1, $vorschlaege[0]->id);
self::assertSame('Bolognese', $vorschlaege[0]->name);
self::assertSame(2, $vorschlaege[0]->auftraete);
self::assertSame(3, $vorschlaege[1]->id);
self::assertSame('Curry', $vorschlaege[1]->name);
self::assertSame(1, $vorschlaege[1]->auftraete);
// Staleness dominiert: das länger nicht gekochte Gericht hat den höheren Score.
self::assertGreaterThan($vorschlaege[1]->score, $vorschlaege[0]->score);
}
public function testExecuteWaehltNeuestenGegessenenEintragAlsRepraesentanten(): void
{
$query = new MahlzeitInspirationQuery($this->manager([
$this->gegessen(1, 'Bolognese', new \DateTimeImmutable('2026-09-01 00:00:00')),
$this->gegessen(4, 'Bolognese', new \DateTimeImmutable('2026-09-08 00:00:00')),
]), $this->clock());
$vorschlaege = $query->execute();
self::assertCount(1, $vorschlaege);
self::assertSame(4, $vorschlaege[0]->id);
self::assertEquals(new \DateTimeImmutable('2026-09-08 00:00:00'), $vorschlaege[0]->gegessenAm);
self::assertSame(2, $vorschlaege[0]->auftraete);
}
public function testExecuteAusschlussSpruengtBereitsVorgeschlageneGerichte(): void
{
$query = new MahlzeitInspirationQuery($this->manager([
$this->gegessen(1, 'Bolognese', new \DateTimeImmutable('2026-09-08 00:00:00')),
$this->gegessen(3, 'Curry', new \DateTimeImmutable('2026-09-15 00:00:00')),
]), $this->clock());
// "Weitere 5": Bolognese (id 1) wurde bereits vorgeschlagen.
$vorschlaege = $query->execute(5, [1]);
self::assertCount(1, $vorschlaege);
self::assertSame('Curry', $vorschlaege[0]->name);
}
public function testExecuteBemaessErgebnisAnLimit(): void
{
$query = new MahlzeitInspirationQuery($this->manager([
$this->gegessen(1, 'Bolognese', new \DateTimeImmutable('2026-09-08 00:00:00')),
$this->gegessen(2, 'Curry', new \DateTimeImmutable('2026-09-12 00:00:00')),
$this->gegessen(3, 'Pasta', new \DateTimeImmutable('2026-09-15 00:00:00')),
]), $this->clock());
$vorschlaege = $query->execute(2);
self::assertCount(2, $vorschlaege);
self::assertSame(['Bolognese', 'Curry'], array_map(static fn ($v) => $v->name, $vorschlaege));
}
public function testExecuteGleichstandEntscheidetAlphabetisch(): void
{
// Beide Gerichte: gleiche Pause, gleiche Auftritte → Score-Gleichstand.
$query = new MahlzeitInspirationQuery($this->manager([
$this->gegessen(1, 'Zwiebelsuppe', new \DateTimeImmutable('2026-09-13 00:00:00')),
$this->gegessen(2, 'Apfelkuchen', new \DateTimeImmutable('2026-09-13 00:00:00')),
]), $this->clock());
$vorschlaege = $query->execute();
self::assertSame(['Apfelkuchen', 'Zwiebelsuppe'], array_map(static fn ($v) => $v->name, $vorschlaege));
}
}