Passed
Pull Request — master (#257)
by Wilmer
23:33 queued 08:42
created

SapiEmitterTest::testEmitDuplicateHeaders()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 18
nc 1
nop 0
dl 0
loc 21
rs 9.6666
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Web\Tests\Emitter;
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 Yiisoft\Yii\Web\Exception\HeadersHaveBeenSentException;
14
use Yiisoft\Yii\Web\SapiEmitter;
15
use Yiisoft\Yii\Web\Tests\Emitter\Support\HTTPFunctions;
16
use Yiisoft\Yii\Web\Tests\Emitter\Support\NotReadableStream;
17
18
final 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 noBodyResponseCodeProvider(): 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
     * @dataProvider noBodyResponseCodeProvider
51
     */
52
    public function testNoBodyForResponseCode(int $code): void
53
    {
54
        $response = $this->createResponse($code, ['X-Test' => 1], 'Example body');
55
56
        $this->createEmitter()->emit($response);
57
58
        $this->assertEquals($code, $this->getResponseCode());
59
        $this->assertTrue(HTTPFunctions::hasHeader('X-Test'));
60
        $this->assertFalse(HTTPFunctions::hasHeader('Content-Length'));
61
        $this->expectOutputString('');
62
    }
63
64
    public function testEmitterWithNotReadableStream(): void
65
    {
66
        $body = new NotReadableStream();
67
        $response = $this->createResponse(200, ['X-Test' => 42], $body);
68
69
        $this->createEmitter()->emit($response);
70
71
        $this->assertEquals(200, $this->getResponseCode());
72
        $this->assertCount(1, $this->getHeaders());
73
        $this->assertContains('X-Test: 42', $this->getHeaders());
74
    }
75
76
    public function testNoBodyAndContentLengthIfEmitToldSo(): void
77
    {
78
        $response = $this->createResponse(200, ['X-Test' => 1], 'Example body');
79
80
        $this->createEmitter()->emit($response, true);
81
82
        $this->assertEquals(200, $this->getResponseCode());
83
        $this->assertTrue(HTTPFunctions::hasHeader('X-Test'));
84
        $this->assertFalse(HTTPFunctions::hasHeader('Content-Length'));
85
        $this->expectOutputString('');
86
    }
87
88
    public function testContentLengthNotOverwrittenIfPresent(): void
89
    {
90
        $length = 100;
91
        $response = $this->createResponse(200, ['Content-Length' => $length, 'X-Test' => 1], 'Example body');
92
93
        $this->createEmitter()->emit($response);
94
95
        $this->assertEquals(200, $this->getResponseCode());
96
        $this->assertCount(2, $this->getHeaders());
97
        $this->assertContains('X-Test: 1', $this->getHeaders());
98
        $this->assertContains('Content-Length: ' . $length, $this->getHeaders());
99
        $this->expectOutputString('Example body');
100
    }
101
102
    public function testNoContentLengthHeaderWhenBodyIsEmpty(): void
103
    {
104
        $length = 100;
105
        $response = $this->createResponse(200, ['Content-Length' => $length, 'X-Test' => 1], '');
106
107
        $this->createEmitter()->emit($response);
108
109
        $this->assertEquals(200, $this->getResponseCode());
110
        $this->assertEquals(['X-Test: 1'], $this->getHeaders());
111
        $this->expectOutputString('');
112
    }
113
114
    public function testContentFullyEmitted(): void
115
    {
116
        $body = 'Example body';
117
        $response = $this->createResponse(200, ['Content-length' => 1, 'X-Test' => 1], $body);
118
119
        $this->createEmitter()->emit($response);
120
121
        $this->expectOutputString($body);
122
    }
123
124
    public function testSentHeadersRemoved(): void
125
    {
126
        HTTPFunctions::header('Cookie-Set: First Cookie');
127
        HTTPFunctions::header('X-Test: 1');
128
        $body = 'Example body';
129
        $response = $this->createResponse(200, [], $body);
130
131
        $this->createEmitter()->emit($response);
132
133
        $this->assertEquals(['Content-Length: ' . strlen($body)], $this->getHeaders());
134
        $this->expectOutputString($body);
135
    }
136
137
    public function testExceptionWhenHeadersHaveBeenSent(): void
138
    {
139
        $body = 'Example body';
140
        $response = $this->createResponse(200, [], $body);
141
        HTTPFunctions::set_headers_sent(true, 'test-file.php', 200);
142
143
        $this->expectException(HeadersHaveBeenSentException::class);
144
        $this->createEmitter()->emit($response);
145
    }
146
147
    public function testEmitDuplicateHeaders(): void
148
    {
149
        $body = 'Example body';
150
        $response = $this->createResponse(200, [], $body)
151
                         ->withHeader('X-Test', '1')
152
                         ->withAddedHeader('X-Test', '2')
153
                         ->withAddedHeader('X-Test', '3; 3.5')
154
                         ->withHeader('Cookie-Set', '1')
155
                         ->withAddedHeader('cookie-Set', '2')
156
                         ->withAddedHeader('Cookie-set', '3');
157
158
        (new SapiEmitter())->emit($response);
159
        $this->assertEquals(200, $this->getResponseCode());
160
        $this->assertContains('X-Test: 1', $this->getHeaders());
161
        $this->assertContains('X-Test: 2', $this->getHeaders());
162
        $this->assertContains('X-Test: 3; 3.5', $this->getHeaders());
163
        $this->assertContains('Cookie-Set: 1', $this->getHeaders());
164
        $this->assertContains('Cookie-Set: 2', $this->getHeaders());
165
        $this->assertContains('Cookie-Set: 3', $this->getHeaders());
166
        $this->assertContains('Content-Length: ' . strlen($body), $this->getHeaders());
167
        $this->expectOutputString($body);
168
    }
169
170
    private function createEmitter(?int $bufferSize = null): SapiEmitter
171
    {
172
        return new SapiEmitter($bufferSize);
173
    }
174
175
    private function createResponse(
176
        int $status = 200,
177
        array $headers = [],
178
        $body = null,
179
        string $version = '1.1'
180
    ): ResponseInterface {
181
        $response = (new Response())
182
            ->withStatus($status)
183
            ->withProtocolVersion($version);
184
        foreach ($headers as $header => $value) {
185
            $response = $response->withHeader($header, $value);
186
        }
187
        if ($body instanceof StreamInterface) {
188
            $response = $response->withBody($body);
189
        } elseif (is_string($body)) {
190
            $response->getBody()->write($body);
191
        }
192
        return $response;
193
    }
194
195
    private function getHeaders(): array
196
    {
197
        return HTTPFunctions::headers_list();
198
    }
199
200
    private function getResponseCode(): int
201
    {
202
        return HTTPFunctions::http_response_code();
203
    }
204
}
205