[TASK] Shorten the extend class in all unit tests
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Http / MessageTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\Http;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Http\Message;
18 use TYPO3\CMS\Core\Http\Stream;
19 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
20
21 /**
22 * Testcase for \TYPO3\CMS\Core\Http\Message
23 *
24 * Adapted from https://github.com/phly/http/
25 */
26 class MessageTest extends UnitTestCase
27 {
28 /**
29 * @var Stream
30 */
31 protected $stream;
32
33 /**
34 * @var Message
35 */
36 protected $message;
37
38 protected function setUp()
39 {
40 $this->stream = new Stream('php://memory', 'wb+');
41 $this->message = (new Message())->withBody($this->stream);
42 }
43
44 /**
45 * @test
46 */
47 public function protocolHasAcceptableDefault()
48 {
49 $this->assertEquals('1.1', $this->message->getProtocolVersion());
50 }
51
52 /**
53 * @test
54 */
55 public function protocolMutatorReturnsCloneWithChanges()
56 {
57 $message = $this->message->withProtocolVersion('1.0');
58 $this->assertNotSame($this->message, $message);
59 $this->assertEquals('1.0', $message->getProtocolVersion());
60 }
61
62 /**
63 * @test
64 */
65 public function usesStreamProvidedInConstructorAsBody()
66 {
67 $this->assertSame($this->stream, $this->message->getBody());
68 }
69
70 /**
71 * @test
72 */
73 public function bodyMutatorReturnsCloneWithChanges()
74 {
75 $stream = new Stream('php://memory', 'wb+');
76 $message = $this->message->withBody($stream);
77 $this->assertNotSame($this->message, $message);
78 $this->assertSame($stream, $message->getBody());
79 }
80
81 /**
82 * @test
83 */
84 public function getHeaderReturnsHeaderValueAsArray()
85 {
86 $message = $this->message->withHeader('X-Foo', ['Foo', 'Bar']);
87 $this->assertNotSame($this->message, $message);
88 $this->assertEquals(['Foo', 'Bar'], $message->getHeader('X-Foo'));
89 }
90
91 /**
92 * @test
93 */
94 public function getHeaderLineReturnsHeaderValueAsCommaConcatenatedString()
95 {
96 $message = $this->message->withHeader('X-Foo', ['Foo', 'Bar']);
97 $this->assertNotSame($this->message, $message);
98 $this->assertEquals('Foo,Bar', $message->getHeaderLine('X-Foo'));
99 }
100
101 /**
102 * @test
103 */
104 public function getHeadersKeepsHeaderCaseSensitivity()
105 {
106 $message = $this->message->withHeader('X-Foo', ['Foo', 'Bar']);
107 $this->assertNotSame($this->message, $message);
108 $this->assertEquals(['X-Foo' => ['Foo', 'Bar']], $message->getHeaders());
109 }
110
111 /**
112 * @test
113 */
114 public function getHeadersReturnsCaseWithWhichHeaderFirstRegistered()
115 {
116 $message = $this->message
117 ->withHeader('X-Foo', 'Foo')
118 ->withAddedHeader('x-foo', 'Bar');
119 $this->assertNotSame($this->message, $message);
120 $this->assertEquals(['X-Foo' => ['Foo', 'Bar']], $message->getHeaders());
121 }
122
123 /**
124 * @test
125 */
126 public function hasHeaderReturnsFalseIfHeaderIsNotPresent()
127 {
128 $this->assertFalse($this->message->hasHeader('X-Foo'));
129 }
130
131 /**
132 * @test
133 */
134 public function hasHeaderReturnsTrueIfHeaderIsPresent()
135 {
136 $message = $this->message->withHeader('X-Foo', 'Foo');
137 $this->assertNotSame($this->message, $message);
138 $this->assertTrue($message->hasHeader('X-Foo'));
139 }
140
141 /**
142 * @test
143 */
144 public function addHeaderAppendsToExistingHeader()
145 {
146 $message = $this->message->withHeader('X-Foo', 'Foo');
147 $this->assertNotSame($this->message, $message);
148 $message2 = $message->withAddedHeader('X-Foo', 'Bar');
149 $this->assertNotSame($message, $message2);
150 $this->assertEquals('Foo,Bar', $message2->getHeaderLine('X-Foo'));
151 }
152
153 /**
154 * @test
155 */
156 public function canRemoveHeaders()
157 {
158 $message = $this->message->withHeader('X-Foo', 'Foo');
159 $this->assertNotSame($this->message, $message);
160 $this->assertTrue($message->hasHeader('x-foo'));
161 $message2 = $message->withoutHeader('x-foo');
162 $this->assertNotSame($this->message, $message2);
163 $this->assertNotSame($message, $message2);
164 $this->assertFalse($message2->hasHeader('X-Foo'));
165 }
166
167 /**
168 * @test
169 */
170 public function headerRemovalIsCaseInsensitive()
171 {
172 $message = $this->message
173 ->withHeader('X-Foo', 'Foo')
174 ->withAddedHeader('x-foo', 'Bar')
175 ->withAddedHeader('X-FOO', 'Baz');
176 $this->assertNotSame($this->message, $message);
177 $this->assertTrue($message->hasHeader('x-foo'));
178 $message2 = $message->withoutHeader('x-foo');
179 $this->assertNotSame($this->message, $message2);
180 $this->assertNotSame($message, $message2);
181 $this->assertFalse($message2->hasHeader('X-Foo'));
182 $headers = $message2->getHeaders();
183 $this->assertEquals(0, count($headers));
184 }
185
186 /**
187 * @return array
188 */
189 public function invalidGeneralHeaderValuesDataProvider()
190 {
191 return [
192 'null' => [null],
193 'true' => [true],
194 'false' => [false],
195 'int' => [1],
196 'float' => [1.1],
197 'array' => [['foo' => ['bar']]],
198 'object' => [(object)['foo' => 'bar']],
199 ];
200 }
201
202 /**
203 * @dataProvider invalidGeneralHeaderValuesDataProvider
204 */
205 public function testWithHeaderRaisesExceptionForInvalidNestedHeaderValue($value)
206 {
207 $this->expectException(\InvalidArgumentException::class);
208 $this->expectExceptionCode(1436717266);
209 $this->message->withHeader('X-Foo', [$value]);
210 }
211
212 /**
213 * @return array
214 */
215 public function invalidHeaderValuesDataProvider()
216 {
217 return [
218 'null' => [null],
219 'true' => [true],
220 'false' => [false],
221 'int' => [1],
222 'float' => [1.1],
223 'object' => [(object)['foo' => 'bar']],
224 ];
225 }
226
227 /**
228 * @dataProvider invalidHeaderValuesDataProvider
229 */
230 public function withHeaderRaisesExceptionForInvalidValueType($value)
231 {
232 $this->expectException(\InvalidArgumentException::class);
233 $this->expectExceptionCode(1436717266);
234 $this->message->withHeader('X-Foo', $value);
235 }
236
237 /**
238 * @dataProvider invalidHeaderValuesDataProvider
239 */
240 public function withAddedHeaderRaisesExceptionForNonStringNonArrayValue($value)
241 {
242 $this->expectException(\InvalidArgumentException::class);
243 $this->expectExceptionCode(1436717267);
244 $this->message->withAddedHeader('X-Foo', $value);
245 }
246
247 /**
248 * @test
249 */
250 public function withoutHeaderDoesNothingIfHeaderDoesNotExist()
251 {
252 $this->assertFalse($this->message->hasHeader('X-Foo'));
253 $message = $this->message->withoutHeader('X-Foo');
254 $this->assertNotSame($this->message, $message);
255 $this->assertFalse($message->hasHeader('X-Foo'));
256 }
257
258 /**
259 * @test
260 */
261 public function getHeaderReturnsAnEmptyArrayWhenHeaderDoesNotExist()
262 {
263 $this->assertSame([], $this->message->getHeader('X-Foo-Bar'));
264 }
265
266 /**
267 * @test
268 */
269 public function getHeaderLineReturnsEmptyStringWhenHeaderDoesNotExist()
270 {
271 $this->assertSame('', $this->message->getHeaderLine('X-Foo-Bar'));
272 }
273
274 /**
275 * @return array
276 */
277 public function headersWithInjectionVectorsDataProvider()
278 {
279 return [
280 'name-with-cr' => ["X-Foo\r-Bar", 'value'],
281 'name-with-lf' => ["X-Foo\n-Bar", 'value'],
282 'name-with-crlf' => ["X-Foo\r\n-Bar", 'value'],
283 'name-with-2crlf' => ["X-Foo\r\n\r\n-Bar", 'value'],
284 'value-with-cr' => ['X-Foo-Bar', "value\rinjection"],
285 'value-with-lf' => ['X-Foo-Bar', "value\ninjection"],
286 'value-with-crlf' => ['X-Foo-Bar', "value\r\ninjection"],
287 'value-with-2crlf' => ['X-Foo-Bar', "value\r\n\r\ninjection"],
288 'array-value-with-cr' => ['X-Foo-Bar', ["value\rinjection"]],
289 'array-value-with-lf' => ['X-Foo-Bar', ["value\ninjection"]],
290 'array-value-with-crlf' => ['X-Foo-Bar', ["value\r\ninjection"]],
291 'array-value-with-2crlf' => ['X-Foo-Bar', ["value\r\n\r\ninjection"]],
292 'multi-line-header-space' => ['X-Foo-Bar', "value\r\n injection"],
293 'multi-line-header-tab' => ['X-Foo-Bar', "value\r\n\tinjection"],
294 ];
295 }
296
297 /**
298 * @dataProvider headersWithInjectionVectorsDataProvider
299 * @test
300 */
301 public function doesNotAllowCRLFInjectionWhenCallingWithHeader($name, $value)
302 {
303 $this->expectException(\InvalidArgumentException::class);
304 $this->message->withHeader($name, $value);
305 }
306
307 /**
308 * @dataProvider headersWithInjectionVectorsDataProvider
309 * @test
310 */
311 public function doesNotAllowCRLFInjectionWhenCallingWithAddedHeader($name, $value)
312 {
313 $this->expectException(\InvalidArgumentException::class);
314 $this->message->withAddedHeader($name, $value);
315 }
316 }