Jacek Kowalski
4 hours ago 6b9278a5d57091fce636dbb640b380cfb990db6c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
<?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();
    }
}