1<?php 2 3namespace MatrixPhp\Tests; 4 5use MatrixPhp\Exceptions\MatrixException; 6use MatrixPhp\Exceptions\MatrixHttpLibException; 7use MatrixPhp\Exceptions\ValidationException; 8use GuzzleHttp\Client; 9use GuzzleHttp\Psr7\Response; 10use GuzzleHttp\Psr7\Request; 11use phpDocumentor\Reflection\DocBlock\Tags\Param; 12 13class MatrixHttpApiTest extends BaseTestCase { 14 protected $userId = "@alice:matrix.org"; 15 protected $roomId = '#foo:matrix.org'; 16 protected $token = "Dp0YKRXwx0iWDhFj7lg3DVjwsWzGcUIgARljgyAip2JD8qd5dSaWcxowTKEFetPulfLijAhv8eO" 17 . "mUSScyGcWgZyNMRTBmoJ0RFc0HotPvTBZU98yKRLtat7V43aCpFmK"; 18 protected $testPath = "/account/whoami"; 19 protected $deviceId = "QBUAZIFURK"; 20 protected $displayName = "test_name"; 21 protected $authBody = [ 22 "auth" => [ 23 "type" => "example.type.foo", 24 "session" => "xxxxx", 25 "example_credential" => "verypoorsharedsecret" 26 ] 27 ]; 28 protected $oneTimeKeys = ["curve25519:AAAAAQ" => "/qyvZvwjiTxGdGU0RCguDCLeR+nmsb3FfNG3/Ve4vU8"]; 29 protected $deviceKeys = [ 30 "user_id" => "@alice:example.com", 31 "device_id" => "JLAFKJWSCS", 32 "algorithms" => [ 33 "m.olm.curve25519-aes-sha256", 34 "m.megolm.v1.aes-sha" 35 ], 36 "keys" => [ 37 "curve25519:JLAFKJWSCS" => "3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI", 38 "ed25519:JLAFKJWSCS" => "lEuiRJBit0IG6nUf5pUzWTUEsRVVe/HJkoKuEww9ULI" 39 ], 40 "signatures" => [ 41 "@alice:example.com" => [ 42 "ed25519:JLAFKJWSCS" => ("dSO80A01XiigH3uBiDVx/EjzaoycHcjq9lfQX0uWsqxl2gi" 43 . "MIiSPR8a4d291W1ihKJL/a+myXS367WT6NAIcBA") 44 ] 45 ] 46 ]; 47 48 protected $mxurl = "mxc://example.com/OonjUOmcuVpUnmOWKtzPmAFe"; 49 /** 50 * @var MatrixHttpApi 51 */ 52 protected $api; 53 54 protected function setUp(): void 55 { 56 parent::setUp(); 57 $this->api = new MatrixHttpApi('http://example.com'); 58 } 59 60 /////////////////////////// 61 // class TestTagsApi 62 /////////////////////////// 63 public function testGetUserTags() { 64 $tagsUrl = "http://example.com/_matrix/client/r0/user/%40alice%3Amatrix.org/rooms/%23foo%3Amatrix.org/tags"; 65 $container = []; 66 $handler = $this->getMockClientHandler([new Response(200, [], '{}')], $container); 67 $this->api->setClient(new Client(['handler' => $handler])); 68 $this->api->getUserTags($this->userId, $this->roomId); 69 /** @var Request $req */ 70 $req = array_get($container, '0.request'); 71 72 $this->assertEquals('GET', $req->getMethod()); 73 $this->assertEquals($tagsUrl, (string)$req->getUri()); 74 } 75 76 public function testAddUserTag() { 77 $tagsUrl = "http://example.com/_matrix/client/r0/user/%40alice%3Amatrix.org/rooms/%23foo%3Amatrix.org/tags/foo"; 78 $container = []; 79 $handler = $this->getMockClientHandler([new Response(200, [], '{}')], $container); 80 $this->api->setClient(new Client(['handler' => $handler])); 81 $this->api->addUserTag($this->userId, $this->roomId, 'foo', null, ["order" => "5"]); 82 83 /** @var Request $req */ 84 $req = array_get($container, '0.request'); 85 86 $this->assertEquals('PUT', $req->getMethod()); 87 $this->assertEquals($tagsUrl, (string)$req->getUri()); 88 } 89 90 public function testRemoveUserTag() { 91 $tagsUrl = "http://example.com/_matrix/client/r0/user/%40alice%3Amatrix.org/rooms/%23foo%3Amatrix.org/tags/foo"; 92 $container = []; 93 $handler = $this->getMockClientHandler([new Response(200, [], '{}')], $container); 94 $this->api->setClient(new Client(['handler' => $handler])); 95 $this->api->removeUserTag($this->userId, $this->roomId, 'foo'); 96 97 /** @var Request $req */ 98 $req = array_get($container, '0.request'); 99 100 $this->assertEquals('DELETE', $req->getMethod()); 101 $this->assertEquals($tagsUrl, (string)$req->getUri()); 102 } 103 104 /////////////////////////// 105 // class TestAccountDataApi 106 /////////////////////////// 107 public function testSetAccountData() { 108 $accountDataUrl = "http://example.com/_matrix/client/r0/user/%40alice%3Amatrix.org/account_data/foo"; 109 $container = []; 110 $handler = $this->getMockClientHandler([new Response(200, [], '{}')], $container); 111 $this->api->setClient(new Client(['handler' => $handler])); 112 $this->api->setAccountData($this->userId, 'foo', ['bar' => 1]); 113 114 /** @var Request $req */ 115 $req = array_get($container, '0.request'); 116 117 $this->assertEquals('PUT', $req->getMethod()); 118 $this->assertEquals($accountDataUrl, (string)$req->getUri()); 119 } 120 121 public function testSetRoomAccountData() { 122 $accountDataUrl = 'http://example.com/_matrix/client/r0/user/%40alice%3Amatrix.org/'; 123 $accountDataUrl .= 'rooms/%23foo%3Amatrix.org/account_data/foo'; 124 $container = []; 125 $handler = $this->getMockClientHandler([new Response(200, [], '{}')], $container); 126 $this->api->setClient(new Client(['handler' => $handler])); 127 $this->api->setRoomAccountData($this->userId, $this->roomId, 'foo', ['bar' => 1]); 128 129 /** @var Request $req */ 130 $req = array_get($container, '0.request'); 131 132 $this->assertEquals('PUT', $req->getMethod()); 133 $this->assertEquals($accountDataUrl, (string)$req->getUri()); 134 } 135 136 /////////////////////////// 137 // class TestAccountDataApi 138 /////////////////////////// 139 public function testUnban() { 140 $unbanUrl = 'http://example.com/_matrix/client/r0/rooms/%23foo%3Amatrix.org/unban'; 141 $container = []; 142 $handler = $this->getMockClientHandler([new Response(200, [], '{}')], $container); 143 $this->api->setClient(new Client(['handler' => $handler])); 144 $this->api->unbanUser($this->roomId, $this->userId); 145 146 /** @var Request $req */ 147 $req = array_get($container, '0.request'); 148 149 $this->assertEquals('POST', $req->getMethod()); 150 $this->assertEquals($unbanUrl, (string)$req->getUri()); 151 } 152 153 /////////////////////////// 154 // class TestDeviceApi 155 /////////////////////////// 156 public function testGetDevices() { 157 $getDevicesUrl = "http://example.com/_matrix/client/r0/devices"; 158 $container = []; 159 $handler = $this->getMockClientHandler([new Response(200, [], '{}')], $container); 160 $this->api->setClient(new Client(['handler' => $handler])); 161 $this->api->getDevices(); 162 163 /** @var Request $req */ 164 $req = array_get($container, '0.request'); 165 166 $this->assertEquals('GET', $req->getMethod()); 167 $this->assertEquals($getDevicesUrl, (string)$req->getUri()); 168 } 169 170 public function testGetDevice() { 171 $getDevicesUrl = "http://example.com/_matrix/client/r0/devices/" . $this->deviceId; 172 $container = []; 173 $handler = $this->getMockClientHandler([new Response(200, [], '{}')], $container); 174 $this->api->setClient(new Client(['handler' => $handler])); 175 $this->api->getDevice($this->deviceId); 176 177 /** @var Request $req */ 178 $req = array_get($container, '0.request'); 179 180 $this->assertEquals('GET', $req->getMethod()); 181 $this->assertEquals($getDevicesUrl, (string)$req->getUri()); 182 } 183 184 public function testUpdateDeviceInfo() { 185 $getDevicesUrl = "http://example.com/_matrix/client/r0/devices/" . $this->deviceId; 186 $container = []; 187 $handler = $this->getMockClientHandler([new Response(200, [], '{}')], $container); 188 $this->api->setClient(new Client(['handler' => $handler])); 189 $this->api->updateDeviceInfo($this->deviceId, $this->displayName); 190 191 /** @var Request $req */ 192 $req = array_get($container, '0.request'); 193 194 $this->assertEquals('PUT', $req->getMethod()); 195 $this->assertEquals($getDevicesUrl, (string)$req->getUri()); 196 } 197 198 public function testDeleteDevice() { 199 $getDevicesUrl = "http://example.com/_matrix/client/r0/devices/" . $this->deviceId; 200 $container = []; 201 // Test for 401 status code of User-Interactive Auth API 202 $handler = $this->getMockClientHandler([new Response(401, [], '{}')], $container); 203 $this->api->setClient(new Client(['handler' => $handler])); 204 $this->expectException(MatrixHttpLibException::class); 205 206 try { 207 $this->api->deleteDevice($this->authBody, $this->deviceId); 208 } catch (MatrixHttpLibException $e) { 209 /** @var Request $req */ 210 $req = array_get($container, '0.request'); 211 212 $this->assertEquals('DELETE', $req->getMethod()); 213 $this->assertEquals($getDevicesUrl, (string)$req->getUri()); 214 215 throw $e; 216 } 217 } 218 219 public function testDeleteDevices() { 220 $getDevicesUrl = "http://example.com/_matrix/client/r0/delete_devices/"; 221 $container = []; 222 // Test for 401 status code of User-Interactive Auth API 223 $handler = $this->getMockClientHandler([new Response(401, [], '{}')], $container); 224 $this->api->setClient(new Client(['handler' => $handler])); 225 $this->expectException(MatrixHttpLibException::class); 226 227 $this->api->deleteDevices($this->authBody, [$this->deviceId]); 228 229 /** @var Request $req */ 230 $req = array_get($container, '0.request'); 231 232 $this->assertEquals('POST', $req->getMethod()); 233 $this->assertEquals($getDevicesUrl, (string)$req->getUri()); 234 } 235 236 /////////////////////////// 237 // class TestKeysApi 238 /////////////////////////// 239 /** 240 * @param array $args 241 * @throws Exceptions\MatrixRequestException 242 * @throws MatrixException 243 * @throws MatrixHttpLibException 244 * @dataProvider uploadKeysProvider 245 */ 246 public function testUploadKeys(array $args) { 247 $uploadKeysUrl = 'http://example.com/_matrix/client/r0/keys/upload'; 248 $container = []; 249 $handler = $this->getMockClientHandler([new Response(200, [], '{}')], $container); 250 $this->api->setClient(new Client(['handler' => $handler])); 251 $this->api->uploadKeys($args); 252 253 /** @var Request $req */ 254 $req = array_get($container, '0.request'); 255 256 $this->assertEquals('POST', $req->getMethod()); 257 $this->assertEquals($uploadKeysUrl, (string)$req->getUri()); 258 } 259 260 public function uploadKeysProvider(): array { 261 return [ 262 [[]], 263 [['device_keys' => $this->deviceKeys]], 264 [['one_time_keys' => $this->oneTimeKeys]], 265 ]; 266 } 267 268 public function testQueryKeys() { 269 $queryKeyUrl = 'http://example.com/_matrix/client/r0/keys/query'; 270 $container = []; 271 $handler = $this->getMockClientHandler([new Response(200, [], '{}')], $container); 272 $this->api->setClient(new Client(['handler' => $handler])); 273 $this->api->queryKeys([$this->userId => $this->deviceId], 10); 274 275 /** @var Request $req */ 276 $req = array_get($container, '0.request'); 277 278 $this->assertEquals('POST', $req->getMethod()); 279 $this->assertEquals($queryKeyUrl, (string)$req->getUri()); 280 } 281 282 public function testClaimKeys() { 283 $claimKeysUrl = 'http://example.com/_matrix/client/r0/keys/claim'; 284 $container = []; 285 $handler = $this->getMockClientHandler([new Response(200, [], '{}')], $container); 286 $this->api->setClient(new Client(['handler' => $handler])); 287 $keyRequest = [$this->userId => [$this->deviceId => 'algo']]; 288 $this->api->claimKeys($keyRequest, 1000); 289 290 /** @var Request $req */ 291 $req = array_get($container, '0.request'); 292 293 $this->assertEquals('POST', $req->getMethod()); 294 $this->assertEquals($claimKeysUrl, (string)$req->getUri()); 295 } 296 297 public function testKeyChange() { 298 $keyChangeUrl = 'http://example.com/_matrix/client/r0/keys/changes'; 299 $container = []; 300 $handler = $this->getMockClientHandler([new Response(200, [], '{}')], $container); 301 $this->api->setClient(new Client(['handler' => $handler])); 302 $this->api->keyChanges('s72594_4483_1934', 's75689_5632_2435'); 303 304 /** @var Request $req */ 305 $req = array_get($container, '0.request'); 306 307 $this->assertEquals('GET', $req->getMethod()); 308 $this->assertEquals($keyChangeUrl, explode('?', (string)$req->getUri())[0]); 309 } 310 311 /////////////////////////// 312 // class TestSendToDeviceApi 313 /////////////////////////// 314 public function testSendToDevice() { 315 $txnId = $this->invokePrivateMethod($this->api, 'makeTxnId'); 316 $sendToDeviceUrl = "http://example.com/_matrix/client/r0/sendToDevice/m.new_device/" . $txnId; 317 $container = []; 318 $handler = $this->getMockClientHandler([new Response(200, [], '{}')], $container); 319 $this->api->setClient(new Client(['handler' => $handler])); 320 $payload = [$this->userId => [$this->deviceId => ['test' => 1]]]; 321 $this->api->sendToDevice('m.new_device', $payload, $txnId); 322 323 /** @var Request $req */ 324 $req = array_get($container, '0.request'); 325 326 $this->assertEquals('PUT', $req->getMethod()); 327 $this->assertEquals($sendToDeviceUrl, (string)$req->getUri()); 328 } 329 330 /////////////////////////// 331 // class TestMainApi 332 /////////////////////////// 333 public function testSendTokenHeader() { 334 $mapi = new MatrixHttpApi("http://example.com", $this->token); 335 336 $r = sprintf('{"application/json": {"user_id": "%s"}}', $this->userId); 337 $container = []; 338 $handler = $this->getMockClientHandler([new Response(200, [], $r)], $container); 339 $mapi->setClient(new Client(['handler' => $handler])); 340 341 $this->invokePrivateMethod($mapi, 'send', ['GET', $this->testPath]); 342 /** @var Request $req */ 343 $req = array_get($container, '0.request'); 344 345 $this->assertEquals('GET', $req->getMethod()); 346 $this->assertEquals(sprintf('Bearer %s', $this->token), $req->getHeader('Authorization')[0]); 347 } 348 349 public function testSendUserAgentHeader() { 350 $mapi = new MatrixHttpApi("http://example.com", $this->token); 351 352 $r = sprintf('{"application/json": {"user_id": "%s"}}', $this->userId); 353 $container = []; 354 $handler = $this->getMockClientHandler([new Response(200, [], $r)], $container); 355 $mapi->setClient(new Client(['handler' => $handler])); 356 357 $this->invokePrivateMethod($mapi, 'send', ['GET', $this->testPath]); 358 /** @var Request $req */ 359 $req = array_get($container, '0.request'); 360 361 $this->assertEquals('GET', $req->getMethod()); 362 $this->assertEquals('php-matrix-sdk/' . $mapi::VERSION, $req->getHeader('User-Agent')[0]); 363 } 364 365 public function testSendTokenQuery() { 366 $mapi = new MatrixHttpApi("http://example.com", $this->token, null, 500, false); 367 368 $r = sprintf('{"application/json": {"user_id": "%s"}}', $this->userId); 369 $container = []; 370 $handler = $this->getMockClientHandler([new Response(200, [], $r)], $container); 371 $mapi->setClient(new Client(['handler' => $handler])); 372 373 $this->invokePrivateMethod($mapi, 'send', ['GET', $this->testPath]); 374 /** @var Request $req */ 375 $req = array_get($container, '0.request'); 376 377 $this->assertEquals('GET', $req->getMethod()); 378 $this->assertStringContainsString($this->token, $req->getRequestTarget()); 379 } 380 381 public function testSendUserId() { 382 $mapi = new MatrixHttpApi("http://example.com", $this->token, $this->userId); 383 384 $r = sprintf('{"application/json": {"user_id": "%s"}}', $this->userId); 385 $container = []; 386 $handler = $this->getMockClientHandler([new Response(200, [], $r)], $container); 387 $mapi->setClient(new Client(['handler' => $handler])); 388 389 $this->invokePrivateMethod($mapi, 'send', ['GET', $this->testPath]); 390 /** @var Request $req */ 391 $req = array_get($container, '0.request'); 392 393 $this->assertEquals('GET', $req->getMethod()); 394 $this->assertStringContainsString(urlencode($this->userId), $req->getRequestTarget()); 395 } 396 397 public function testSendUnsupMethod() { 398 $this->expectException(MatrixException::class); 399 $mapi = new MatrixHttpApi("http://example.com", $this->token, $this->userId); 400 $this->invokePrivateMethod($mapi, 'send', ['GOT', $this->testPath]); 401 } 402 403 public function testSendRequestError() { 404 $this->expectException(MatrixHttpLibException::class); 405 $mapi = new MatrixHttpApi("http://example.com"); 406 $this->invokePrivateMethod($mapi, 'send', ['GET', $this->testPath]); 407 } 408 409 /////////////////////////// 410 // class TestMediaApi 411 /////////////////////////// 412 public function testMediaDownload() { 413 $dlUrl = "http://example.com/_matrix/media/r0/download/" . substr($this->mxurl, 6); 414 $container = []; 415 $response = new Response(200, [ 416 'Content-Type' => 'application/php' 417 ], file_get_contents('./tests/TestHelper.php')); 418 $handler = $this->getMockClientHandler([$response], $container); 419 $this->api->setClient(new Client(['handler' => $handler])); 420 $this->api->mediaDownload($this->mxurl, false); 421 422 /** @var Request $req */ 423 $req = array_get($container, '0.request'); 424 425 $this->assertEquals('GET', $req->getMethod()); 426 $this->assertEquals($dlUrl, explode('?', (string)$req->getUri())[0]); 427 } 428 429 public function testMediaDownloadWrongUrl() { 430 $this->expectException(ValidationException::class); 431 432 $this->api->mediaDownload(substr($this->mxurl, 6)); 433 } 434 435 public function testGetThumbnail() { 436 $dlUrl = "http://example.com/_matrix/media/r0/thumbnail/" . substr($this->mxurl, 6); 437 $container = []; 438 $response = new Response(200, [ 439 'Content-Type' => 'application/php' 440 ], file_get_contents('./tests/TestHelper.php')); 441 $handler = $this->getMockClientHandler([$response], $container); 442 $this->api->setClient(new Client(['handler' => $handler])); 443 $this->api->getThumbnail($this->mxurl, 28, 28, 'scale', false); 444 445 /** @var Request $req */ 446 $req = array_get($container, '0.request'); 447 448 $this->assertEquals('GET', $req->getMethod()); 449 $this->assertEquals($dlUrl, explode('?', (string)$req->getUri())[0]); 450 } 451 452 public function testThumbnailWrongUrl() { 453 $this->expectException(ValidationException::class); 454 455 $this->api->getThumbnail(substr($this->mxurl, 6), 28, 28); 456 } 457 458 public function testThumbnailWrongMethod() { 459 $this->expectException(ValidationException::class); 460 461 $this->api->getThumbnail($this->mxurl, 28, 28, 'cut', false); 462 } 463 464 public function testGetUrlPreview() { 465 $mediaUrl = "http://example.com/_matrix/media/r0/preview_url"; 466 $r = json_encode(TestHelper::EXAMPLE_PREVIEW_URL); 467 $container = []; 468 $handler = $this->getMockClientHandler([new Response(200, [], $r)], $container); 469 $this->api->setClient(new Client(['handler' => $handler])); 470 $this->api->getUrlPreview("https://google.com/", 1510610716656); 471 472 /** @var Request $req */ 473 $req = array_get($container, '0.request'); 474 475 $this->assertEquals('GET', $req->getMethod()); 476 $this->assertEquals($mediaUrl, explode('?', (string)$req->getUri())[0]); 477 } 478 479 480 /////////////////////////// 481 // class TestRoomApi 482 /////////////////////////// 483 /** 484 * @dataProvider createRoomVisibilityProvider 485 */ 486 public function testCreateRoomVisibility(bool $isPublic, string $visibility) { 487 $createRoomUrl = "http://example.com/_matrix/client/r0/createRoom"; 488 $container = []; 489 $r = '{"room_id": "!sefiuhWgwghwWgh:example.com"}'; 490 $handler = $this->getMockClientHandler([new Response(200, [], $r)], $container); 491 $this->api->setClient(new Client(['handler' => $handler])); 492 $this->api->createRoom("#test:example.com", 'test', $isPublic); 493 494 /** @var Request $req */ 495 $req = array_get($container, '0.request'); 496 497 $this->assertEquals('POST', $req->getMethod()); 498 $this->assertEquals($createRoomUrl, (string)$req->getUri()); 499 $body = json_decode($req->getBody()->getContents(), true); 500 $this->assertEquals("#test:example.com", $body['room_alias_name']); 501 $this->assertEquals($visibility, $body['visibility']); 502 $this->assertEquals('test', $body['name']); 503 } 504 505 public function createRoomVisibilityProvider(): array { 506 return [ 507 [true, 'public'], 508 [false, 'private'], 509 ]; 510 } 511 512 /** 513 * @dataProvider createRoomFederationProvider 514 */ 515 public function testCreateRoomFederation(bool $isFederated) { 516 $createRoomUrl = "http://example.com/_matrix/client/r0/createRoom"; 517 $container = []; 518 $r = '{"room_id": "!sefiuhWgwghwWgh:example.com"}'; 519 $handler = $this->getMockClientHandler([new Response(200, [], $r)], $container); 520 $this->api->setClient(new Client(['handler' => $handler])); 521 522 $this->api->createRoom("#test:example.com", 'test', false, [], $isFederated); 523 /** @var Request $req */ 524 $req = array_get($container, '0.request'); 525 526 $this->assertEquals('POST', $req->getMethod()); 527 $this->assertEquals($createRoomUrl, (string)$req->getUri()); 528 $body = json_decode($req->getBody()->getContents(), true); 529 $this->assertEquals("#test:example.com", $body['room_alias_name']); 530 $this->assertEquals($isFederated, array_key_exists('m.federate', array_get($body, 'creation_content', []))); 531 } 532 533 534 public function createRoomFederationProvider(): array { 535 return [ 536 [true], 537 [false], 538 ]; 539 } 540 541 /////////////////////////// 542 // class TestRoomApi 543 /////////////////////////// 544 public function testWhoami() { 545 $mapi = new MatrixHttpApi("http://example.com", $this->token); 546 $whoamiUrl = "http://example.com/_matrix/client/r0/account/whoami"; 547 548 $r = sprintf('{"user_id": "%s"}', $this->userId); 549 $container = []; 550 $handler = $this->getMockClientHandler([new Response(200, [], $r)], $container); 551 $mapi->setClient(new Client(['handler' => $handler])); 552 553 $mapi->whoami(); 554 /** @var Request $req */ 555 $req = array_get($container, '0.request'); 556 557 $this->assertEquals('GET', $req->getMethod()); 558 $this->assertStringContainsString($req->getRequestTarget(), $whoamiUrl); 559 } 560 561 public function testWhoamiUnauth() { 562 $this->expectException(MatrixException::class); 563 564 $r = sprintf('{"user_id": "%s"}', $this->userId); 565 $container = []; 566 $handler = $this->getMockClientHandler([new Response(200, [], $r)], $container); 567 $this->api->setClient(new Client(['handler' => $handler])); 568 569 $this->api->whoami(); 570 } 571 572 573} 574