calls); $handlerStack = HandlerStack::create($mock); $handlerStack->push($history); parent::__construct(['handler' => $handlerStack]); } } /** * Test Api class * * @package Ovh * @category Ovh */ class ApiTest extends TestCase { /** * Get private and protected property to unit test it * * @param string $name * * @return \ReflectionProperty */ protected static function getPrivateProperty($name) { $class = new \ReflectionClass('Ovh\Api'); $property = $class->getProperty($name); $property->setAccessible(true); return $property; } /** * Test missing $application_key */ public function testMissingApplicationKey() { $this->expectException(InvalidParameterException::class); $this->expectExceptionMessage('Application key parameter is empty'); $api = new Api(null, MOCK_APPLICATION_SECRET, 'ovh-eu', MOCK_CONSUMER_KEY, new MockClient()); $api->get('/me'); } /** * Test missing $application_secret */ public function testMissingApplicationSecret() { $this->expectException(InvalidParameterException::class); $this->expectExceptionMessage('Application secret parameter is empty'); $api = new Api(MOCK_APPLICATION_KEY, null, 'ovh-eu', MOCK_CONSUMER_KEY, new MockClient()); $api->get('/me'); } /** * Test we don't check Application Key for unauthenticated call */ public function testNoCheckAppKeyForUnauthCall() { $client = new MockClient(new Response(200, [], '{}')); $api = new Api(null, null, 'ovh-eu', null, $client); $api->get("/me", null, null, false); $calls = $client->calls; $this->assertCount(1, $calls); $req = $calls[0]['request']; $this->assertSame('GET', $req->getMethod()); $this->assertSame('https://eu.api.ovh.com/1.0/me', $req->getUri()->__toString()); $this->assertSame('', $req->getHeaderLine('X-Ovh-Application')); } /** * Test missing $api_endpoint */ public function testMissingApiEndpoint() { $this->expectException(InvalidParameterException::class); $this->expectExceptionMessage('Endpoint parameter is empty'); new Api(MOCK_APPLICATION_KEY, MOCK_APPLICATION_SECRET, null, MOCK_CONSUMER_KEY, new MockClient()); } /** * Test bad $api_endpoint */ public function testBadApiEndpoint() { $this->expectException(InvalidParameterException::class); $this->expectExceptionMessage('Unknown provided endpoint'); new Api(MOCK_APPLICATION_KEY, MOCK_APPLICATION_SECRET, 'i_am_invalid', MOCK_CONSUMER_KEY, new MockClient()); } /** * Test creating Client if none is provided */ public function testClientCreation() { $api = new Api(MOCK_APPLICATION_KEY, MOCK_APPLICATION_SECRET, 'ovh-eu', MOCK_CONSUMER_KEY); $this->assertInstanceOf('\\GuzzleHttp\\Client', $api->getHttpClient()); } /** * Test the compute of time delta */ public function testTimeDeltaCompute() { $client = new MockClient( new Response(200, [], time() - 10), new Response(200, [], '{}'), ); $api = new Api(MOCK_APPLICATION_KEY, MOCK_APPLICATION_SECRET, 'ovh-eu', MOCK_CONSUMER_KEY, $client); $api->get("/me"); $property = self::getPrivateProperty('time_delta'); $time_delta = $property->getValue($api); $this->assertSame('-10', $time_delta); $calls = $client->calls; $this->assertCount(2, $calls); $req = $calls[0]['request']; $this->assertSame('GET', $req->getMethod()); $this->assertSame('https://eu.api.ovh.com/1.0/auth/time', $req->getUri()->__toString()); $req = $calls[1]['request']; $this->assertSame('GET', $req->getMethod()); $this->assertSame('https://eu.api.ovh.com/1.0/me', $req->getUri()->__toString()); } /** * Test if consumer key is replaced */ public function testIfConsumerKeyIsReplace() { $client = new MockClient( new Response(200, [], MOCK_TIME), new Response( 200, ['Content-Type' => 'application/json; charset=utf-8'], '{"validationUrl":"https://api.ovh.com/login/?credentialToken=token","consumerKey":"consumer_remote","state":"pendingValidation"}' ), ); $api = new Api(MOCK_APPLICATION_KEY, MOCK_APPLICATION_SECRET, 'ovh-eu', MOCK_CONSUMER_KEY, $client); $this->assertNotEquals('consumer_remote', $api->getConsumerKey()); $credentials = $api->requestCredentials(['method' => 'GET', 'path' => '/*']); $this->assertSame('consumer_remote', $api->getConsumerKey()); $calls = $client->calls; $this->assertCount(2, $calls); $req = $calls[0]['request']; $this->assertSame('GET', $req->getMethod()); $this->assertSame('https://eu.api.ovh.com/1.0/auth/time', $req->getUri()->__toString()); $req = $calls[1]['request']; $this->assertSame('POST', $req->getMethod()); $this->assertSame('https://eu.api.ovh.com/1.0/auth/credential', $req->getUri()->__toString()); } /** * Test invalid applicationKey */ public function testInvalidApplicationKey() { $error = '{"class":"Client::Forbidden","message":"Invalid application key"}'; $this->expectException(ClientException::class); $this->expectExceptionCode(403); $this->expectExceptionMessage($error); $client = new MockClient(new Response(403, ['Content-Type' => 'application/json; charset=utf-8'], $error)); $api = new Api(MOCK_APPLICATION_KEY, MOCK_APPLICATION_SECRET, 'ovh-eu', MOCK_CONSUMER_KEY, $client); $api->requestCredentials(['method' => 'GET', 'path' => '/*']); } /** * Test invalid rights */ public function testInvalidRight() { $error = '{"message": "Invalid credentials"}'; $this->expectException(ClientException::class); $this->expectExceptionCode(403); $this->expectExceptionMessage($error); $client = new MockClient(new Response(403, ['Content-Type' => 'application/json; charset=utf-8'], $error)); $api = new Api(MOCK_APPLICATION_KEY, MOCK_APPLICATION_SECRET, 'ovh-eu', MOCK_CONSUMER_KEY, $client); $api->get('/me', null, null, false); } public function testGetConsumerKey() { $api = new Api(MOCK_APPLICATION_KEY, MOCK_APPLICATION_SECRET, 'ovh-eu', MOCK_CONSUMER_KEY); $this->assertSame(MOCK_CONSUMER_KEY, $api->getConsumerKey()); } /** * Test GET query args */ public function testGetQueryArgs() { $client = new MockClient(new Response(200, [], '{}')); $api = new Api(MOCK_APPLICATION_KEY, MOCK_APPLICATION_SECRET, 'ovh-eu', MOCK_CONSUMER_KEY, $client); $api->get('/me/api/credential?applicationId=49', ['status' => 'pendingValidation'], null, false); $calls = $client->calls; $this->assertCount(1, $calls); $req = $calls[0]['request']; $this->assertSame('GET', $req->getMethod()); $this->assertSame('https://eu.api.ovh.com/1.0/me/api/credential?applicationId=49&status=pendingValidation', $req->getUri()->__toString()); } /** * Test GET overlapping query args */ public function testGetOverlappingQueryArgs() { $client = new MockClient(new Response(200, [], '{}')); $api = new Api(MOCK_APPLICATION_KEY, MOCK_APPLICATION_SECRET, 'ovh-eu', MOCK_CONSUMER_KEY, $client); $api->get('/me/api/credential?applicationId=49&status=pendingValidation', ['status' => 'expired', 'test' => "success"], null, false); $calls = $client->calls; $this->assertCount(1, $calls); $req = $calls[0]['request']; $this->assertSame('GET', $req->getMethod()); $this->assertSame('https://eu.api.ovh.com/1.0/me/api/credential?applicationId=49&status=expired&test=success', $req->getUri()->__toString()); } /** * Test GET boolean query args */ public function testGetBooleanQueryArgs() { $client = new MockClient(new Response(200, [], '{}')); $api = new Api(MOCK_APPLICATION_KEY, MOCK_APPLICATION_SECRET, 'ovh-eu', MOCK_CONSUMER_KEY, $client); $api->get('/me/api/credential', ['dryRun' => true, 'notDryRun' => false], null, false); $calls = $client->calls; $this->assertCount(1, $calls); $req = $calls[0]['request']; $this->assertSame('GET', $req->getMethod()); $this->assertSame('https://eu.api.ovh.com/1.0/me/api/credential?dryRun=true¬DryRun=false', $req->getUri()->__toString()); } /** * Test valid provided endpoint */ public function testProvidedUrl() { foreach ([ ['endpoint' => 'http://api.ovh.com/1.0', 'expectedUrl' => 'http://api.ovh.com/1.0'], ['endpoint' => 'https://api.ovh.com/1.0', 'expectedUrl' => 'https://api.ovh.com/1.0'], ['endpoint' => 'ovh-eu', 'expectedUrl' => 'https://eu.api.ovh.com/1.0'], ] as $test) { $client = new MockClient(new Response(200, [], '{}')); $api = new Api(MOCK_APPLICATION_KEY, MOCK_APPLICATION_SECRET, $test['endpoint'], MOCK_CONSUMER_KEY, $client); $api->get('/me/api/credential', null, null, false); $calls = $client->calls; $this->assertCount(1, $calls); $req = $calls[0]['request']; $this->assertSame('GET', $req->getMethod()); $this->assertSame($test['expectedUrl'] . '/me/api/credential', $req->getUri()->__toString()); } } /** * Test missing header X-OVH-Application on requestCredentials */ public function testMissingOvhApplicationHeaderOnRequestCredentials() { $client = new MockClient( new Response(200, [], MOCK_TIME), new Response(200, [], '{"validationUrl":"https://api.ovh.com/login/?credentialToken=token","consumerKey":"consumer_remote","state":"pendingValidation"}'), ); $api = new Api(MOCK_APPLICATION_KEY, MOCK_APPLICATION_SECRET, 'ovh-eu', MOCK_CONSUMER_KEY, $client); $api->requestCredentials([]); $calls = $client->calls; $this->assertCount(2, $calls); $req = $calls[0]['request']; $this->assertSame('GET', $req->getMethod()); $this->assertSame('https://eu.api.ovh.com/1.0/auth/time', $req->getUri()->__toString()); $req = $calls[1]['request']; $this->assertSame('POST', $req->getMethod()); $this->assertSame('https://eu.api.ovh.com/1.0/auth/credential', $req->getUri()->__toString()); $this->assertSame(MOCK_APPLICATION_KEY, $req->getHeaderLine('X-Ovh-Application')); $this->assertSame(MOCK_CONSUMER_KEY, $req->getHeaderLine('X-Ovh-Consumer')); $this->assertSame(MOCK_TIME, $req->getHeaderLine('X-Ovh-Timestamp')); } public function testCallSignature() { // GET /auth/time $mocks = [new Response(200, [], MOCK_TIME)]; // (GET,POST,PUT,DELETE) x (/auth,/unauth) for ($i = 0; $i < 8; $i++) { $mocks[] = new Response(200, [], '{}'); } $client = new MockClient(...$mocks); $api = new Api(MOCK_APPLICATION_KEY, MOCK_APPLICATION_SECRET, 'ovh-eu', MOCK_CONSUMER_KEY, $client); $body = ["a" => "b", "c" => "d"]; // Authenticated calls $api->get('/auth'); $api->post('/auth', $body); $api->put('/auth', $body); $api->delete('/auth'); // Unauth calls $api->get('/unauth', null, null, false); $api->post('/unauth', $body, null, false); $api->put('/unauth', $body, null, false); $api->delete('/unauth', null, null, false); $calls = $client->calls; $this->assertCount(9, $calls); $req = $calls[0]['request']; $this->assertSame('GET', $req->getMethod()); $this->assertSame('https://eu.api.ovh.com/1.0/auth/time', $req->getUri()->__toString()); foreach ([ 1 => ['method' => 'GET', 'sig' => '$1$e9556054b6309771395efa467c22e627407461ad'], 2 => ['method' => 'POST', 'sig' => '$1$ec2fb5c7a81f64723c77d2e5b609ae6f58a84fc1'], 3 => ['method' => 'PUT', 'sig' => '$1$8a75a9e7c8e7296c9dbeda6a2a735eb6bd58ec4b'], 4 => ['method' => 'DELETE', 'sig' => '$1$a1eecd00b3b02b6cf5708b84b9ff42059a950d85'], ] as $i => $test) { $req = $calls[$i]['request']; $this->assertSame($test['method'], $req->getMethod()); $this->assertSame('https://eu.api.ovh.com/1.0/auth', $req->getUri()->__toString()); $this->assertSame(MOCK_APPLICATION_KEY, $req->getHeaderLine('X-Ovh-Application')); $this->assertSame(MOCK_CONSUMER_KEY, $req->getHeaderLine('X-Ovh-Consumer')); $this->assertSame(MOCK_TIME, $req->getHeaderLine('X-Ovh-Timestamp')); $this->assertSame($test['sig'], $req->getHeaderLine('X-Ovh-Signature')); if ($test['method'] == 'POST' || $test['method'] == 'PUT') { $this->assertSame('application/json; charset=utf-8', $req->getHeaderLine('Content-Type')); } } foreach (['GET', 'POST', 'PUT', 'DELETE'] as $i => $method) { $req = $calls[$i + 5]['request']; $this->assertSame($method, $req->getMethod()); $this->assertSame('https://eu.api.ovh.com/1.0/unauth', $req->getUri()->__toString()); $this->assertSame(MOCK_APPLICATION_KEY, $req->getHeaderLine('X-Ovh-Application')); $this->assertNotTrue($req->hasHeader('X-Ovh-Consumer')); $this->assertNotTrue($req->hasHeader('X-Ovh-Timestamp')); $this->assertNotTrue($req->hasHeader('X-Ovh-Signature')); if ($method == 'POST' || $method == 'PUT') { $this->assertSame('application/json; charset=utf-8', $req->getHeaderLine('Content-Type')); } } } public function testVersionInUrl() { // GET /auth/time $mocks = [new Response(200, [], MOCK_TIME)]; // GET) x (/1.0/call,/v1/call,/v2/call) for ($i = 0; $i < 3; $i++) { $mocks[] = new Response(200, [], '{}'); } $client = new MockClient(...$mocks); $api = new Api(MOCK_APPLICATION_KEY, MOCK_APPLICATION_SECRET, 'ovh-eu', MOCK_CONSUMER_KEY, $client); $api->get('/call'); $api->get('/v1/call'); $api->get('/v2/call'); $calls = $client->calls; $this->assertCount(4, $calls); $req = $calls[0]['request']; $this->assertSame('GET', $req->getMethod()); $this->assertSame('https://eu.api.ovh.com/1.0/auth/time', $req->getUri()->__toString()); foreach ([ 1 => ['path' => '1.0/call', 'sig' => '$1$7f2db49253edfc41891023fcd1a54cf61db05fbb'], 2 => ['path' => 'v1/call', 'sig' => '$1$e6e7906d385eb28adcbfbe6b66c1528a42d741ad'], 3 => ['path' => 'v2/call', 'sig' => '$1$bb63b132a6f84ad5433d0c534d48d3f7c3804285'], ] as $i => $test) { $req = $calls[$i]['request']; $this->assertSame('GET', $req->getMethod()); $this->assertSame('https://eu.api.ovh.com/' . $test['path'], $req->getUri()->__toString()); $this->assertSame(MOCK_APPLICATION_KEY, $req->getHeaderLine('X-Ovh-Application')); $this->assertSame(MOCK_CONSUMER_KEY, $req->getHeaderLine('X-Ovh-Consumer')); $this->assertSame(MOCK_TIME, $req->getHeaderLine('X-Ovh-Timestamp')); $this->assertSame($test['sig'], $req->getHeaderLine('X-Ovh-Signature')); } } }