scraper = new InMemoryScraperAdapter(); $this->repository = new InMemoryScrapingJobRepository(); $this->eventBus = new InMemoryEventBus(); $this->handler = new ScrapeChapterHandler( $this->scraper, $this->repository, $this->eventBus ); } public function testHandleSuccessfully(): void { $command = new ScrapeChapter( chapterId: 2, sourceId: 3, mangaId: 1 ); $this->handler->handle($command); $scrapingJobs = $this->scraper->getJobs(); $this->assertCount(1, $scrapingJobs); $job = $scrapingJobs[0]; $savedJobs = $this->repository->getJobs(); $this->assertCount(1, $savedJobs); $this->assertSame($job, $savedJobs[0]); $dispatchedMessages = $this->eventBus->getDispatchedMessages(); $this->assertCount(1, $dispatchedMessages); $this->assertInstanceOf(ChapterScrapingStarted::class, $dispatchedMessages[0]); $this->assertEquals($job->getId(), $dispatchedMessages[0]->getJobId()); } public function testHandleThrowsException(): void { $command = new ScrapeChapter( chapterId: 2, sourceId: 3, mangaId: 1 ); $exception = new \Exception('Scraping failed'); $this->scraper->simulateError($exception); $this->expectException(\Exception::class); $this->expectExceptionMessage('Scraping failed'); try { $this->handler->handle($command); } finally { $dispatchedMessages = $this->eventBus->getDispatchedMessages(); $this->assertCount(1, $dispatchedMessages); $this->assertInstanceOf(ChapterScrapingFailed::class, $dispatchedMessages[0]); $this->assertEquals(2, $dispatchedMessages[0]->getChapterId()); $this->assertEquals('Scraping failed', $dispatchedMessages[0]->getReason()); } } }