createStub(TransactionManagerInterface::class); $tx->method('execute')->willReturnCallback(static fn (\Closure $work): mixed => $work()); return $tx; } private function clock(): ClockInterface { $clock = $this->createStub(ClockInterface::class); $clock->method('now')->willReturn(new \DateTimeImmutable('2026-09-18 12:00:00')); return $clock; } public function testExecuteKopiertQuelleAlsNeuenGeplantEintrag(): void { $jetzt = new \DateTimeImmutable('2026-09-18 12:00:00'); $quelle = new Mahlzeit(7, 'Bolognese', 'mit Parmesan', [Wochentag::Mo], 3, MahlzeitStatus::Gegessen, $jetzt, $jetzt, $jetzt); $manager = $this->createMock(MahlzeitManagerInterface::class); $manager->expects(self::once())->method('find')->with(7)->willReturn($quelle); $manager->expects(self::once())->method('create')->willReturnCallback(function (Mahlzeit $m) { // Neue, geplante Kopie: gleiche Inhalte, aber ohne gegessenAm und ohne ID. self::assertSame('Bolognese', $m->name); self::assertSame('mit Parmesan', $m->notiz); self::assertSame([Wochentag::Mo], $m->wochentage); self::assertSame(MahlzeitStatus::Geplant, $m->status); self::assertNull($m->gegessenAm); self::assertSame(0, $m->sortOrder); self::assertSame(0, $m->id); // Persistenz vergibt die neue ID. return $m->mitId(42); }); $events = $this->createMock(EventDispatcherInterface::class); $events->expects(self::once())->method('dispatch')->with( self::equalTo(new MahlzeitAdoptedEvent(id: 42, name: 'Bolognese', quellId: 7)), ); $useCase = new MahlzeitAdoptUseCase($this->tx(), $manager, new MahlzeitFactory(), $events, $this->clock()); $response = $useCase->execute(7); self::assertSame(42, $response->id); self::assertSame('Bolognese', $response->name); self::assertSame(MahlzeitStatus::Geplant, $response->status); self::assertNull($response->gegessenAm); } public function testExecuteUnbekannteQuelleWirft404UndLegtNichtsAn(): void { $manager = $this->createMock(MahlzeitManagerInterface::class); $manager->method('find')->willThrowException(new MahlzeitNotFoundException(99)); $manager->expects(self::never())->method('create'); $events = $this->createMock(EventDispatcherInterface::class); $events->expects(self::never())->method('dispatch'); $useCase = new MahlzeitAdoptUseCase($this->tx(), $manager, new MahlzeitFactory(), $events, $this->clock()); $this->expectException(MahlzeitNotFoundException::class); try { $useCase->execute(99); } finally { // Erwartete Exception oben. } } }