Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
| 1 | <?php |
||
| 13 | class RequestTest extends TestCase |
||
| 14 | { |
||
| 15 | public function testRequestUriMayBeString() |
||
| 16 | { |
||
| 17 | $r = new Request('GET', '/'); |
||
| 18 | $this->assertEquals('/', (string) $r->getUri()); |
||
| 19 | } |
||
| 20 | |||
| 21 | public function testRequestUriMayBeUri() |
||
| 22 | { |
||
| 23 | $uri = new Uri('/'); |
||
| 24 | $r = new Request('GET', $uri); |
||
| 25 | $this->assertSame($uri, $r->getUri()); |
||
| 26 | } |
||
| 27 | |||
| 28 | public function testValidateRequestUri() |
||
| 29 | { |
||
| 30 | $this->expectException(\InvalidArgumentException::class); |
||
| 31 | $this->expectExceptionMessage('Unable to parse URI: ///'); |
||
| 32 | |||
| 33 | new Request('GET', '///'); |
||
| 34 | } |
||
| 35 | |||
| 36 | public function testCanConstructWithBody() |
||
| 37 | { |
||
| 38 | $r = new Request('GET', '/', [], 'baz'); |
||
| 39 | $this->assertInstanceOf(StreamInterface::class, $r->getBody()); |
||
| 40 | $this->assertEquals('baz', (string) $r->getBody()); |
||
| 41 | } |
||
| 42 | |||
| 43 | public function testNullBody() |
||
| 44 | { |
||
| 45 | $r = new Request('GET', '/', [], null); |
||
| 46 | $this->assertInstanceOf(StreamInterface::class, $r->getBody()); |
||
| 47 | $this->assertSame('', (string) $r->getBody()); |
||
| 48 | } |
||
| 49 | |||
| 50 | public function testFalseyBody() |
||
| 51 | { |
||
| 52 | $r = new Request('GET', '/', [], '0'); |
||
| 53 | $this->assertInstanceOf(StreamInterface::class, $r->getBody()); |
||
| 54 | $this->assertSame('0', (string) $r->getBody()); |
||
| 55 | } |
||
| 56 | |||
| 57 | public function testConstructorDoesNotReadStreamBody() |
||
| 58 | { |
||
| 59 | $body = $this->getMockBuilder(StreamInterface::class)->getMock(); |
||
| 60 | $body->expects($this->never()) |
||
| 61 | ->method('__toString'); |
||
| 62 | |||
| 63 | $r = new Request('GET', '/', [], $body); |
||
| 64 | $this->assertSame($body, $r->getBody()); |
||
| 65 | } |
||
| 66 | |||
| 67 | public function testWithUri() |
||
| 68 | { |
||
| 69 | $r1 = new Request('GET', '/'); |
||
| 70 | $u1 = $r1->getUri(); |
||
| 71 | $u2 = new Uri('http://www.example.com'); |
||
| 72 | $r2 = $r1->withUri($u2); |
||
| 73 | $this->assertNotSame($r1, $r2); |
||
| 74 | $this->assertSame($u2, $r2->getUri()); |
||
| 75 | $this->assertSame($u1, $r1->getUri()); |
||
| 76 | |||
| 77 | $r3 = new Request('GET', '/'); |
||
| 78 | $u3 = $r3->getUri(); |
||
| 79 | $r4 = $r3->withUri($u3); |
||
| 80 | $this->assertSame($r3, $r4, 'If the Request did not change, then there is no need to create a new request object'); |
||
| 81 | |||
| 82 | $u4 = new Uri('/'); |
||
| 83 | $r5 = $r3->withUri($u4); |
||
| 84 | $this->assertNotSame($r3, $r5); |
||
| 85 | } |
||
| 86 | |||
| 87 | public function testSameInstanceWhenSameUri() |
||
| 88 | { |
||
| 89 | $r1 = new Request('GET', 'http://foo.com'); |
||
| 90 | $r2 = $r1->withUri($r1->getUri()); |
||
| 91 | $this->assertSame($r1, $r2); |
||
| 92 | } |
||
| 93 | |||
| 94 | public function testWithRequestTarget() |
||
| 95 | { |
||
| 96 | $r1 = new Request('GET', '/'); |
||
| 97 | $r2 = $r1->withRequestTarget('*'); |
||
| 98 | $this->assertEquals('*', $r2->getRequestTarget()); |
||
| 99 | $this->assertEquals('/', $r1->getRequestTarget()); |
||
| 100 | } |
||
| 101 | |||
| 102 | public function testWithInvalidRequestTarget() |
||
| 103 | { |
||
| 104 | $r = new Request('GET', '/'); |
||
| 105 | $this->expectException(\InvalidArgumentException::class); |
||
| 106 | $r->withRequestTarget('foo bar'); |
||
| 107 | } |
||
| 108 | |||
| 109 | public function testGetRequestTarget() |
||
| 110 | { |
||
| 111 | $r = new Request('GET', 'https://nyholm.tech'); |
||
| 112 | $this->assertEquals('/', $r->getRequestTarget()); |
||
| 113 | |||
| 114 | $r = new Request('GET', 'https://nyholm.tech/foo?bar=baz'); |
||
| 115 | $this->assertEquals('/foo?bar=baz', $r->getRequestTarget()); |
||
| 116 | |||
| 117 | $r = new Request('GET', 'https://nyholm.tech?bar=baz'); |
||
| 118 | $this->assertEquals('/?bar=baz', $r->getRequestTarget()); |
||
| 119 | } |
||
| 120 | |||
| 121 | public function testRequestTargetDoesNotAllowSpaces() |
||
| 122 | { |
||
| 123 | $this->expectException(\InvalidArgumentException::class); |
||
| 124 | $this->expectExceptionMessage('Invalid request target provided; cannot contain whitespace'); |
||
| 125 | |||
| 126 | $r1 = new Request('GET', '/'); |
||
| 127 | $r1->withRequestTarget('/foo bar'); |
||
| 128 | } |
||
| 129 | |||
| 130 | public function testRequestTargetDefaultsToSlash() |
||
| 131 | { |
||
| 132 | $r1 = new Request('GET', ''); |
||
| 133 | $this->assertEquals('/', $r1->getRequestTarget()); |
||
| 134 | $r2 = new Request('GET', '*'); |
||
| 135 | $this->assertEquals('*', $r2->getRequestTarget()); |
||
| 136 | $r3 = new Request('GET', 'http://foo.com/bar baz/'); |
||
| 137 | $this->assertEquals('/bar%20baz/', $r3->getRequestTarget()); |
||
| 138 | } |
||
| 139 | |||
| 140 | public function testBuildsRequestTarget() |
||
| 141 | { |
||
| 142 | $r1 = new Request('GET', 'http://foo.com/baz?bar=bam'); |
||
| 143 | $this->assertEquals('/baz?bar=bam', $r1->getRequestTarget()); |
||
| 144 | } |
||
| 145 | |||
| 146 | public function testBuildsRequestTargetWithFalseyQuery() |
||
| 147 | { |
||
| 148 | $r1 = new Request('GET', 'http://foo.com/baz?0'); |
||
| 149 | $this->assertEquals('/baz?0', $r1->getRequestTarget()); |
||
| 150 | } |
||
| 151 | |||
| 152 | public function testHostIsAddedFirst() |
||
| 153 | { |
||
| 154 | $r = new Request('GET', 'http://foo.com/baz?bar=bam', ['Foo' => 'Bar']); |
||
| 155 | $this->assertEquals([ |
||
| 156 | 'Host' => ['foo.com'], |
||
| 157 | 'Foo' => ['Bar'], |
||
| 158 | ], $r->getHeaders()); |
||
| 159 | } |
||
| 160 | |||
| 161 | public function testCanGetHeaderAsCsv() |
||
| 162 | { |
||
| 163 | $r = new Request('GET', 'http://foo.com/baz?bar=bam', [ |
||
| 164 | 'Foo' => ['a', 'b', 'c'], |
||
| 165 | ]); |
||
| 166 | $this->assertEquals('a, b, c', $r->getHeaderLine('Foo')); |
||
| 167 | $this->assertEquals('', $r->getHeaderLine('Bar')); |
||
| 168 | } |
||
| 169 | |||
| 170 | public function testHostIsNotOverwrittenWhenPreservingHost() |
||
| 171 | { |
||
| 172 | $r = new Request('GET', 'http://foo.com/baz?bar=bam', ['Host' => 'a.com']); |
||
| 173 | $this->assertEquals(['Host' => ['a.com']], $r->getHeaders()); |
||
| 174 | $r2 = $r->withUri(new Uri('http://www.foo.com/bar'), true); |
||
| 175 | $this->assertEquals('a.com', $r2->getHeaderLine('Host')); |
||
| 176 | } |
||
| 177 | |||
| 178 | public function testOverridesHostWithUri() |
||
| 179 | { |
||
| 180 | $r = new Request('GET', 'http://foo.com/baz?bar=bam'); |
||
| 181 | $this->assertEquals(['Host' => ['foo.com']], $r->getHeaders()); |
||
| 182 | $r2 = $r->withUri(new Uri('http://www.baz.com/bar')); |
||
| 183 | $this->assertEquals('www.baz.com', $r2->getHeaderLine('Host')); |
||
| 184 | } |
||
| 185 | |||
| 186 | public function testAggregatesHeaders() |
||
| 187 | { |
||
| 188 | $r = new Request('GET', '', [ |
||
| 189 | 'ZOO' => 'zoobar', |
||
| 190 | 'zoo' => ['foobar', 'zoobar'], |
||
| 191 | ]); |
||
| 192 | $this->assertEquals(['ZOO' => ['zoobar', 'foobar', 'zoobar']], $r->getHeaders()); |
||
| 193 | $this->assertEquals('zoobar, foobar, zoobar', $r->getHeaderLine('zoo')); |
||
| 194 | } |
||
| 195 | |||
| 196 | public function testSupportNumericHeaders() |
||
| 197 | { |
||
| 198 | $r = new Request('GET', '', [ |
||
| 199 | 'Content-Length' => 200, |
||
| 200 | ]); |
||
| 201 | $this->assertSame(['Content-Length' => ['200']], $r->getHeaders()); |
||
| 202 | $this->assertSame('200', $r->getHeaderLine('Content-Length')); |
||
| 203 | } |
||
| 204 | |||
| 205 | public function testAddsPortToHeader() |
||
| 206 | { |
||
| 207 | $r = new Request('GET', 'http://foo.com:8124/bar'); |
||
| 208 | $this->assertEquals('foo.com:8124', $r->getHeaderLine('host')); |
||
| 209 | } |
||
| 210 | |||
| 211 | public function testAddsPortToHeaderAndReplacePreviousPort() |
||
| 212 | { |
||
| 213 | $r = new Request('GET', 'http://foo.com:8124/bar'); |
||
| 214 | $r = $r->withUri(new Uri('http://foo.com:8125/bar')); |
||
| 215 | $this->assertEquals('foo.com:8125', $r->getHeaderLine('host')); |
||
| 216 | } |
||
| 217 | |||
| 218 | public function testCannotHaveHeaderWithEmptyName() |
||
| 219 | { |
||
| 220 | $this->expectException(\InvalidArgumentException::class); |
||
| 221 | $this->expectExceptionMessage('Header name must be an RFC 7230 compatible string.'); |
||
| 222 | $r = new Request('GET', 'https://example.com/'); |
||
| 223 | $r->withHeader('', 'Bar'); |
||
| 224 | } |
||
| 225 | |||
| 226 | public function testCanHaveHeaderWithEmptyValue() |
||
| 227 | { |
||
| 228 | $r = new Request('GET', 'https://example.com/'); |
||
| 229 | $r = $r->withHeader('Foo', ''); |
||
| 230 | $this->assertEquals([''], $r->getHeader('Foo')); |
||
| 231 | } |
||
| 232 | |||
| 233 | public function testUpdateHostFromUri() |
||
| 234 | { |
||
| 235 | $request = new Request('GET', '/'); |
||
| 236 | $request = $request->withUri(new Uri('https://nyholm.tech')); |
||
| 237 | $this->assertEquals('nyholm.tech', $request->getHeaderLine('Host')); |
||
| 238 | |||
| 239 | $request = new Request('GET', 'https://example.com/'); |
||
| 240 | $this->assertEquals('example.com', $request->getHeaderLine('Host')); |
||
| 241 | $request = $request->withUri(new Uri('https://nyholm.tech')); |
||
| 242 | $this->assertEquals('nyholm.tech', $request->getHeaderLine('Host')); |
||
| 243 | |||
| 244 | $request = new Request('GET', '/'); |
||
| 245 | $request = $request->withUri(new Uri('https://nyholm.tech:8080')); |
||
| 246 | $this->assertEquals('nyholm.tech:8080', $request->getHeaderLine('Host')); |
||
| 247 | |||
| 248 | $request = new Request('GET', '/'); |
||
| 249 | $request = $request->withUri(new Uri('https://nyholm.tech:443')); |
||
| 250 | $this->assertEquals('nyholm.tech', $request->getHeaderLine('Host')); |
||
| 251 | } |
||
| 252 | } |
||
| 253 |