Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
86 / 86
n/a
0 / 0
CRAP
n/a
0 / 0
1<?php
2
3// ╔════════════════════════════════════════════════════════════╗
4// ║ MIT Licence (#Expat) - https://opensource.org/licenses/MIT ║
5// ║ Copyright 2026 Frederic Poeydomenge <dyno@phexium.com>     ║
6// ╚════════════════════════════════════════════════════════════╝
7
8declare(strict_types=1);
9
10pest()->group('unit');
11
12use Nyholm\Psr7\Factory\Psr17Factory;
13use Nyholm\Psr7\Response;
14use Phexium\Presentation\ResponseBuilder;
15
16describe('Response creation', function (): void {
17    it('starts from the response given to withResponse()', function (): void {
18        $builder = new ResponseBuilder(new Psr17Factory());
19
20        $result = $builder
21            ->withResponse(new Response(404, ['X-Custom' => 'toto'], 'text body'))
22            ->build()
23        ;
24
25        expect($result->getStatusCode())->toBe(404);
26        expect($result->getHeaderLine('X-Custom'))->toBe('toto');
27        expect((string) $result->getBody())->toBe('text body');
28    });
29
30    it('builds HTML response with default charset', function (): void {
31        $builder = new ResponseBuilder(new Psr17Factory());
32
33        $result = $builder
34            ->withHtmlBody('<h1>Hello World</h1>')
35            ->build()
36        ;
37
38        expect($result->getStatusCode())->toBe(200);
39        expect($result->getHeaderLine('Content-Type'))->toBe('text/html; charset=utf-8');
40        expect((string) $result->getBody())->toBe('<h1>Hello World</h1>');
41    });
42
43    it('builds JSON response with default charset', function (): void {
44        $builder = new ResponseBuilder(new Psr17Factory());
45
46        $result = $builder
47            ->withJsonBody(['message' => 'Hello World'])
48            ->build()
49        ;
50
51        expect($result->getStatusCode())->toBe(200);
52        expect($result->getHeaderLine('Content-Type'))->toBe('application/json; charset=utf-8');
53        expect((string) $result->getBody())->json()->toBe(['message' => 'Hello World']);
54    });
55
56    it('builds raw response with the given content type', function (): void {
57        $builder = new ResponseBuilder(new Psr17Factory());
58
59        $result = $builder
60            ->withRawBody('fake-jpeg-bytes', 'image/jpeg')
61            ->build()
62        ;
63
64        expect($result->getHeaderLine('Content-Type'))->toBe('image/jpeg');
65        expect((string) $result->getBody())->toBe('fake-jpeg-bytes');
66    });
67
68    it('sets custom status code', function (): void {
69        $builder = new ResponseBuilder(new Psr17Factory());
70
71        $result = $builder->withStatus(404)->build();
72
73        expect($result->getStatusCode())->toBe(404);
74    });
75
76    it('replaces a body already present on the response', function (): void {
77        $builder = new ResponseBuilder(new Psr17Factory());
78
79        $result = $builder
80            ->withResponse(new Response(200, [], 'previous body'))
81            ->withHtmlBody('new body')
82            ->build()
83        ;
84
85        expect((string) $result->getBody())->toBe('new body');
86    });
87});
88
89describe('Headers', function (): void {
90    it('replaces existing header with withHeader()', function (): void {
91        $builder = new ResponseBuilder(new Psr17Factory());
92
93        $result = $builder
94            ->withResponse(new Response(200, ['X-Custom' => 'value1']))
95            ->withHeader('X-Custom', 'value2')
96            ->build()
97        ;
98
99        expect($result->getHeaderLine('X-Custom'))->toBe('value2');
100    });
101
102    it('appends header value with addHeader()', function (): void {
103        $builder = new ResponseBuilder(new Psr17Factory());
104
105        $result = $builder
106            ->withResponse(new Response(200, ['X-Custom' => 'value1']))
107            ->addHeader('X-Custom', 'value2')
108            ->build()
109        ;
110
111        expect($result->getHeader('X-Custom'))->toBe(['value1', 'value2']);
112    });
113});
114
115describe('Immutability', function (): void {
116    it('leaves the original builder untouched', function (): void {
117        $builder = new ResponseBuilder(new Psr17Factory());
118        $builder->withStatus(302)->withHeader('Location', '/books')->withJsonBody(['posts' => []]);
119
120        $result = $builder->build();
121
122        expect([$result->getStatusCode(), $result->getHeaders(), (string) $result->getBody()])
123            ->toBe([200, [], ''])
124        ;
125    });
126});