<?php
|
declare(strict_types=1);
|
|
namespace App\Tests\Functional;
|
|
use App\Service\CoverStorage;
|
use App\Tests\DatabaseTestCase;
|
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
|
use Symfony\Component\Filesystem\Filesystem;
|
use Symfony\Component\HttpClient\MockHttpClient;
|
use Symfony\Component\HttpClient\Response\MockResponse;
|
|
#[RequiresPhpExtension('gd')]
|
final class LibraryThingCoverTest extends DatabaseTestCase {
|
private const string ISBN = '9788373843455';
|
|
/** @var list<string> */
|
private array $requestedUrls = [];
|
|
/** @var array{mixed, mixed} the key in $_ENV and $_SERVER before the test */
|
private array $previousKey;
|
|
protected function setUp(): void {
|
parent::setUp();
|
|
$this->previousKey = [$_ENV['LIBRARYTHING_API_KEY'] ?? null, $_SERVER['LIBRARYTHING_API_KEY'] ?? null];
|
new Filesystem()->remove(static::getContainer()->getParameter('kernel.project_dir') . '/var/test/covers');
|
}
|
|
protected function tearDown(): void {
|
[$_ENV['LIBRARYTHING_API_KEY'], $_SERVER['LIBRARYTHING_API_KEY']] = $this->previousKey;
|
|
parent::tearDown();
|
}
|
|
public function testNewBookGetsTheLibraryThingCover(): void {
|
$image = self::png(120, 180);
|
$this->mockLibraryThing('secret-key', new MockResponse($image, ['response_headers' => ['content-type' => 'image/png']]));
|
|
$this->addBook(self::ISBN);
|
|
self::assertSame(['https://covers.librarything.com/devkey/secret-key/large/isbn/' . self::ISBN], $this->requestedUrls);
|
$cover = $this->findBook(5)?->getCover();
|
self::assertNotNull($cover);
|
self::assertStringEqualsFile(static::getContainer()->get(CoverStorage::class)->originalPath($cover), $image);
|
$this->client->followRedirect();
|
self::assertAnySelectorTextContains('.flash', 'downloaded from LibraryThing');
|
}
|
|
public function testPlaceholderMeansNoCover(): void {
|
$this->mockLibraryThing('secret-key', new MockResponse(self::png(1, 1)));
|
|
$this->addBook(self::ISBN);
|
|
self::assertCount(1, $this->requestedUrls);
|
self::assertNull($this->findBook(5)?->getCover());
|
}
|
|
public function testFailedDownloadStillSavesTheBook(): void {
|
$this->mockLibraryThing('secret-key', new MockResponse('', ['http_code' => 503]));
|
|
$this->addBook(self::ISBN);
|
|
self::assertResponseRedirects('/book/5');
|
self::assertNull($this->findBook(5)?->getCover());
|
}
|
|
public function testNothingIsRequestedWithoutAKey(): void {
|
$this->mockLibraryThing('', new MockResponse(self::png(120, 180)));
|
|
$this->addBook(self::ISBN);
|
|
self::assertSame([], $this->requestedUrls);
|
self::assertNull($this->findBook(5)?->getCover());
|
}
|
|
public function testNothingIsRequestedWithoutAnIsbn(): void {
|
$this->mockLibraryThing('secret-key', new MockResponse(self::png(120, 180)));
|
|
$this->addBook('');
|
|
self::assertSame([], $this->requestedUrls);
|
}
|
|
private function mockLibraryThing(string $key, MockResponse $response): void {
|
$_ENV['LIBRARYTHING_API_KEY'] = $_SERVER['LIBRARYTHING_API_KEY'] = $key;
|
// A reboot between the form and its submission would drop the mock.
|
$this->client->disableReboot();
|
|
static::getContainer()->set('librarything.client', new MockHttpClient(
|
function (string $method, string $url) use ($response): MockResponse {
|
$this->requestedUrls[] = $url;
|
|
return $response;
|
},
|
'https://covers.librarything.com',
|
));
|
}
|
|
private function addBook(string $isbn): void {
|
$crawler = $this->client->request('GET', '/add/form?id=5');
|
$form = $crawler->selectButton('Save')->form();
|
$form['book[title]'] = 'Title';
|
$form['book[author]'] = 'Author';
|
$form['book[isbn]'] = $isbn;
|
$this->client->submit($form);
|
}
|
|
private static function png(int $width, int $height): string {
|
$image = imagecreatetruecolor($width, $height);
|
ob_start();
|
imagepng($image);
|
|
return (string)ob_get_clean();
|
}
|
}
|