mangaRepository = new InMemoryMangaRepository(); $this->chapterRepository = new InMemoryChapterRepository(); $this->pathManager = new InMemoryPathManager(); $this->handler = new ImportChapterHandler( $this->mangaRepository, $this->chapterRepository, $this->pathManager ); } public function test_it_throws_exception_when_chapter_not_found(): void { // Arrange $mangaId = 'manga-123'; $manga = new Manga( new MangaId($mangaId), new MangaTitle('One Piece'), new MangaSlug('one-piece'), 'Description', 'Eiichiro Oda', 1997, ['action', 'adventure'], 'ongoing' ); $this->mangaRepository->save($manga); $cbzBinary = $this->createValidCbzBinary(); $command = new ImportChapter( mangaId: $mangaId, chapterNumber: 1.5, fileBinary: $cbzBinary ); // Assert $this->expectException(ChapterNotFoundException::class); // Act $this->handler->handle($command); } public function test_it_updates_existing_chapter_with_new_cbz(): void { // Arrange $mangaId = 'manga-123'; $manga = new Manga( new MangaId($mangaId), new MangaTitle('One Piece'), new MangaSlug('one-piece'), 'Description', 'Eiichiro Oda', 1997, ['action', 'adventure'], 'ongoing' ); $this->mangaRepository->save($manga); // Create an existing chapter without CBZ $existingChapter = new Chapter( new ChapterId('chapter-123'), $mangaId, 1.5, 'Chapter 1.5', 1, true, null ); $this->chapterRepository->save($existingChapter); // Import the same chapter with CBZ $cbzBinary = $this->createValidCbzBinary(); $command = new ImportChapter( mangaId: $mangaId, chapterNumber: 1.5, fileBinary: $cbzBinary ); // Act $this->handler->handle($command); // Assert $chapters = $this->chapterRepository->getAll(); $this->assertCount(1, $chapters); // Still only one chapter $updatedChapter = $chapters[0]; $this->assertEquals('chapter-123', $updatedChapter->getId()); $this->assertEquals($mangaId, $updatedChapter->getMangaId()); $this->assertEquals(1.5, $updatedChapter->getNumber()); $this->assertEquals('Chapter 1.5', $updatedChapter->getTitle()); // Title preserved $this->assertEquals(1, $updatedChapter->getVolume()); // Volume preserved $this->assertTrue($updatedChapter->isVisible()); $this->assertTrue($updatedChapter->isAvailable()); // Now has CBZ $this->assertStringContainsString('_vol1_ch1.5.cbz', $updatedChapter->getCbzPath()); } public function test_it_throws_exception_when_manga_not_found(): void { // Arrange $cbzBinary = $this->createValidCbzBinary(); $command = new ImportChapter( mangaId: 'non-existent-manga', chapterNumber: 1.0, fileBinary: $cbzBinary ); // Assert $this->expectException(MangaNotFoundException::class); // Act $this->handler->handle($command); } public function test_it_throws_exception_when_file_is_not_valid_cbz(): void { // Arrange $mangaId = 'manga-123'; $manga = new Manga( new MangaId($mangaId), new MangaTitle('One Piece'), new MangaSlug('one-piece'), 'Description', 'Eiichiro Oda', 1997, ['action', 'adventure'], 'ongoing' ); $this->mangaRepository->save($manga); $invalidBinary = 'This is not a CBZ file'; $command = new ImportChapter( mangaId: $mangaId, chapterNumber: 1.0, fileBinary: $invalidBinary ); // Assert $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('The provided file is not a valid CBZ file'); // Act $this->handler->handle($command); } /** * Create a minimal valid CBZ (ZIP) binary for testing */ private function createValidCbzBinary(): string { $tmpFile = tempnam(sys_get_temp_dir(), 'cbz'); // Delete the empty file created by tempnam unlink($tmpFile); $zip = new \ZipArchive(); // Create a new ZIP archive (avoid opening empty file) if ($zip->open($tmpFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) { throw new \RuntimeException('Cannot create test CBZ file'); } // Add a dummy image file to the ZIP $zip->addFromString('image1.jpg', 'fake-image-data'); $zip->close(); $binaryContent = file_get_contents($tmpFile); unlink($tmpFile); return $binaryContent; } }