projectDir = static::getContainer()->getParameter('kernel.project_dir'); $this->createdDirs = []; $storageDir = $this->projectDir . '/var/storage/images'; if (!is_dir($storageDir)) { mkdir($storageDir, 0755, true); } $this->fixtureJpeg = $this->projectDir . '/var/storage/images/_render_fixture.jpg'; $imagick = new \Imagick(); $imagick->newImage(20, 20, new \ImagickPixel('white')); $imagick->setImageFormat('jpeg'); $imagick->writeImage($this->fixtureJpeg); $imagick->destroy(); } protected function tearDown(): void { foreach ($this->createdDirs as $dir) { if (is_dir($dir)) { $this->deleteDir($dir); } } if (file_exists($this->fixtureJpeg)) { unlink($this->fixtureJpeg); } parent::tearDown(); } private function deleteDir(string $dir): void { foreach (new \FilesystemIterator($dir) as $item) { $item->isDir() ? $this->deleteDir($item->getPathname()) : unlink($item->getPathname()); } rmdir($dir); } private function invokeHandler(int $imageId, string $model = 'v1', string $orientation = 'landscape'): void { $handler = static::getContainer()->get(RenderImageMessageHandler::class); $handler(new RenderImageMessage($imageId, $model, $orientation)); } // MH-01: happy path — bin written, RenderedAsset status = Ready public function test_mh01_renders_image_to_bin_and_marks_ready(): void { $user = $this->createUser('mh01@example.com'); $image = (new Image())->setUser($user) ->setOriginalFilename('test.jpg') ->setStoragePath('var/storage/images/_render_fixture.jpg'); $this->em()->persist($image); $this->em()->flush(); $imageId = $image->getId(); $imageDir = $this->projectDir . '/var/storage/images/' . $imageId; mkdir($imageDir, 0755, true); $this->createdDirs[] = $imageDir; $this->invokeHandler($imageId); /** @var RenderedAssetRepository $assetRepo */ $assetRepo = $this->em()->getRepository(\App\Entity\RenderedAsset::class); $asset = $assetRepo->findOneBy(['image' => $image]); $this->assertNotNull($asset); $this->assertSame(RenderStatus::Ready, $asset->getStatus()); $this->assertFileExists($this->projectDir . '/' . $asset->getFilePath()); } // MH-01b: composited.jpg is preferred over the raw original when present public function test_mh01b_composited_jpg_preferred_over_original(): void { $user = $this->createUser('mh01b@example.com'); $image = (new Image())->setUser($user) ->setOriginalFilename('test.jpg') ->setStoragePath('var/storage/images/_render_fixture.jpg'); $this->em()->persist($image); $this->em()->flush(); $imageId = $image->getId(); $imageDir = $this->projectDir . '/var/storage/images/' . $imageId; mkdir($imageDir, 0755, true); $this->createdDirs[] = $imageDir; copy($this->fixtureJpeg, $imageDir . '/composited.jpg'); $this->invokeHandler($imageId); $assetRepo = $this->em()->getRepository(\App\Entity\RenderedAsset::class); $asset = $assetRepo->findOneBy(['image' => $image]); $this->assertNotNull($asset); $this->assertSame(RenderStatus::Ready, $asset->getStatus()); } // MH-02: non-existent imageId → handler returns early, no RenderedAsset created public function test_mh02_nonexistent_image_returns_early(): void { $this->invokeHandler(999999999); $assetRepo = $this->em()->getRepository(\App\Entity\RenderedAsset::class); $assets = $assetRepo->findAll(); $this->assertCount(0, $assets); } // MH-03: corrupt/non-image file → RenderedAsset status = Failed public function test_mh03_imagick_failure_marks_asset_as_failed(): void { $badFile = $this->projectDir . '/var/storage/images/_render_bad.txt'; file_put_contents($badFile, 'not-an-image'); $user = $this->createUser('mh03@example.com'); $image = (new Image())->setUser($user) ->setOriginalFilename('bad.jpg') ->setStoragePath('var/storage/images/_render_bad.txt'); $this->em()->persist($image); $this->em()->flush(); $imageId = $image->getId(); $imageDir = $this->projectDir . '/var/storage/images/' . $imageId; mkdir($imageDir, 0755, true); $this->createdDirs[] = $imageDir; $this->invokeHandler($imageId); $assetRepo = $this->em()->getRepository(\App\Entity\RenderedAsset::class); $asset = $assetRepo->findOneBy(['image' => $image]); $this->assertNotNull($asset); $this->assertSame(RenderStatus::Failed, $asset->getStatus()); if (file_exists($badFile)) { unlink($badFile); } } }