<?php
|
declare(strict_types=1);
|
|
namespace App\Tests\Functional;
|
|
use App\Entity\Book;
|
use App\Entity\OustedBook;
|
use App\Service\CoverStorage;
|
use App\Tests\DatabaseTestCase;
|
use App\Tests\LegacyDatabase;
|
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
|
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
use Symfony\Component\Console\Tester\CommandTester;
|
use Symfony\Component\Filesystem\Filesystem;
|
|
#[RequiresPhpExtension('gd')]
|
final class ImportLegacyCoversTest extends DatabaseTestCase {
|
use LegacyDatabase;
|
|
private const string ISBN_A = '9788373843455';
|
private const string ISBN_B = '9788303001795';
|
private const string ISBN_C = '9780306406157';
|
|
private string $coversDir;
|
|
protected function setUp(): void {
|
parent::setUp();
|
|
// The table import runs first and must find nothing to add.
|
$this->createLegacyTables();
|
$this->coversDir = sys_get_temp_dir() . '/legacy-covers-test-' . bin2hex(random_bytes(4));
|
$filesystem = new Filesystem();
|
$filesystem->mkdir($this->coversDir . '/own');
|
$filesystem->remove(static::getContainer()->getParameter('kernel.project_dir') . '/var/test/covers');
|
}
|
|
protected function tearDown(): void {
|
new Filesystem()->remove($this->coversDir);
|
|
parent::tearDown();
|
}
|
|
public function testCoversArePickedByPreference(): void {
|
$this->createBookWithIsbn(1, self::ISBN_A);
|
$this->createBookWithIsbn(2, self::ISBN_B);
|
$this->createBookWithIsbn(3, self::ISBN_C);
|
$this->createBook(4);
|
|
$generic = $this->createImage(self::ISBN_A);
|
$this->createImage(self::ISBN_B);
|
$this->createImage(self::ISBN_C);
|
$ownByIsbn = $this->createImage('own/' . self::ISBN_B);
|
$this->createImage('own/' . self::ISBN_C);
|
$ownById = $this->createImage('own/00000003');
|
$this->createImage('own/00000004');
|
|
$tester = $this->runImport(['--covers' => $this->coversDir]);
|
|
$tester->assertCommandIsSuccessful();
|
self::assertStringContainsString('4 covers imported', $tester->getDisplay());
|
self::assertFileEquals($generic, $this->originalOf(1));
|
self::assertFileEquals($ownByIsbn, $this->originalOf(2));
|
self::assertFileEquals($ownById, $this->originalOf(3));
|
self::assertNotNull($this->findBook(4)?->getCover());
|
self::assertFileExists($generic, 'The source files stay in place.');
|
}
|
|
public function testOustedBooksGetCoversToo(): void {
|
$book = new OustedBook('Zniszczona')->setId(7)->setTitle('T')->setAuthor('A')->setIsbn(self::ISBN_A);
|
$this->entityManager()->persist($book);
|
$this->entityManager()->flush();
|
$this->createImage(self::ISBN_A);
|
|
$this->runImport(['--covers' => $this->coversDir])->assertCommandIsSuccessful();
|
|
$this->entityManager()->clear();
|
self::assertNotNull($this->entityManager()->find(OustedBook::class, 7)?->getCover());
|
}
|
|
public function testUnreadableFilesGiveWayAndOddFilesAreReported(): void {
|
$this->createBookWithIsbn(1, self::ISBN_A);
|
$generic = $this->createImage(self::ISBN_A);
|
$this->createImage(self::ISBN_B);
|
$this->createImage('own/00000099');
|
file_put_contents($this->coversDir . '/notes.txt', 'x');
|
file_put_contents($this->coversDir . '/own/00000001', 'not an image');
|
|
$tester = $this->runImport(['--covers' => $this->coversDir]);
|
|
$tester->assertCommandIsSuccessful();
|
self::assertFileEquals($generic, $this->originalOf(1));
|
$display = preg_replace('/\s+/', ' ', $tester->getDisplay());
|
self::assertStringContainsString('1 files could not be imported', $display);
|
self::assertStringContainsString('own/00000001: Unsupported image type.', $display);
|
self::assertStringContainsString('2 files match no book', $display);
|
self::assertStringContainsString(self::ISBN_B, $display);
|
self::assertStringContainsString('own/00000099', $display);
|
self::assertStringContainsString('1 files are named neither', $display);
|
self::assertStringContainsString('notes.txt', $display);
|
}
|
|
public function testBooksWithACoverKeepIt(): void {
|
$this->createBookWithIsbn(1, self::ISBN_A);
|
$this->createImage(self::ISBN_A);
|
$this->runImport(['--covers' => $this->coversDir])->assertCommandIsSuccessful();
|
$cover = $this->findBook(1)?->getCover();
|
|
$this->createImage('own/00000001');
|
$tester = $this->runImport(['--covers' => $this->coversDir]);
|
|
$tester->assertCommandIsSuccessful();
|
self::assertSame($cover, $this->findBook(1)?->getCover());
|
self::assertStringContainsString('0 covers imported', $tester->getDisplay());
|
}
|
|
public function testDryRunStoresNothing(): void {
|
$this->createBookWithIsbn(1, self::ISBN_A);
|
$this->createImage(self::ISBN_A);
|
|
$tester = $this->runImport(['--covers' => $this->coversDir, '--dry-run' => true]);
|
|
$tester->assertCommandIsSuccessful();
|
self::assertNull($this->findBook(1)?->getCover());
|
$originals = static::getContainer()->getParameter('app.cover_originals_dir');
|
self::assertSame([], is_dir($originals) ? array_diff(scandir($originals), ['.', '..']) : []);
|
}
|
|
public function testMissingDirectoryFails(): void {
|
$this->expectException(\InvalidArgumentException::class);
|
$this->expectExceptionMessage('is not a readable directory');
|
|
$this->runImport(['--covers' => $this->coversDir . '/missing']);
|
}
|
|
private function createBookWithIsbn(int $id, string $isbn): Book {
|
$book = $this->createBook($id)->setIsbn($isbn);
|
$this->entityManager()->flush();
|
|
return $book;
|
}
|
|
private function createImage(string $name): string {
|
$path = $this->coversDir . '/' . $name;
|
$image = imagecreatetruecolor(20, 30);
|
// Random pixels make each generated file unique.
|
imagesetpixel($image, 0, 0, random_int(1, 0xFFFFFF));
|
imagepng($image, $path);
|
|
return $path;
|
}
|
|
private function originalOf(int $id): string {
|
$cover = $this->findBook($id)?->getCover();
|
self::assertNotNull($cover);
|
|
return static::getContainer()->get(CoverStorage::class)->originalPath($cover);
|
}
|
|
/**
|
* @param array<string, mixed> $input
|
*/
|
private function runImport(array $input): CommandTester {
|
$application = new Application(static::$kernel);
|
$tester = new CommandTester($application->find('app:import-legacy'));
|
$tester->execute($input);
|
|
return $tester;
|
}
|
}
|