Passed
Push — master ( 9106cd...b3e6e6 )
by Alexander
02:14
created

exceptionWhenHeadersHaveBeenSent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 0
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
namespace Yiisoft\Yii\Web\Tests\Emitter;
3
4
include 'httpFunctionMocks.php';
5
6
use Nyholm\Psr7\Response;
7
use PHPUnit\Framework\TestCase;
8
use Psr\Http\Message\ResponseInterface;
9
use Psr\Http\Message\StreamInterface;
10
use Yiisoft\FriendlyException\FriendlyExceptionInterface;
11
use Yiisoft\Yii\Web\Emitter\EmitterInterface;
12
use Yiisoft\Yii\Web\Emitter\SapiEmitter;
13
use Yiisoft\Yii\Web\Exception\HeadersHaveBeenSentException;
14
15
/**
16
 * @runTestsInSeparateProcesses
17
 */
18
class SapiEmitterTest extends TestCase
19
{
20
    public function setUp(): void
21
    {
22
        HTTPFunctions::reset();
23
    }
24
25
    public static function tearDownAfterClass(): void
26
    {
27
        HTTPFunctions::reset();
28
    }
29
30
    public function noBodyHTTPCodeProvider(): array
31
    {
32
        return [[100], [101], [102], [204], [205], [304]];
33
    }
34
35
    public function testEmit(): void
36
    {
37
        $body = 'Example body';
38
        $response = $this->createResponse(200, ['X-Test' => 1], $body);
39
40
        $this->createEmitter()->emit($response);
41
42
        $this->assertEquals(200, $this->getResponseCode());
43
        $this->assertCount(2, $this->getHeaders());
44
        $this->assertContains('X-Test: 1', $this->getHeaders());
45
        $this->assertContains('Content-Length: ' . strlen($body), $this->getHeaders());
46
        $this->expectOutputString($body);
47
    }
48
49
    /**
50
     * @test
51
     * @dataProvider noBodyHTTPCodeProvider
52
     */
53
    public function shouldNotOutputBodyByResponseCode(int $code): void
54
    {
55
        $response = $this->createResponse($code, ['X-Test' => 1], 'Example body');
56
57
        $this->createEmitter()->emit($response);
58
59
        $this->assertEquals($code, $this->getResponseCode());
60
        $this->assertTrue(HTTPFunctions::hasHeader('X-Test'));
61
        $this->assertFalse(HTTPFunctions::hasHeader('Content-Length'));
62
        $this->expectOutputString('');
63
    }
64
65
    /**
66
     * @test
67
     */
68
    public function shouldNotOutputBodyAndContentLengthIfEmitToldSo(): void
69
    {
70
        $response = $this->createResponse(200, ['X-Test' => 1], 'Example body');
71
72
        $this->createEmitter()->emit($response, true);
73
74
        $this->assertEquals(200, $this->getResponseCode());
75
        $this->assertTrue(HTTPFunctions::hasHeader('X-Test'));
76
        $this->assertFalse(HTTPFunctions::hasHeader('Content-Length'));
77
        $this->expectOutputString('');
78
    }
79
80
    /**
81
     * @test
82
     */
83
    public function contentLengthShouldNotBeOverwrittenIfPresent(): void
84
    {
85
        $length = 100;
86
        $response = $this->createResponse(200, ['Content-Length' => $length, 'X-Test' => 1], 'Example body');
87
88
        $this->createEmitter()->emit($response);
89
90
        $this->assertEquals(200, $this->getResponseCode());
91
        $this->assertCount(2, $this->getHeaders());
92
        $this->assertContains('X-Test: 1', $this->getHeaders());
93
        $this->assertContains('Content-Length: ' . $length, $this->getHeaders());
94
        $this->expectOutputString('Example body');
95
    }
96
97
    /**
98
     * @test
99
     */
100
    public function contentLengthShouldNotOutputWhenBodyIsEmpty(): void
101
    {
102
        $length = 100;
103
        $response = $this->createResponse(200, ['Content-Length' => $length, 'X-Test' => 1], '');
104
105
        $this->createEmitter()->emit($response);
106
107
        $this->assertEquals(200, $this->getResponseCode());
108
        $this->assertEquals(['X-Test: 1'], $this->getHeaders());
109
        $this->expectOutputString('');
110
    }
111
112
    /**
113
     * @test
114
     */
115
    public function contentAlwaysShouldBeFullyEmitted(): void
116
    {
117
        $body = 'Example body';
118
        $response = $this->createResponse(200, ['Content-length' => 1, 'X-Test' => 1], $body);
119
120
        $this->createEmitter()->emit($response);
121
122
        $this->expectOutputString($body);
123
    }
124
125
    /**
126
     * @test
127
     */
128
    public function sentHeadersShouldBeRemoved(): void
129
    {
130
        HTTPFunctions::header('Cookie-Set: First Cookie');
131
        HTTPFunctions::header('X-Test: 1');
132
        $body = 'Example body';
133
        $response = $this->createResponse(200, [], $body);
134
135
        $this->createEmitter()->emit($response);
136
137
        $this->assertEquals(['Content-Length: ' . strlen($body)], $this->getHeaders());
138
        $this->expectOutputString($body);
139
    }
140
141
    /**
142
     * @test
143
     */
144
    public function exceptionWhenHeadersHaveBeenSent(): void
145
    {
146
        $body = 'Example body';
147
        $response = $this->createResponse(200, [], $body);
148
        HTTPFunctions::set_headers_sent(true, 'test-file.php', 200);
149
150
        $this->expectException(HeadersHaveBeenSentException::class);
151
        $this->createEmitter()->emit($response);
152
    }
153
154
    /**
155
     * @test
156
     */
157
    public function shouldEmitDuplicateHeaders(): void
158
    {
159
        $body = 'Example body';
160
        $response = $this->createResponse(200, [], $body)
161
                         ->withHeader('X-Test', '1')
162
                         ->withAddedHeader('X-Test', '2')
163
                         ->withAddedHeader('X-Test', '3; 3.5')
164
                         ->withHeader('Cookie-Set', '1')
165
                         ->withAddedHeader('cookie-Set', '2')
166
                         ->withAddedHeader('Cookie-set', '3');
167
168
        $this->createEmitter()->emit($response);
169
        $this->assertEquals(200, $this->getResponseCode());
170
        $this->assertContains('X-Test: 1', $this->getHeaders());
171
        $this->assertContains('X-Test: 2', $this->getHeaders());
172
        $this->assertContains('X-Test: 3; 3.5', $this->getHeaders());
173
        $this->assertContains('Cookie-Set: 1', $this->getHeaders());
174
        $this->assertContains('Cookie-Set: 2', $this->getHeaders());
175
        $this->assertContains('Cookie-Set: 3', $this->getHeaders());
176
        $this->assertContains('Content-Length: ' . strlen($body), $this->getHeaders());
177
        $this->expectOutputString($body);
178
    }
179
180
    private function createEmitter(?int $bufferSize = null): EmitterInterface
181
    {
182
        return new SapiEmitter($bufferSize);
183
    }
184
185
    private function createResponse(
186
        int $status = 200,
187
        array $headers = [],
188
        $body = null,
189
        string $version = '1.1'
190
    ): ResponseInterface {
191
        $response = (new Response())
192
            ->withStatus($status)
193
            ->withProtocolVersion($version);
194
        foreach ($headers as $header => $value) {
195
            $response = $response->withHeader($header, $value);
196
        }
197
        if ($body instanceof StreamInterface) {
198
            $response = $response->withBody($body);
199
        } elseif (is_string($body)) {
200
            $response->getBody()->write($body);
201
        }
202
        return $response;
203
    }
204
205
    private function getHeaders(): array
206
    {
207
        return HTTPFunctions::headers_list();
208
    }
209
210
    private function getResponseCode(): int
211
    {
212
        return HTTPFunctions::http_response_code();
213
    }
214
}
215