Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 125
0.00% covered (danger)
0.00%
0 / 16
CRAP
0.00% covered (danger)
0.00%
0 / 4
HttpTest
0.00% covered (danger)
0.00%
0 / 99
0.00% covered (danger)
0.00%
0 / 7
56
0.00% covered (danger)
0.00%
0 / 1
 setUpBeforeClass
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 tearDownAfterClass
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 testVersionHeaderPresence
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
2
 testGET
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
2
 testPOST
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
2
 testRequestBasic
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
2
 testRequestReturnValues
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
2
CurlWrapperMock
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 4
30
0.00% covered (danger)
0.00%
0 / 1
 init
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setOpt
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 exec
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getInfo
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
HttpMock
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 2
6
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getCurlLastParams
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
HttpMockReturnData
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 3
30
0.00% covered (danger)
0.00%
0 / 1
 setResponseJSONString
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 request
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 endsWith
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2namespace tests;
3
4use Ably\AblyRest;
5use Ably\Http;
6use Ably\Utils\CurlWrapper;
7use Ably\Models\Untyped;
8
9require_once __DIR__ . '/factories/TestApp.php';
10
11class HttpTest extends \PHPUnit_Framework_TestCase {
12
13    protected static $testApp;
14    protected static $defaultOptions;
15    protected static $ably;
16
17    public static function setUpBeforeClass() {
18        self::$testApp = new TestApp();
19        self::$defaultOptions = self::$testApp->getOptions();
20        self::$ably = new AblyRest( array_merge( self::$defaultOptions, [
21            'key' => self::$testApp->getAppKeyDefault()->string,
22        ] ) );
23    }
24
25    public static function tearDownAfterClass() {
26        self::$testApp->release();
27    }
28
29    /**
30     * Verify that API version is sent in HTTP requests
31     */
32    public function testVersionHeaderPresence() {
33        $opts = [
34            'key' => 'fake.key:totallyFake',
35            'httpClass' => 'tests\HttpMock',
36        ];
37        $ably = new AblyRest( $opts );
38        $ably->time(); // make a request
39
40        $curlParams = $ably->http->getCurlLastParams();
41
42        $expectedVersion = '1.0';
43
44        $this->assertArrayHasKey( 'X-Ably-Version', $curlParams[CURLOPT_HTTPHEADER], 'Expected Ably version header in HTTP request' );
45        $this->assertEquals( $expectedVersion, $curlParams[CURLOPT_HTTPHEADER]['X-Ably-Version'], 'Expected Ably version in HTTP header to match AblyRest constant' );
46
47        $this->assertArrayHasKey( 'X-Ably-Lib', $curlParams[CURLOPT_HTTPHEADER], 'Expected Ably lib header in HTTP request' );
48        $this->assertContains( 'php-' . $expectedVersion, $curlParams[CURLOPT_HTTPHEADER]['X-Ably-Lib'], 'Expected Ably lib in HTTP header to match AblyRest constant' );
49
50        AblyRest::setLibraryFlavourString( 'test' );
51        $ably = new AblyRest( $opts );
52        $ably->time(); // make a request
53
54        $curlParams = $ably->http->getCurlLastParams();
55        $this->assertContains( 'php-test-' . $expectedVersion, $curlParams[CURLOPT_HTTPHEADER]['X-Ably-Lib'], 'Expected X-Ably-Lib to contain library flavour string' );
56
57        AblyRest::setLibraryFlavourString();
58    }
59
60
61    /**
62     * Verify that GET requests are encoded properly (using requestToken)
63     */
64    public function testGET() {
65        $authParams = [
66            'param1' => '&?#',
67            'param2' => 'x',
68        ];
69        $tokenParams = [
70            'clientId' => 'test',
71        ];
72
73        $ably = new AblyRest( [
74            'key' => 'fake.key:totallyFake',
75            'authUrl' => 'http://test.test/tokenRequest',
76            'authParams' => $authParams,
77            'authMethod' => 'GET',
78            'httpClass' => 'tests\HttpMock',
79        ] );
80
81        $expectedParams = array_merge( $authParams, $tokenParams );
82
83        $ably->auth->requestToken( $tokenParams );
84
85        $curlParams = $ably->http->getCurlLastParams();
86
87        $this->assertEquals( 'http://test.test/tokenRequest?'.http_build_query($expectedParams), $curlParams[CURLOPT_URL], 'Expected URL to contain encoded GET parameters' );
88    }
89
90
91    /**
92     * Verify that POST requests are encoded properly (using requestToken)
93     */
94    public function testPOST() {
95        $authParams = [
96            'param1' => '&?#',
97            'param2' => 'x',
98        ];
99        $tokenParams = [
100            'clientId' => 'test',
101        ];
102
103        $ably = new AblyRest( [
104            'key' => 'fake.key:totallyFake',
105            'authUrl' => 'http://test.test/tokenRequest',
106            'authParams' => $authParams,
107            'authMethod' => 'POST',
108            'httpClass' => 'tests\HttpMock',
109        ] );
110
111        $expectedParams = array_merge( $authParams, $tokenParams );
112
113        $ably->auth->requestToken( $tokenParams );
114
115        $curlParams = $ably->http->getCurlLastParams();
116
117        $this->assertEquals( 'http://test.test/tokenRequest', $curlParams[CURLOPT_URL], 'Expected URL to match authUrl' );
118        $this->assertEquals( http_build_query($expectedParams), $curlParams[CURLOPT_POSTFIELDS], 'Expected POST params to contain encoded params' );
119    }
120
121    /**
122     * Test basic AblyRest::request functionality
123     */
124    public function testRequestBasic() {
125        $ably = self::$ably;
126
127        $msg = (object) [
128            'name' => 'testEvent',
129            'data' => 'testPayload',
130        ];
131
132        $res = $ably->request('POST', '/channels/persisted:test/messages', [], $msg );
133
134        $this->assertTrue($res->success, 'Expected sending a message via custom request to succeed');
135        $this->assertLessThan(300, $res->statusCode, 'Expected statusCode < 300');
136        $this->assertEmpty($res->errorCode, 'Expected empty errorCode');
137        $this->assertEmpty($res->errorMessage, 'Expected empty errorMessage');
138
139        $res2 = $ably->request('GET', '/channels/persisted:test/messages');
140
141        $this->assertTrue($res2->success, 'Expected retrieving the message via custom request to succeed');
142        $this->assertLessThan(300, $res2->statusCode, 'Expected statusCode < 300');
143        $this->assertArrayHasKey('Content-Type', $res2->headers, 'Expected headers to be an array containing key `Content-Type`');
144        $this->assertEquals(1, count($res2->items), 'Expected to receive 1 message');
145        $this->assertEquals($msg->name, $res2->items[0]->name, 'Expected to receive matching message contents');
146
147        $res3 = $ably->request('GET', '/this-does-not-exist');
148
149        $this->assertEquals(404, $res3->statusCode, 'Expected statusCode 404');
150        $this->assertEquals(40400, $res3->errorCode, 'Expected errorCode 40400');
151        $this->assertNotEmpty($res3->errorMessage, 'Expected errorMessage to be set');
152        $this->assertArrayHasKey('X-Ably-Errorcode', $res3->headers, 'Expected X-Ably-Errorcode header to be present');
153        $this->assertArrayHasKey('X-Ably-Errormessage', $res3->headers, 'Expected X-Ably-Errormessage header to be present');
154    }
155
156    /**
157     * Test that Response handles various returned structures properly
158     */
159    public function testRequestReturnValues() {
160        $ably = new AblyRest( [
161            'key' => 'fake.key:totallyFake',
162            'httpClass' => 'tests\HttpMockReturnData',
163        ] );
164
165        // array of objects
166        $ably->http->setResponseJSONString('[{"test":"one"},{"test":"two"},{"test":"three"}]');
167        $res1 = $ably->request('GET', '/get_test_json');
168        $this->assertEquals('[{"test":"one"},{"test":"two"},{"test":"three"}]', json_encode($res1->items));
169
170        // array with single object
171        $ably->http->setResponseJSONString('[{"test":"yes"}]');
172        $res2 = $ably->request('GET', '/get_test_json');
173        $this->assertEquals('[{"test":"yes"}]', json_encode($res2->items));
174
175        // single object - should be returned as array with single object
176        $ably->http->setResponseJSONString('{"test":"yes"}');
177        $res3 = $ably->request('GET', '/get_test_json');
178        $this->assertEquals('[{"test":"yes"}]', json_encode($res3->items));
179
180        // not an object or array - should be returned as empty array
181        $ably->http->setResponseJSONString('"invalid"');
182        $res4 = $ably->request('GET', '/get_test_json');
183        $this->assertEquals('[]', json_encode($res4->items));
184    }
185}
186
187
188class CurlWrapperMock extends CurlWrapper {
189    public $lastParams;
190
191    public function init( $url = null ) {
192        $this->lastParams = [ CURLOPT_URL => $url ];
193
194        return parent::init( $url );
195    }
196
197    public function setOpt( $handle, $option, $value ) {
198        $this->lastParams[$option] = $value;
199
200        return parent::setOpt( $handle, $option, $value );
201    }
202
203    /**
204     * Returns a fake token when tere is `/tokenRequest` in the URL, otherwise returns current time
205     * wrapped in an array (as does GET /time) without actually making the request.
206     */
207    public function exec( $handle ) {
208        if (preg_match('/\\/tokenRequest/', $this->lastParams[CURLOPT_URL])) {
209            return 'tokentokentoken';
210        }
211
212        return '[' . round( microtime( true ) * 1000 ) . ']';
213    }
214
215    public function getInfo( $handle ) {
216        return [
217            'http_code' => 200,
218            'header_size' => 0,
219        ];
220    }
221}
222
223
224class HttpMock extends Http {
225    public function __construct() {
226        parent::__construct(new \Ably\Models\ClientOptions());
227        $this->curl = new CurlWrapperMock();
228    }
229
230    public function getCurlLastParams() {
231        return $this->curl->lastParams;
232    }
233}
234
235
236class HttpMockReturnData extends Http {
237    private $responseStr = '';
238    public function setResponseJSONString($str) {
239        $this->responseStr = $str;
240    }
241
242    public function request($method, $url, $headers = [], $params = []) {
243
244        if ($method == 'GET' && self::endsWith($url, '/get_test_json')) {
245            return [
246                'headers' => 'HTTP/1.1 200 OK'."\n",
247                'body' => json_decode($this->responseStr),
248            ];
249        } else {
250            return [
251                'headers' => 'HTTP/1.1 404 Not found'."\n",
252                'body' => '',
253            ];
254        }
255    }
256
257    private static function endsWith($haystack, $needle) {
258        return substr($haystack, -strlen($needle)) == $needle;
259    }
260}