6 namespace Friendica\Test\legacy;
9 use Friendica\Core\Config\Capability\IManageConfigValues;
11 use Friendica\Module\BaseApi;
12 use Friendica\Security\BasicAuth;
13 use Friendica\Test\FixtureTest;
14 use Friendica\Util\Arrays;
15 use Friendica\Util\DateTimeFormat;
16 use Monolog\Handler\TestHandler;
18 require_once __DIR__ . '/../../include/api.php';
21 * Tests for the API functions.
23 * Functions that use header() need to be tested in a separate process.
24 * @see https://phpunit.de/manual/5.7/en/appendixes.annotations.html#appendixes.annotations.runTestsInSeparateProcesses
26 * @backupGlobals enabled
28 class ApiTest extends FixtureTest
31 * @var TestHandler Can handle log-outputs
38 protected $friendUser;
42 protected $wrongUserId;
47 /** @var IManageConfigValues */
51 * Create variables used by tests.
53 protected function setUp() : void
55 global $API, $called_api;
61 /** @var IManageConfigValues $config */
62 $this->config = $this->dice->create(IManageConfigValues::class);
64 $this->config->set('system', 'url', 'http://localhost');
65 $this->config->set('system', 'hostname', 'localhost');
66 $this->config->set('system', 'worker_dont_fork', true);
69 $this->config->set('config', 'hostname', 'localhost');
70 $this->config->set('system', 'throttle_limit_day', 100);
71 $this->config->set('system', 'throttle_limit_week', 100);
72 $this->config->set('system', 'throttle_limit_month', 100);
73 $this->config->set('system', 'theme', 'system_theme');
77 $this->app = DI::app();
79 DI::args()->setArgc(1);
81 // User data that the test database is populated with
84 'name' => 'Self contact',
85 'nick' => 'selfcontact',
86 'nurl' => 'http://localhost/profile/selfcontact'
90 'name' => 'Friend contact',
91 'nick' => 'friendcontact',
92 'nurl' => 'http://localhost/profile/friendcontact'
96 'name' => 'othercontact',
97 'nick' => 'othercontact',
98 'nurl' => 'http://localhost/profile/othercontact'
101 // User ID that we know is not in the database
102 $this->wrongUserId = 666;
104 DI::session()->start();
106 // Most API require login so we force the session
108 'authenticated' => true,
109 'uid' => $this->selfUser['id']
111 BasicAuth::setCurrentUserID($this->selfUser['id']);
115 * Assert that a list array contains expected keys.
117 * @param array $list List array
121 private function assertList(array $list = [])
123 self::assertIsString($list['name']);
124 self::assertIsInt($list['id']);
125 self::assertIsString('string', $list['id_str']);
126 self::assertContains($list['mode'], ['public', 'private']);
127 // We could probably do more checks here.
131 * Assert that the string is XML and contain the root element.
133 * @param string $result XML string
134 * @param string $root_element Root element name
138 private function assertXml($result = '', $root_element = '')
140 self::assertStringStartsWith('<?xml version="1.0"?>', $result);
141 self::assertStringContainsString('<' . $root_element, $result);
142 // We could probably do more checks here.
146 * Test the api_user() function.
150 public function testApiUser()
152 self::assertEquals($this->selfUser['id'], BaseApi::getCurrentUserID());
158 * Test the api_source() function.
162 public function testApiSource()
164 self::assertEquals('api', BasicAuth::getCurrentApplicationToken()['name']);
168 * Test the api_source() function with a Twidere user agent.
172 public function testApiSourceWithTwidere()
174 $_SERVER['HTTP_USER_AGENT'] = 'Twidere';
175 self::assertEquals('Twidere', BasicAuth::getCurrentApplicationToken()['name']);
179 * Test the api_source() function with a GET parameter.
183 public function testApiSourceWithGet()
185 $_REQUEST['source'] = 'source_name';
186 self::assertEquals('source_name', BasicAuth::getCurrentApplicationToken()['name']);
190 * Test the api_date() function.
194 public function testApiDate()
196 self::assertEquals('Wed Oct 10 00:00:00 +0000 1990', DateTimeFormat::utc('1990-10-10', DateTimeFormat::API));
200 * Test the api_register_func() function.
204 public function testApiRegisterFunc()
216 self::assertTrue(is_callable($API['api_path']['func']));
220 * Test the BasicAuth::getCurrentUserID() function without any login.
222 * @runInSeparateProcess
223 * @preserveGlobalState disabled
224 * @preserveGlobalState disabled
226 public function testApiLoginWithoutLogin()
228 BasicAuth::setCurrentUserID();
229 $this->expectException(\Friendica\Network\HTTPException\UnauthorizedException::class);
230 BasicAuth::getCurrentUserID(true);
234 * Test the BasicAuth::getCurrentUserID() function with a bad login.
236 * @runInSeparateProcess
237 * @preserveGlobalState disabled
238 * @preserveGlobalState disabled
240 public function testApiLoginWithBadLogin()
242 BasicAuth::setCurrentUserID();
243 $this->expectException(\Friendica\Network\HTTPException\UnauthorizedException::class);
244 $_SERVER['PHP_AUTH_USER'] = 'user@server';
245 BasicAuth::getCurrentUserID(true);
249 * Test the BasicAuth::getCurrentUserID() function with oAuth.
253 public function testApiLoginWithOauth()
255 $this->markTestIncomplete('Can we test this easily?');
259 * Test the BasicAuth::getCurrentUserID() function with authentication provided by an addon.
263 public function testApiLoginWithAddonAuth()
265 $this->markTestIncomplete('Can we test this easily?');
269 * Test the BasicAuth::getCurrentUserID() function with a correct login.
271 * @runInSeparateProcess
272 * @preserveGlobalState disabled
273 * @doesNotPerformAssertions
275 public function testApiLoginWithCorrectLogin()
277 BasicAuth::setCurrentUserID();
278 $_SERVER['PHP_AUTH_USER'] = 'Test user';
279 $_SERVER['PHP_AUTH_PW'] = 'password';
280 BasicAuth::getCurrentUserID(true);
284 * Test the BasicAuth::getCurrentUserID() function with a remote user.
286 * @runInSeparateProcess
287 * @preserveGlobalState disabled
289 public function testApiLoginWithRemoteUser()
291 BasicAuth::setCurrentUserID();
292 $this->expectException(\Friendica\Network\HTTPException\UnauthorizedException::class);
293 $_SERVER['REDIRECT_REMOTE_USER'] = '123456dXNlcjpwYXNzd29yZA==';
294 BasicAuth::getCurrentUserID(true);
298 * Test the api_call() function.
300 * @runInSeparateProcess
301 * @preserveGlobalState disabled
303 public function testApiCall()
307 'method' => 'method',
308 'func' => function () {
309 return ['data' => ['some_data']];
312 $_SERVER['REQUEST_METHOD'] = 'method';
313 $_SERVER['QUERY_STRING'] = 'pagename=api_path';
314 $_GET['callback'] = 'callback_name';
317 'callback_name(["some_data"])',
318 api_call('api_path', 'json')
323 * Test the api_call() function with the profiled enabled.
325 * @runInSeparateProcess
326 * @preserveGlobalState disabled
328 public function testApiCallWithProfiler()
332 'method' => 'method',
333 'func' => function () {
334 return ['data' => ['some_data']];
338 $_SERVER['REQUEST_METHOD'] = 'method';
339 $_SERVER['QUERY_STRING'] = 'pagename=api_path';
341 $this->config->set('system', 'profiler', true);
342 $this->config->set('rendertime', 'callstack', true);
343 $this->app->callstack = [
344 'database' => ['some_function' => 200],
345 'database_write' => ['some_function' => 200],
346 'cache' => ['some_function' => 200],
347 'cache_write' => ['some_function' => 200],
348 'network' => ['some_function' => 200]
353 api_call('api_path', 'json')
358 * Test the api_call() function with a JSON result.
360 * @runInSeparateProcess
361 * @preserveGlobalState disabled
363 public function testApiCallWithJson()
367 'method' => 'method',
368 'func' => function () {
369 return ['data' => ['some_data']];
372 $_SERVER['REQUEST_METHOD'] = 'method';
373 $_SERVER['QUERY_STRING'] = 'pagename=api_path.json';
377 api_call('api_path.json', 'json')
382 * Test the api_call() function with an XML result.
384 * @runInSeparateProcess
385 * @preserveGlobalState disabled
387 public function testApiCallWithXml()
391 'method' => 'method',
392 'func' => function () {
396 $_SERVER['REQUEST_METHOD'] = 'method';
397 $_SERVER['QUERY_STRING'] = 'pagename=api_path.xml';
399 $args = DI::args()->determine($_SERVER, $_GET);
403 api_call('api_path.xml', 'xml')
408 * Test the api_call() function with an RSS result.
410 * @runInSeparateProcess
411 * @preserveGlobalState disabled
413 public function testApiCallWithRss()
417 'method' => 'method',
418 'func' => function () {
422 $_SERVER['REQUEST_METHOD'] = 'method';
423 $_SERVER['QUERY_STRING'] = 'pagename=api_path.rss';
426 '<?xml version="1.0" encoding="UTF-8"?>' . "\n" .
428 api_call('api_path.rss', 'rss')
433 * Test the api_call() function with an Atom result.
435 * @runInSeparateProcess
436 * @preserveGlobalState disabled
438 public function testApiCallWithAtom()
442 'method' => 'method',
443 'func' => function () {
447 $_SERVER['REQUEST_METHOD'] = 'method';
448 $_SERVER['QUERY_STRING'] = 'pagename=api_path.atom';
451 '<?xml version="1.0" encoding="UTF-8"?>' . "\n" .
453 api_call('api_path.atom', 'atom')
458 * Test the api_rss_extra() function.
462 public function testApiRssExtra()
465 $user_info = ['url' => 'user_url', 'lang' => 'en'];
466 $result = api_rss_extra([], $user_info);
467 self::assertEquals($user_info, $result['$user']);
468 self::assertEquals($user_info['url'], $result['$rss']['alternate']);
469 self::assertArrayHasKey('self', $result['$rss']);
470 self::assertArrayHasKey('base', $result['$rss']);
471 self::assertArrayHasKey('updated', $result['$rss']);
472 self::assertArrayHasKey('atom_updated', $result['$rss']);
473 self::assertArrayHasKey('language', $result['$rss']);
474 self::assertArrayHasKey('logo', $result['$rss']);
479 * Test the api_rss_extra() function without any user info.
483 public function testApiRssExtraWithoutUserInfo()
486 $result = api_rss_extra([], null);
487 self::assertIsArray($result['$user']);
488 self::assertArrayHasKey('alternate', $result['$rss']);
489 self::assertArrayHasKey('self', $result['$rss']);
490 self::assertArrayHasKey('base', $result['$rss']);
491 self::assertArrayHasKey('updated', $result['$rss']);
492 self::assertArrayHasKey('atom_updated', $result['$rss']);
493 self::assertArrayHasKey('language', $result['$rss']);
494 self::assertArrayHasKey('logo', $result['$rss']);
499 * Test the Arrays::walkRecursive() function.
503 public function testApiWalkRecursive()
508 Arrays::walkRecursive(
511 // Should we test this with a callback that actually does something?
519 * Test the Arrays::walkRecursive() function with an array.
523 public function testApiWalkRecursiveWithArray()
525 $array = [['item1'], ['item2']];
528 Arrays::walkRecursive(
531 // Should we test this with a callback that actually does something?
541 * Test the api_format_items_embeded_images() function.
545 public function testApiFormatItemsEmbededImages()
549 'text ' . DI::baseUrl() . '/display/item_guid',
550 api_format_items_embeded_images(['guid' => 'item_guid'], 'text data:image/foo')
556 * Test the api_format_items() function.
557 * @doesNotPerformAssertions
559 public function testApiFormatItems()
562 $items = Post::selectToArray([], ['uid' => 42]);
563 foreach ($items as $item) {
564 $status = api_format_item($item);
565 self::assertStatus($status);
571 * Test the api_format_items() function with an XML result.
572 * @doesNotPerformAssertions
574 public function testApiFormatItemsWithXml()
577 $items = Post::selectToArray([], ['uid' => 42]);
578 foreach ($items as $item) {
579 $status = api_format_item($item, 'xml');
580 self::assertStatus($status);
586 * Test the api_lists_list() function.
590 public function testApiListsList()
592 $result = api_lists_list('json');
593 self::assertEquals(['lists_list' => []], $result);
597 * Test the api_lists_ownerships() function.
601 public function testApiListsOwnerships()
603 $result = api_lists_ownerships('json');
604 foreach ($result['lists']['lists'] as $list) {
605 self::assertList($list);
610 * Test the api_lists_ownerships() function without an authenticated user.
614 public function testApiListsOwnershipsWithoutAuthenticatedUser()
616 $this->expectException(\Friendica\Network\HTTPException\UnauthorizedException::class);
617 BasicAuth::setCurrentUserID();
618 $_SESSION['authenticated'] = false;
619 api_lists_ownerships('json');
623 * Test the api_statuses_f() function.
627 public function testApiStatusesFWithIncoming()
629 // $result = api_statuses_f('incoming');
630 // self::assertArrayHasKey('user', $result);
634 * Test the api_fr_photos_list() function.
638 public function testApiFrPhotosList()
640 $result = api_fr_photos_list('json');
641 self::assertArrayHasKey('photo', $result);
645 * Test the api_fr_photos_list() function without an authenticated user.
649 public function testApiFrPhotosListWithoutAuthenticatedUser()
651 $this->expectException(\Friendica\Network\HTTPException\UnauthorizedException::class);
652 BasicAuth::setCurrentUserID();
653 $_SESSION['authenticated'] = false;
654 api_fr_photos_list('json');
658 * Test the api_fr_photo_create_update() function.
660 public function testApiFrPhotoCreateUpdate()
662 $this->expectException(\Friendica\Network\HTTPException\BadRequestException::class);
663 api_fr_photo_create_update('json');
667 * Test the api_fr_photo_create_update() function without an authenticated user.
671 public function testApiFrPhotoCreateUpdateWithoutAuthenticatedUser()
673 $this->expectException(\Friendica\Network\HTTPException\UnauthorizedException::class);
674 BasicAuth::setCurrentUserID();
675 $_SESSION['authenticated'] = false;
676 api_fr_photo_create_update('json');
680 * Test the api_fr_photo_create_update() function with an album name.
684 public function testApiFrPhotoCreateUpdateWithAlbum()
686 $this->expectException(\Friendica\Network\HTTPException\BadRequestException::class);
687 $_REQUEST['album'] = 'album_name';
688 api_fr_photo_create_update('json');
692 * Test the api_fr_photo_create_update() function with the update mode.
696 public function testApiFrPhotoCreateUpdateWithUpdate()
698 $this->markTestIncomplete('We need to create a dataset for this');
702 * Test the api_fr_photo_create_update() function with an uploaded file.
706 public function testApiFrPhotoCreateUpdateWithFile()
708 $this->markTestIncomplete();
712 * Test the api_fr_photo_detail() function.
716 public function testApiFrPhotoDetail()
718 $this->expectException(\Friendica\Network\HTTPException\BadRequestException::class);
719 api_fr_photo_detail('json');
723 * Test the api_fr_photo_detail() function without an authenticated user.
727 public function testApiFrPhotoDetailWithoutAuthenticatedUser()
729 $this->expectException(\Friendica\Network\HTTPException\UnauthorizedException::class);
730 BasicAuth::setCurrentUserID();
731 $_SESSION['authenticated'] = false;
732 api_fr_photo_detail('json');
736 * Test the api_fr_photo_detail() function with a photo ID.
740 public function testApiFrPhotoDetailWithPhotoId()
742 $this->expectException(\Friendica\Network\HTTPException\NotFoundException::class);
743 $_REQUEST['photo_id'] = 1;
744 api_fr_photo_detail('json');
748 * Test the api_fr_photo_detail() function with a correct photo ID.
752 public function testApiFrPhotoDetailCorrectPhotoId()
754 $this->markTestIncomplete('We need to create a dataset for this.');
758 * Test the api_account_update_profile_image() function.
762 public function testApiAccountUpdateProfileImage()
764 $this->expectException(\Friendica\Network\HTTPException\BadRequestException::class);
765 api_account_update_profile_image('json');
769 * Test the api_account_update_profile_image() function without an authenticated user.
773 public function testApiAccountUpdateProfileImageWithoutAuthenticatedUser()
775 $this->expectException(\Friendica\Network\HTTPException\UnauthorizedException::class);
776 BasicAuth::setCurrentUserID();
777 $_SESSION['authenticated'] = false;
778 api_account_update_profile_image('json');
782 * Test the api_account_update_profile_image() function with an uploaded file.
786 public function testApiAccountUpdateProfileImageWithUpload()
788 $this->expectException(\Friendica\Network\HTTPException\BadRequestException::class);
789 $this->markTestIncomplete();
793 * Test the check_acl_input() function.
797 public function testCheckAclInput()
799 $result = check_acl_input('<aclstring>', BaseApi::getCurrentUserID());
800 // Where does this result come from?
801 self::assertEquals(1, $result);
805 * Test the check_acl_input() function with an empty ACL string.
809 public function testCheckAclInputWithEmptyAclString()
811 $result = check_acl_input(' ', BaseApi::getCurrentUserID());
812 self::assertFalse($result);
816 * Test the save_media_to_database() function.
820 public function testSaveMediaToDatabase()
822 $this->markTestIncomplete();
826 * Test the post_photo_item() function.
830 public function testPostPhotoItem()
832 $this->markTestIncomplete();
836 * Test the prepare_photo_data() function.
840 public function testPreparePhotoData()
842 $this->markTestIncomplete();
846 * Test the api_clean_plain_items() function.
850 public function testApiCleanPlainItems()
852 //$_REQUEST['include_entities'] = 'true';
853 //$result = api_clean_plain_items('some_text [url="some_url"]some_text[/url]');
854 //self::assertEquals('some_text [url="some_url"]"some_url"[/url]', $result);
858 * Test the api_friendica_group_show() function.
862 public function testApiFriendicaGroupShow()
864 $this->markTestIncomplete();
868 * Test the api_lists_destroy() function.
872 public function testApiListsDestroy()
874 $this->markTestIncomplete();
878 * Test the group_create() function.
882 public function testGroupCreate()
884 $this->markTestIncomplete();
888 * Test the api_friendica_group_create() function.
892 public function testApiFriendicaGroupCreate()
894 $this->markTestIncomplete();
898 * Test the api_lists_create() function.
902 public function testApiListsCreate()
904 $this->markTestIncomplete();
908 * Test the api_lists_update() function.
912 public function testApiListsUpdate()
914 $this->markTestIncomplete();