createStub(TransactionManagerInterface::class); $tx->method('execute')->willReturnCallback(static fn (\Closure $work): mixed => $work()); return $tx; } private function mahlzeit(int $id, int $sortOrder): Mahlzeit { $jetzt = new \DateTimeImmutable('2026-09-18 12:00:00'); return new Mahlzeit($id, 'Gericht' . $id, '', [], $sortOrder, MahlzeitStatus::Geplant, $jetzt, $jetzt, null); } public function testExecuteRenommertSortOrderUndDispatchtEvent(): void { $manager = $this->createMock(MahlzeitManagerInterface::class); $manager->method('find')->willReturnMap([ [2, $this->mahlzeit(2, 2)], [1, $this->mahlzeit(1, 1)], [3, $this->mahlzeit(3, 3)], ]); $gespeicherte = []; $manager->expects(self::exactly(2))->method('update')->willReturnCallback(function (Mahlzeit $m) use (&$gespeicherte) { $gespeicherte[$m->id] = $m->sortOrder; return $m; }); $events = $this->createMock(EventDispatcherInterface::class); $events->expects(self::once())->method('dispatch')->with( self::equalTo(new MahlzeitReorderedEvent([2, 1, 3])), ); $useCase = new MahlzeitReorderUseCase($this->tx(), $manager, $events); $useCase->execute(new MahlzeitReorderRequest([2, 1, 3])); // id 2 → Position 1, id 1 → Position 2; id 3 (Position 3) bleibt unverändert. self::assertSame([2 => 1, 1 => 2], $gespeicherte); } public function testExecuteSpruengtBereitsKorrektePosition(): void { $manager = $this->createMock(MahlzeitManagerInterface::class); $manager->method('find')->willReturnMap([ [1, $this->mahlzeit(1, 1)], [2, $this->mahlzeit(2, 2)], ]); $manager->expects(self::never())->method('update'); $events = $this->createMock(EventDispatcherInterface::class); $events->expects(self::once())->method('dispatch'); $useCase = new MahlzeitReorderUseCase($this->tx(), $manager, $events); $useCase->execute(new MahlzeitReorderRequest([1, 2])); } public function testExecuteLeereListeIstNoOp(): void { $manager = $this->createMock(MahlzeitManagerInterface::class); $manager->expects(self::never())->method('find'); $manager->expects(self::never())->method('update'); $events = $this->createMock(EventDispatcherInterface::class); $events->expects(self::never())->method('dispatch'); $useCase = new MahlzeitReorderUseCase($this->tx(), $manager, $events); $useCase->execute(new MahlzeitReorderRequest([])); } public function testExecuteUnbekannteIdWirft404(): void { $manager = $this->createStub(MahlzeitManagerInterface::class); $manager->method('find')->willThrowException(new MahlzeitNotFoundException(99)); $events = $this->createMock(EventDispatcherInterface::class); $events->expects(self::never())->method('dispatch'); $useCase = new MahlzeitReorderUseCase($this->tx(), $manager, $events); $this->expectException(MahlzeitNotFoundException::class); try { $useCase->execute(new MahlzeitReorderRequest([99])); } finally { // Erwartete Exception oben. } } }