1<?php
2namespace GuzzleHttp\Tests\Ring;
3
4use GuzzleHttp\Ring\Core;
5use GuzzleHttp\Ring\Future\CompletedFutureArray;
6use GuzzleHttp\Ring\Future\FutureArray;
7use GuzzleHttp\Stream\Stream;
8use React\Promise\Deferred;
9
10class CoreTest extends \PHPUnit_Framework_TestCase
11{
12    public function testReturnsNullNoHeadersAreSet()
13    {
14        $this->assertNull(Core::header([], 'Foo'));
15        $this->assertNull(Core::firstHeader([], 'Foo'));
16    }
17
18    public function testChecksIfHasHeader()
19    {
20        $message = [
21            'headers' => [
22                'Foo' => ['Bar', 'Baz'],
23                'foo' => ['hello'],
24                'bar' => ['1']
25            ]
26        ];
27        $this->assertTrue(Core::hasHeader($message, 'Foo'));
28        $this->assertTrue(Core::hasHeader($message, 'foo'));
29        $this->assertTrue(Core::hasHeader($message, 'FoO'));
30        $this->assertTrue(Core::hasHeader($message, 'bar'));
31        $this->assertFalse(Core::hasHeader($message, 'barr'));
32    }
33
34    public function testReturnsFirstHeaderWhenSimple()
35    {
36        $this->assertEquals('Bar', Core::firstHeader([
37            'headers' => ['Foo' => ['Bar', 'Baz']],
38        ], 'Foo'));
39    }
40
41    public function testReturnsFirstHeaderWhenMultiplePerLine()
42    {
43        $this->assertEquals('Bar', Core::firstHeader([
44            'headers' => ['Foo' => ['Bar, Baz']],
45        ], 'Foo'));
46    }
47
48    public function testExtractsCaseInsensitiveHeader()
49    {
50        $this->assertEquals(
51            'hello',
52            Core::header(['headers' => ['foo' => ['hello']]], 'FoO')
53        );
54    }
55
56    public function testExtractsCaseInsensitiveHeaderLines()
57    {
58        $this->assertEquals(
59            ['a', 'b', 'c', 'd'],
60            Core::headerLines([
61                'headers' => [
62                    'foo' => ['a', 'b'],
63                    'Foo' => ['c', 'd']
64                ]
65            ], 'foo')
66        );
67    }
68
69    public function testExtractsHeaderLines()
70    {
71        $this->assertEquals(
72            ['bar', 'baz'],
73            Core::headerLines([
74                'headers' => [
75                    'Foo' => ['bar', 'baz'],
76                ],
77            ], 'Foo')
78        );
79    }
80
81    public function testExtractsHeaderAsString()
82    {
83        $this->assertEquals(
84            'bar, baz',
85            Core::header([
86                'headers' => [
87                    'Foo' => ['bar', 'baz'],
88                ],
89            ], 'Foo', true)
90        );
91    }
92
93    public function testReturnsNullWhenHeaderNotFound()
94    {
95        $this->assertNull(Core::header(['headers' => []], 'Foo'));
96    }
97
98    public function testRemovesHeaders()
99    {
100        $message = [
101            'headers' => [
102                'foo' => ['bar'],
103                'Foo' => ['bam'],
104                'baz' => ['123'],
105            ],
106        ];
107
108        $this->assertSame($message, Core::removeHeader($message, 'bam'));
109        $this->assertEquals([
110            'headers' => ['baz' => ['123']],
111        ], Core::removeHeader($message, 'foo'));
112    }
113
114    public function testCreatesUrl()
115    {
116        $req = [
117            'scheme'  => 'http',
118            'headers' => ['host' => ['foo.com']],
119            'uri'     => '/',
120        ];
121
122        $this->assertEquals('http://foo.com/', Core::url($req));
123    }
124
125    /**
126     * @expectedException \InvalidArgumentException
127     * @expectedExceptionMessage No Host header was provided
128     */
129    public function testEnsuresHostIsAvailableWhenCreatingUrls()
130    {
131        Core::url([]);
132    }
133
134    public function testCreatesUrlWithQueryString()
135    {
136        $req = [
137            'scheme'       => 'http',
138            'headers'      => ['host' => ['foo.com']],
139            'uri'          => '/',
140            'query_string' => 'foo=baz',
141        ];
142
143        $this->assertEquals('http://foo.com/?foo=baz', Core::url($req));
144    }
145
146    public function testUsesUrlIfSet()
147    {
148        $req = ['url' => 'http://foo.com'];
149        $this->assertEquals('http://foo.com', Core::url($req));
150    }
151
152    public function testReturnsNullWhenNoBody()
153    {
154        $this->assertNull(Core::body([]));
155    }
156
157    public function testReturnsStreamAsString()
158    {
159        $this->assertEquals(
160            'foo',
161            Core::body(['body' => Stream::factory('foo')])
162        );
163    }
164
165    public function testReturnsString()
166    {
167        $this->assertEquals('foo', Core::body(['body' => 'foo']));
168    }
169
170    public function testReturnsResourceContent()
171    {
172        $r = fopen('php://memory', 'w+');
173        fwrite($r, 'foo');
174        rewind($r);
175        $this->assertEquals('foo', Core::body(['body' => $r]));
176        fclose($r);
177    }
178
179    public function testReturnsIteratorContent()
180    {
181        $a = new \ArrayIterator(['a', 'b', 'cd', '']);
182        $this->assertEquals('abcd', Core::body(['body' => $a]));
183    }
184
185    public function testReturnsObjectToString()
186    {
187        $this->assertEquals('foo', Core::body(['body' => new StrClass]));
188    }
189
190    /**
191     * @expectedException \InvalidArgumentException
192     */
193    public function testEnsuresBodyIsValid()
194    {
195        Core::body(['body' => false]);
196    }
197
198    public function testParsesHeadersFromLines()
199    {
200        $lines = ['Foo: bar', 'Foo: baz', 'Abc: 123', 'Def: a, b'];
201        $this->assertEquals([
202            'Foo' => ['bar', 'baz'],
203            'Abc' => ['123'],
204            'Def' => ['a, b'],
205        ], Core::headersFromLines($lines));
206    }
207
208    public function testParsesHeadersFromLinesWithMultipleLines()
209    {
210        $lines = ['Foo: bar', 'Foo: baz', 'Foo: 123'];
211        $this->assertEquals([
212            'Foo' => ['bar', 'baz', '123'],
213        ], Core::headersFromLines($lines));
214    }
215
216    public function testCreatesArrayCallFunctions()
217    {
218        $called = [];
219        $a = function ($a, $b) use (&$called) {
220            $called['a'] = func_get_args();
221        };
222        $b = function ($a, $b) use (&$called) {
223            $called['b'] = func_get_args();
224        };
225        $c = Core::callArray([$a, $b]);
226        $c(1, 2);
227        $this->assertEquals([1, 2], $called['a']);
228        $this->assertEquals([1, 2], $called['b']);
229    }
230
231    public function testRewindsGuzzleStreams()
232    {
233        $str = Stream::factory('foo');
234        $this->assertTrue(Core::rewindBody(['body' => $str]));
235    }
236
237    public function testRewindsStreams()
238    {
239        $str = Stream::factory('foo')->detach();
240        $this->assertTrue(Core::rewindBody(['body' => $str]));
241    }
242
243    public function testRewindsIterators()
244    {
245        $iter = new \ArrayIterator(['foo']);
246        $this->assertTrue(Core::rewindBody(['body' => $iter]));
247    }
248
249    public function testRewindsStrings()
250    {
251        $this->assertTrue(Core::rewindBody(['body' => 'hi']));
252    }
253
254    public function testRewindsToStrings()
255    {
256        $this->assertTrue(Core::rewindBody(['body' => new StrClass()]));
257    }
258
259    public function typeProvider()
260    {
261        return [
262            ['foo', 'string(3) "foo"'],
263            [true, 'bool(true)'],
264            [false, 'bool(false)'],
265            [10, 'int(10)'],
266            [1.0, 'float(1)'],
267            [new StrClass(), 'object(GuzzleHttp\Tests\Ring\StrClass)'],
268            [['foo'], 'array(1)']
269        ];
270    }
271
272    /**
273     * @dataProvider typeProvider
274     */
275    public function testDescribesType($input, $output)
276    {
277        $this->assertEquals($output, Core::describeType($input));
278    }
279
280    public function testDoesSleep()
281    {
282        $t = microtime(true);
283        $expected = $t + (100 / 1000);
284        Core::doSleep(['client' => ['delay' => 100]]);
285        $this->assertGreaterThanOrEqual($expected, microtime(true));
286    }
287
288    public function testProxiesFuture()
289    {
290        $f = new CompletedFutureArray(['status' => 200]);
291        $res = null;
292        $proxied = Core::proxy($f, function ($value) use (&$res) {
293            $value['foo'] = 'bar';
294            $res = $value;
295            return $value;
296        });
297        $this->assertNotSame($f, $proxied);
298        $this->assertEquals(200, $f->wait()['status']);
299        $this->assertArrayNotHasKey('foo', $f->wait());
300        $this->assertEquals('bar', $proxied->wait()['foo']);
301        $this->assertEquals(200, $proxied->wait()['status']);
302    }
303
304    public function testProxiesDeferredFuture()
305    {
306        $d = new Deferred();
307        $f = new FutureArray($d->promise());
308        $f2 = Core::proxy($f);
309        $d->resolve(['foo' => 'bar']);
310        $this->assertEquals('bar', $f['foo']);
311        $this->assertEquals('bar', $f2['foo']);
312    }
313
314    public function testProxiesDeferredFutureFailure()
315    {
316        $d = new Deferred();
317        $f = new FutureArray($d->promise());
318        $f2 = Core::proxy($f);
319        $d->reject(new \Exception('foo'));
320        try {
321            $f2['hello?'];
322            $this->fail('did not throw');
323        } catch (\Exception $e) {
324            $this->assertEquals('foo', $e->getMessage());
325        }
326
327    }
328}
329
330final class StrClass
331{
332    public function __toString()
333    {
334        return 'foo';
335    }
336}
337