Passed
Push — master ( 9cc91e...8ec59b )
by Adrien
03:01
created

ImageHandlerTest   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 111
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 9
eloc 58
c 3
b 0
f 0
dl 0
loc 111
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A setUp() 0 10 1
A handle() 0 5 1
A createRepositoryMock() 0 6 1
A testWillErrorIfImageNotFoundOnDisk() 0 9 1
A createImageMock() 0 6 1
A assertError() 0 6 1
A testWillServeWebpIfAccepted() 0 22 1
A testWillServeJpgByDefault() 0 21 1
A testWillErrorIfImageNotFoundInDatabase() 0 8 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace EcodevTests\Felix\Handler;
6
7
use Doctrine\Persistence\ObjectRepository;
8
use Ecodev\Felix\Handler\ImageHandler;
9
use Ecodev\Felix\Model\Image;
10
use Ecodev\Felix\Service\ImageResizer;
11
use Laminas\Diactoros\ServerRequest;
12
use org\bovigo\vfs\vfsStream;
13
use PHPUnit\Framework\TestCase;
14
use Psr\Http\Message\ResponseInterface;
15
use Psr\Http\Message\ServerRequestInterface;
16
17
class ImageHandlerTest extends TestCase
18
{
19
    protected function setUp(): void
20
    {
21
        // Minimal binary headers to cheat mime detection
22
        $virtualFileSystem = [
23
            'image.png' => '',
24
            'image-100.jpg' => "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01\x01\x01\x00\x60",
25
            'image-100.webp' => 'RIFF4<..WEBPVP8',
26
        ];
27
28
        vfsStream::setup('felix', null, $virtualFileSystem);
29
    }
30
31
    public function testWillServeJpgByDefault(): void
32
    {
33
        $image = $this->createImageMock();
34
        $repository = $this->createRepositoryMock($image);
35
36
        $maxHeight = 100;
37
        $imageResizer = $this->createMock(ImageResizer::class);
38
        $imageResizer->expects(self::once())
39
            ->method('resize')
40
            ->with($image, $maxHeight, false)
41
            ->willReturn('vfs://felix/image-100.jpg');
42
43
        // A request without accept header
44
        $request = new ServerRequest();
45
        $request = $request->withAttribute('maxHeight', $maxHeight);
46
47
        $response = $this->handle($repository, $imageResizer, $request);
48
49
        self::assertSame('image/jpeg', $response->getHeaderLine('content-type'));
50
        self::assertSame('16', $response->getHeaderLine('content-length'));
51
        self::assertSame('max-age=21600', $response->getHeaderLine('cache-control'));
52
    }
53
54
    public function testWillServeWebpIfAccepted(): void
55
    {
56
        $image = $this->createImageMock();
57
        $repository = $this->createRepositoryMock($image);
58
59
        $maxHeight = 100;
60
        $imageResizer = $this->createMock(ImageResizer::class);
61
        $imageResizer->expects(self::once())
62
            ->method('resize')
63
            ->with($image, $maxHeight, true)
64
            ->willReturn('vfs://felix/image-100.webp');
65
66
        // A request specifically accepting webp images
67
        $request = new ServerRequest();
68
        $request = $request->withAttribute('maxHeight', $maxHeight)
69
            ->withHeader('accept', 'text/html, image/webp, */*;q=0.8');
70
71
        $response = $this->handle($repository, $imageResizer, $request);
72
73
        self::assertSame('image/webp', $response->getHeaderLine('content-type'));
74
        self::assertSame('15', $response->getHeaderLine('content-length'));
75
        self::assertSame('max-age=21600', $response->getHeaderLine('cache-control'));
76
    }
77
78
    public function testWillErrorIfImageNotFoundInDatabase(): void
79
    {
80
        $repository = $this->createRepositoryMock(null);
81
        $imageResizer = $this->createMock(ImageResizer::class);
82
        $request = new ServerRequest();
83
84
        $response = $this->handle($repository, $imageResizer, $request);
85
        $this->assertError(['error' => 'Image 0 not found in database'], $response);
86
    }
87
88
    public function testWillErrorIfImageNotFoundOnDisk(): void
89
    {
90
        $image = $this->createImageMock('vfs://felix/totally-non-existing-path');
91
        $repository = $this->createRepositoryMock($image);
92
        $imageResizer = $this->createMock(ImageResizer::class);
93
        $request = new ServerRequest();
94
95
        $response = $this->handle($repository, $imageResizer, $request);
96
        $this->assertError(['error' => 'Image for image 0 not found on disk, or not readable'], $response);
97
    }
98
99
    private function assertError(array $expected, ResponseInterface $response): void
100
    {
101
        self::assertSame('application/json', $response->getHeaderLine('content-type'));
102
        self::assertSame('', $response->getHeaderLine('content-length'));
103
        self::assertSame('', $response->getHeaderLine('cache-control'));
104
        self::assertSame($expected, json_decode($response->getBody()->getContents(), true));
105
    }
106
107
    private function handle(ObjectRepository $repository, ImageResizer $imageResizer, ServerRequestInterface $request): ResponseInterface
108
    {
109
        $handler = new ImageHandler($repository, $imageResizer);
110
111
        return $handler->handle($request);
112
    }
113
114
    private function createImageMock(string $path = 'vfs://felix/image.png'): Image
115
    {
116
        $image = $this->createMock(Image::class);
117
        $image->expects(self::once())->method('getPath')->willReturn($path);
118
119
        return $image;
120
    }
121
122
    private function createRepositoryMock(?Image $image): ObjectRepository
123
    {
124
        $repository = $this->createMock(ObjectRepository::class);
125
        $repository->expects(self::once())->method('find')->willReturn($image);
126
127
        return $repository;
128
    }
129
}
130