Passed
Pull Request — master (#602)
by Aleksei
07:06
created

SapiEmitterTest::createResponse()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 10
c 1
b 0
f 0
nc 6
nop 4
dl 0
loc 18
rs 9.9332
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spiral\Tests\Http\SapiEmitter;
6
7
include 'Support/httpFunctionMocks.php';
8
9
use Nyholm\Psr7\Response;
10
use PHPUnit\Framework\TestCase;
11
use Psr\Http\Message\ResponseInterface;
12
use Psr\Http\Message\StreamInterface;
13
use Spiral\Http\Exception\EmitterException;
14
use Spiral\Http\Emitter\SapiEmitter;
15
use Spiral\Tests\Http\SapiEmitter\Support\HTTPFunctions;
16
use Spiral\Tests\Http\SapiEmitter\Support\NotReadableStream;
17
18
/**
19
 * @runInSeparateProcess
20
 */
21
final class SapiEmitterTest extends TestCase
22
{
23
    public function setUp(): void
24
    {
25
        HTTPFunctions::reset();
26
    }
27
28
    public static function tearDownAfterClass(): void
29
    {
30
        HTTPFunctions::reset();
31
    }
32
33
    public function testEmit(): void
34
    {
35
        $body = 'Example body';
36
        $response = $this->createResponse(200, ['X-Test' => 1], $body);
37
38
        $this->createEmitter()->emit($response);
39
40
        $this->assertEquals(200, $this->getResponseCode());
41
        $this->assertContains('X-Test: 1', $this->getHeaders());
42
        // $this->assertContains('Content-Length: ' . strlen($body), $this->getHeaders());
43
        $this->expectOutputString($body);
44
    }
45
46
    public function testEmitterWithNotReadableStream(): void
47
    {
48
        $body = new NotReadableStream();
49
        $response = $this->createResponse(200, ['X-Test' => 42], $body);
50
51
        $this->createEmitter()->emit($response);
52
53
        $this->assertEquals(200, $this->getResponseCode());
54
        $this->assertCount(1, $this->getHeaders());
55
        $this->assertContains('X-Test: 42', $this->getHeaders());
56
    }
57
58
    public function testContentLengthNotOverwrittenIfPresent(): void
59
    {
60
        $length = 100;
61
        $response = $this->createResponse(200, ['Content-Length' => $length, 'X-Test' => 1], 'Example body');
62
63
        $this->createEmitter()->emit($response);
64
65
        $this->assertEquals(200, $this->getResponseCode());
66
        $this->assertCount(2, $this->getHeaders());
67
        $this->assertContains('X-Test: 1', $this->getHeaders());
68
        $this->assertContains('Content-Length: ' . $length, $this->getHeaders());
69
        $this->expectOutputString('Example body');
70
    }
71
72
    public function testContentFullyEmitted(): void
73
    {
74
        $body = 'Example body';
75
        $response = $this->createResponse(200, ['Content-length' => 1, 'X-Test' => 1], $body);
76
77
        $this->createEmitter()->emit($response);
78
79
        $this->expectOutputString($body);
80
    }
81
82
    public function testSentHeadersRemoved(): void
83
    {
84
        HTTPFunctions::header('Cookie-Set: First Cookie');
85
        HTTPFunctions::header('X-Test: 1');
86
        $body = 'Example body';
87
        $response = $this->createResponse(200, [], $body);
88
89
        $this->createEmitter()->emit($response);
90
91
        // $this->assertEquals(['Content-Length: ' . strlen($body)], $this->getHeaders());
92
        $this->assertEquals([
93
            'Cookie-Set: First Cookie',
94
            'X-Test: 1',
95
            // 'Content-Length: ' . strlen($body)
96
        ], $this->getHeaders());
97
        $this->expectOutputString($body);
98
    }
99
100
    public function testExceptionWhenHeadersHaveBeenSent(): void
101
    {
102
        $body = 'Example body';
103
        $response = $this->createResponse(200, [], $body);
104
        HTTPFunctions::set_headers_sent(true, 'test-file.php', 200);
105
106
        $this->expectException(EmitterException::class);
107
        $this->expectExceptionMessage('Unable to emit response, headers already send.');
108
109
        $this->createEmitter()->emit($response);
110
    }
111
112
    public function testExceptionBufferWithData(): void
113
    {
114
        ob_start();
115
        $body = 'Example body';
116
        $response = $this->createResponse(200, [], $body);
117
118
        $this->expectException(EmitterException::class);
119
        $this->expectExceptionMessage('Unable to emit response, found non closed buffered output.');
120
121
        try {
122
            echo 'some data';
123
            $this->createEmitter()->emit($response);
124
        } catch (\Throwable $e) {
125
            throw $e;
126
        } finally {
127
            \ob_end_clean();
128
        }
129
    }
130
131
    public function testEmitDuplicateHeaders(): void
132
    {
133
        $body = 'Example body';
134
        $response = $this->createResponse(200, [], $body)
135
                         ->withHeader('X-Test', '1')
136
                         ->withAddedHeader('X-Test', '2')
137
                         ->withAddedHeader('X-Test', '3; 3.5')
138
                         ->withHeader('Cookie-Set', '1')
139
                         ->withAddedHeader('cookie-Set', '2')
140
                         ->withAddedHeader('Cookie-set', '3');
141
142
        (new SapiEmitter())->emit($response);
143
        $this->assertEquals(200, $this->getResponseCode());
144
        $this->assertContains('X-Test: 1', $this->getHeaders());
145
        $this->assertContains('X-Test: 2', $this->getHeaders());
146
        $this->assertContains('X-Test: 3; 3.5', $this->getHeaders());
147
        $this->assertContains('Cookie-Set: 1', $this->getHeaders());
148
        $this->assertContains('Cookie-Set: 2', $this->getHeaders());
149
        $this->assertContains('Cookie-Set: 3', $this->getHeaders());
150
        // $this->assertContains('Content-Length: ' . strlen($body), $this->getHeaders());
151
        $this->expectOutputString($body);
152
    }
153
154
    private function createEmitter(?int $bufferSize = null): SapiEmitter
155
    {
156
        $emitter = new SapiEmitter();
157
        if ($bufferSize !== null) {
158
            $emitter->bufferSize = $bufferSize;
159
        }
160
        return $emitter;
161
    }
162
163
    private function createResponse(
164
        int $status = 200,
165
        array $headers = [],
166
        $body = null,
167
        string $version = '1.1'
168
    ): ResponseInterface {
169
        $response = (new Response())
170
            ->withStatus($status)
171
            ->withProtocolVersion($version);
172
        foreach ($headers as $header => $value) {
173
            $response = $response->withHeader($header, $value);
174
        }
175
        if ($body instanceof StreamInterface) {
176
            $response = $response->withBody($body);
177
        } elseif (is_string($body)) {
178
            $response->getBody()->write($body);
179
        }
180
        return $response;
181
    }
182
183
    private function getHeaders(): array
184
    {
185
        return HTTPFunctions::headers_list();
186
    }
187
188
    private function getResponseCode(): int
189
    {
190
        return HTTPFunctions::http_response_code();
191
    }
192
}
193