<commit_before><?php
/**
 * ApiTest class.
 */

namespace Friendica\Test;

use Friendica\BaseObject;
use Friendica\Core\Config;
use Friendica\Core\PConfig;
use Friendica\Core\Protocol;
use Friendica\Core\System;
use Friendica\Network\HTTPException;

require_once __DIR__ . '/../../include/api.php';

/**
 * Tests for the API functions.
 *
 * Functions that use header() need to be tested in a separate process.
 * @see https://phpunit.de/manual/5.7/en/appendixes.annotations.html#appendixes.annotations.runTestsInSeparateProcesses
 */
class ApiTest extends DatabaseTest
{
	/**
	 * Create variables used by tests.
	 */
	public function setUp()
	{
		parent::setUp();

		$this->app = BaseObject::getApp();

		// User data that the test database is populated with
		$this->selfUser = [
			'id' => 42,
			'name' => '<NAME>',
			'nick' => 'selfcontact',
			'nurl' => 'http://localhost/profile/selfcontact'
		];
		$this->friendUser = [
			'id' => 44,
			'name' => '<NAME>',
			'nick' => 'friendcontact',
			'nurl' => 'http://localhost/profile/friendcontact'
		];
		$this->otherUser = [
			'id' => 43,
			'name' => 'othercontact',
			'nick' => 'othercontact',
			'nurl' => 'http://localhost/profile/othercontact'
		];

		// User ID that we know is not in the database
		$this->wrongUserId = 666;

		// Most API require login so we force the session
		$_SESSION = [
			'allow_api' => true,
			'authenticated' => true,
			'uid' => $this->selfUser['id']
		];

		Config::set('system', 'url', 'http://localhost');
		Config::set('system', 'hostname', 'localhost');
		Config::set('system', 'worker_dont_fork', true);

		// Default config
		Config::set('config', 'hostname', 'localhost');
		Config::set('system', 'throttle_limit_day', 100);
		Config::set('system', 'throttle_limit_week', 100);
		Config::set('system', 'throttle_limit_month', 100);
		Config::set('system', 'theme', 'system_theme');
	}

	/**
	 * Cleanup variables used by tests.
	 */
	protected function tearDown()
	{
		parent::tearDown();

		$this->app->argc = 1;
		$this->app->argv = ['home'];
	}

	/**
	 * Assert that an user array contains expected keys.
	 * @param array $user User array
	 * @return void
	 */
	private function assertSelfUser(array $user)
	{
		$this->assertEquals($this->selfUser['id'], $user['uid']);
		$this->assertEquals($this->selfUser['id'], $user['cid']);
		$this->assertEquals(1, $user['self']);
		$this->assertEquals('DFRN', $user['location']);
		$this->assertEquals($this->selfUser['name'], $user['name']);
		$this->assertEquals($this->selfUser['nick'], $user['screen_name']);
		$this->assertEquals('dfrn', $user['network']);
		$this->assertTrue($user['verified']);
	}

	/**
	 * Assert that an user array contains expected keys.
	 * @param array $user User array
	 * @return void
	 */
	private function assertOtherUser(array $user)
	{
		$this->assertEquals($this->otherUser['id'], $user['id']);
		$this->assertEquals($this->otherUser['id'], $user['id_str']);
		$this->assertEquals(0, $user['self']);
		$this->assertEquals($this->otherUser['name'], $user['name']);
		$this->assertEquals($this->otherUser['nick'], $user['screen_name']);
		$this->assertFalse($user['verified']);
	}

	/**
	 * Assert that a status array contains expected keys.
	 * @param array $status Status array
	 * @return void
	 */
	private function assertStatus(array $status)
	{
		$this->assertInternalType('string', $status['text']);
		$this->assertInternalType('int', $status['id']);
		// We could probably do more checks here.
	}

	/**
	 * Assert that a list array contains expected keys.
	 * @param array $list List array
	 * @return void
	 */
	private function assertList(array $list)
	{
		$this->assertInternalType('string', $list['name']);
		$this->assertInternalType('int', $list['id']);
		$this->assertInternalType('string', $list['id_str']);
		$this->assertContains($list['mode'], ['public', 'private']);
		// We could probably do more checks here.
	}

	/**
	 * Assert that the string is XML and contain the root element.
	 * @param string $result       XML string
	 * @param string $root_element Root element name
	 * @return void
	 */
	private function assertXml($result, $root_element)
	{
		$this->assertStringStartsWith('<?xml version="1.0"?>', $result);
		$this->assertContains('<'.$root_element, $result);
		// We could probably do more checks here.
	}

	/**
	 * Get the path to a temporary empty PNG image.
	 * @return string Path
	 */
	private function getTempImage()
	{
		$tmpFile = tempnam(sys_get_temp_dir(), 'tmp_file');
		file_put_contents(
			$tmpFile,
			base64_decode(
				// Empty 1x1 px PNG image
				'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg=='
			)
		);

		return $tmpFile;
	}

	/**
	 * Test the api_user() function.
	 * @return void
	 */
	public function testApiUser()
	{
		$this->assertEquals($this->selfUser['id'], api_user());
	}

	/**
	 * Test the api_user() function with an unallowed user.
	 * @return void
	 */
	public function testApiUserWithUnallowedUser()
	{
		$_SESSION = ['allow_api' => false];
		$this->assertEquals(false, api_user());
	}

	/**
	 * Test the api_source() function.
	 * @return void
	 */
	public function testApiSource()
	{
		$this->assertEquals('api', api_source());
	}

	/**
	 * Test the api_source() function with a Twidere user agent.
	 * @return void
	 */
	public function testApiSourceWithTwidere()
	{
		$_SERVER['HTTP_USER_AGENT'] = 'Twidere';
		$this->assertEquals('Twidere', api_source());
	}

	/**
	 * Test the api_source() function with a GET parameter.
	 * @return void
	 */
	public function testApiSourceWithGet()
	{
		$_GET['source'] = 'source_name';
		$this->assertEquals('source_name', api_source());
	}

	/**
	 * Test the api_date() function.
	 * @return void
	 */
	public function testApiDate()
	{
		$this->assertEquals('Wed Oct 10 00:00:00 +0000 1990', api_date('1990-10-10'));
	}

	/**
	 * Test the api_register_func() function.
	 * @return void
	 */
	public function testApiRegisterFunc()
	{
		global $API;
		$this->assertNull(
			api_register_func(
				'api_path',
				function () {
				},
				true,
				'method'
			)
		);
		$this->assertTrue($API['api_path']['auth']);
		$this->assertEquals('method', $API['api_path']['method']);
		$this->assertTrue(is_callable($API['api_path']['func']));
	}

	/**
	 * Test the api_login() function without any login.
	 * @return void
	 * @runInSeparateProcess
	 * @expectedException Friendica\Network\HTTPException\UnauthorizedException
	 */
	public function testApiLoginWithoutLogin()
	{
		api_login($this->app);
	}

	/**
	 * Test the api_login() function with a bad login.
	 * @return void
	 * @runInSeparateProcess
	 * @expectedException Friendica\Network\HTTPException\UnauthorizedException
	 */
	public function testApiLoginWithBadLogin()
	{
		$_SERVER['PHP_AUTH_USER'] = 'user@server';
		api_login($this->app);
	}

	/**
	 * Test the api_login() function with oAuth.
	 * @return void
	 */
	public function testApiLoginWithOauth()
	{
		$this->markTestIncomplete('Can we test this easily?');
	}

	/**
	 * Test the api_login() function with authentication provided by an addon.
	 * @return void
	 */
	public function testApiLoginWithAddonAuth()
	{
		$this->markTestIncomplete('Can we test this easily?');
	}

	/**
	 * Test the api_login() function with a correct login.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiLoginWithCorrectLogin()
	{
		$_SERVER['PHP_AUTH_USER'] = 'Test user';
		$_SERVER['PHP_AUTH_PW'] = 'password';
		api_login($this->app);
	}

	/**
	 * Test the api_login() function with a remote user.
	 * @return void
	 * @runInSeparateProcess
	 * @expectedException Friendica\Network\HTTPException\UnauthorizedException
	 */
	public function testApiLoginWithRemoteUser()
	{
		$_SERVER['REDIRECT_REMOTE_USER'] = '123456dXNlcjpwYXNzd29yZA==';
		api_login($this->app);
	}

	/**
	 * Test the api_check_method() function.
	 * @return void
	 */
	public function testApiCheckMethod()
	{
		$this->assertFalse(api_check_method('method'));
	}

	/**
	 * Test the api_check_method() function with a correct method.
	 * @return void
	 */
	public function testApiCheckMethodWithCorrectMethod()
	{
		$_SERVER['REQUEST_METHOD'] = 'method';
		$this->assertTrue(api_check_method('method'));
	}

	/**
	 * Test the api_check_method() function with a wildcard.
	 * @return void
	 */
	public function testApiCheckMethodWithWildcard()
	{
		$this->assertTrue(api_check_method('*'));
	}

	/**
	 * Test the api_call() function.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCall()
	{
		global $API;
		$API['api_path'] = [
			'method' => 'method',
			'func' => function () {
				return ['data' => ['some_data']];
			}
		];
		$_SERVER['REQUEST_METHOD'] = 'method';
		$_GET['callback'] = 'callback_name';

		$this->app->query_string = 'api_path';
		$this->assertEquals(
			'callback_name(["some_data"])',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_call() function with the profiled enabled.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCallWithProfiler()
	{
		global $API;
		$API['api_path'] = [
			'method' => 'method',
			'func' => function () {
				return ['data' => ['some_data']];
			}
		];
		$_SERVER['REQUEST_METHOD'] = 'method';
		Config::set('system', 'profiler', true);
		Config::set('rendertime', 'callstack', true);
		$this->app->callstack = [
			'database' => ['some_function' => 200],
			'database_write' => ['some_function' => 200],
			'cache' => ['some_function' => 200],
			'cache_write' => ['some_function' => 200],
			'network' => ['some_function' => 200]
		];

		$this->app->query_string = 'api_path';
		$this->assertEquals(
			'["some_data"]',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_call() function without any result.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCallWithNoResult()
	{
		global $API;
		$API['api_path'] = [
			'method' => 'method',
			'func' => function () {
				return false;
			}
		];
		$_SERVER['REQUEST_METHOD'] = 'method';

		$this->app->query_string = 'api_path';
		$this->assertEquals(
			'{"status":{"error":"Internal Server Error","code":"500 Internal Server Error","request":"api_path"}}',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_call() function with an unimplemented API.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCallWithUninplementedApi()
	{
		$this->assertEquals(
			'{"status":{"error":"Not Implemented","code":"501 Not Implemented","request":""}}',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_call() function with a JSON result.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCallWithJson()
	{
		global $API;
		$API['api_path'] = [
			'method' => 'method',
			'func' => function () {
				return ['data' => ['some_data']];
			}
		];
		$_SERVER['REQUEST_METHOD'] = 'method';

		$this->app->query_string = 'api_path.json';
		$this->assertEquals(
			'["some_data"]',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_call() function with an XML result.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCallWithXml()
	{
		global $API;
		$API['api_path'] = [
			'method' => 'method',
			'func' => function () {
				return 'some_data';
			}
		];
		$_SERVER['REQUEST_METHOD'] = 'method';

		$this->app->query_string = 'api_path.xml';
		$this->assertEquals(
			'some_data',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_call() function with an RSS result.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCallWithRss()
	{
		global $API;
		$API['api_path'] = [
			'method' => 'method',
			'func' => function () {
				return 'some_data';
			}
		];
		$_SERVER['REQUEST_METHOD'] = 'method';

		$this->app->query_string = 'api_path.rss';
		$this->assertEquals(
			'<?xml version="1.0" encoding="UTF-8"?>'."\n".
				'some_data',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_call() function with an Atom result.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCallWithAtom()
	{
		global $API;
		$API['api_path'] = [
			'method' => 'method',
			'func' => function () {
				return 'some_data';
			}
		];
		$_SERVER['REQUEST_METHOD'] = 'method';

		$this->app->query_string = 'api_path.atom';
		$this->assertEquals(
			'<?xml version="1.0" encoding="UTF-8"?>'."\n".
				'some_data',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_call() function with an unallowed method.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCallWithWrongMethod()
	{
		global $API;
		$API['api_path'] = ['method' => 'method'];

		$this->app->query_string = 'api_path';
		$this->assertEquals(
			'{"status":{"error":"Method Not Allowed","code":"405 Method Not Allowed","request":"api_path"}}',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_call() function with an unauthorized user.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCallWithWrongAuth()
	{
		global $API;
		$API['api_path'] = [
			'method' => 'method',
			'auth' => true
		];
		$_SERVER['REQUEST_METHOD'] = 'method';
		$_SESSION['authenticated'] = false;

		$this->app->query_string = 'api_path';
		$this->assertEquals(
			'{"status":{"error":"This API requires login","code":"401 Unauthorized","request":"api_path"}}',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_error() function with a JSON result.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiErrorWithJson()
	{
		$this->assertEquals(
			'{"status":{"error":"error_message","code":"200 Friendica\\\\Network\\\\HTTP","request":""}}',
			api_error('json', new HTTPException('error_message'))
		);
	}

	/**
	 * Test the api_error() function with an XML result.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiErrorWithXml()
	{
		$this->assertEquals(
			'<?xml version="1.0"?>'."\n".
			'<status xmlns="http://api.twitter.com" xmlns:statusnet="http://status.net/schema/api/1/" '.
				'xmlns:friendica="http://friendi.ca/schema/api/1/" '.
				'xmlns:georss="http://www.georss.org/georss">'."\n".
			'  <error>error_message</error>'."\n".
			'  <code>200 Friendica\Network\HTTP</code>'."\n".
			'  <request/>'."\n".
			'</status>'."\n",
			api_error('xml', new HTTPException('error_message'))
		);
	}

	/**
	 * Test the api_error() function with an RSS result.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiErrorWithRss()
	{
		$this->assertEquals(
			'<?xml version="1.0"?>'."\n".
			'<status xmlns="http://api.twitter.com" xmlns:statusnet="http://status.net/schema/api/1/" '.
				'xmlns:friendica="http://friendi.ca/schema/api/1/" '.
				'xmlns:georss="http://www.georss.org/georss">'."\n".
			'  <error>error_message</error>'."\n".
			'  <code>200 Friendica\Network\HTTP</code>'."\n".
			'  <request/>'."\n".
			'</status>'."\n",
			api_error('rss', new HTTPException('error_message'))
		);
	}

	/**
	 * Test the api_error() function with an Atom result.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiErrorWithAtom()
	{
		$this->assertEquals(
			'<?xml version="1.0"?>'."\n".
			'<status xmlns="http://api.twitter.com" xmlns:statusnet="http://status.net/schema/api/1/" '.
				'xmlns:friendica="http://friendi.ca/schema/api/1/" '.
				'xmlns:georss="http://www.georss.org/georss">'."\n".
			'  <error>error_message</error>'."\n".
			'  <code>200 Friendica\Network\HTTP</code>'."\n".
			'  <request/>'."\n".
			'</status>'."\n",
			api_error('atom', new HTTPException('error_message'))
		);
	}

	/**
	 * Test the api_rss_extra() function.
	 * @return void
	 */
	public function testApiRssExtra()
	{
		$user_info = ['url' => 'user_url', 'lang' => 'en'];
		$result = api_rss_extra($this->app, [], $user_info);
		$this->assertEquals($user_info, $result['$user']);
		$this->assertEquals($user_info['url'], $result['$rss']['alternate']);
		$this->assertArrayHasKey('self', $result['$rss']);
		$this->assertArrayHasKey('base', $result['$rss']);
		$this->assertArrayHasKey('updated', $result['$rss']);
		$this->assertArrayHasKey('atom_updated', $result['$rss']);
		$this->assertArrayHasKey('language', $result['$rss']);
		$this->assertArrayHasKey('logo', $result['$rss']);
	}

	/**
	 * Test the api_rss_extra() function without any user info.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiRssExtraWithoutUserInfo()
	{
		$result = api_rss_extra($this->app, [], null);
		$this->assertInternalType('array', $result['$user']);
		$this->assertArrayHasKey('alternate', $result['$rss']);
		$this->assertArrayHasKey('self', $result['$rss']);
		$this->assertArrayHasKey('base', $result['$rss']);
		$this->assertArrayHasKey('updated', $result['$rss']);
		$this->assertArrayHasKey('atom_updated', $result['$rss']);
		$this->assertArrayHasKey('language', $result['$rss']);
		$this->assertArrayHasKey('logo', $result['$rss']);
	}

	/**
	 * Test the api_unique_id_to_nurl() function.
	 * @return void
	 */
	public function testApiUniqueIdToNurl()
	{
		$this->assertFalse(api_unique_id_to_nurl($this->wrongUserId));
	}

	/**
	 * Test the api_unique_id_to_nurl() function with a correct ID.
	 * @return void
	 */
	public function testApiUniqueIdToNurlWithCorrectId()
	{
		$this->assertEquals($this->otherUser['nurl'], api_unique_id_to_nurl($this->otherUser['id']));
	}

	/**
	 * Test the api_get_user() function.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUser()
	{
		$user = api_get_user($this->app);
		$this->assertSelfUser($user);
		$this->assertEquals('708fa0', $user['profile_sidebar_fill_color']);
		$this->assertEquals('6fdbe8', $user['profile_link_color']);
		$this->assertEquals('ededed', $user['profile_background_color']);
	}

	/**
	 * Test the api_get_user() function with a Frio schema.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithFrioSchema()
	{
		PConfig::set($this->selfUser['id'], 'frio', 'schema', 'red');
		$user = api_get_user($this->app);
		$this->assertSelfUser($user);
		$this->assertEquals('708fa0', $user['profile_sidebar_fill_color']);
		$this->assertEquals('6fdbe8', $user['profile_link_color']);
		$this->assertEquals('ededed', $user['profile_background_color']);
	}

	/**
	 * Test the api_get_user() function with a custom Frio schema.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithCustomFrioSchema()
	{
		$ret1 = PConfig::set($this->selfUser['id'], 'frio', 'schema', '---');
		$ret2 = PConfig::set($this->selfUser['id'], 'frio', 'nav_bg', '#123456');
		$ret3 = PConfig::set($this->selfUser['id'], 'frio', 'link_color', '#123456');
		$ret4 = PConfig::set($this->selfUser['id'], 'frio', 'background_color', '#123456');
		$user = api_get_user($this->app);
		$this->assertSelfUser($user);
		$this->assertEquals('123456', $user['profile_sidebar_fill_color']);
		$this->assertEquals('123456', $user['profile_link_color']);
		$this->assertEquals('123456', $user['profile_background_color']);
	}

	/**
	 * Test the api_get_user() function with an empty Frio schema.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithEmptyFrioSchema()
	{
		PConfig::set($this->selfUser['id'], 'frio', 'schema', '---');
		$user = api_get_user($this->app);
		$this->assertSelfUser($user);
		$this->assertEquals('708fa0', $user['profile_sidebar_fill_color']);
		$this->assertEquals('6fdbe8', $user['profile_link_color']);
		$this->assertEquals('ededed', $user['profile_background_color']);
	}

	/**
	 * Test the api_get_user() function with an user that is not allowed to use the API.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithoutApiUser()
	{
		$_SERVER['PHP_AUTH_USER'] = 'Test user';
		$_SERVER['PHP_AUTH_PW'] = 'password';
		$_SESSION['allow_api'] = false;
		$this->assertFalse(api_get_user($this->app));
	}

	/**
	 * Test the api_get_user() function with an user ID in a GET parameter.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithGetId()
	{
		$_GET['user_id'] = $this->otherUser['id'];
		$this->assertOtherUser(api_get_user($this->app));
	}

	/**
	 * Test the api_get_user() function with a wrong user ID in a GET parameter.
	 * @return void
	 * @runInSeparateProcess
	 * @expectedException Friendica\Network\HTTPException\BadRequestException
	 */
	public function testApiGetUserWithWrongGetId()
	{
		$_GET['user_id'] = $this->wrongUserId;
		$this->assertOtherUser(api_get_user($this->app));
	}

	/**
	 * Test the api_get_user() function with an user name in a GET parameter.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithGetName()
	{
		$_GET['screen_name'] = $this->selfUser['nick'];
		$this->assertSelfUser(api_get_user($this->app));
	}

	/**
	 * Test the api_get_user() function with a profile URL in a GET parameter.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithGetUrl()
	{
		$_GET['profileurl'] = $this->selfUser['nurl'];
		$this->assertSelfUser(api_get_user($this->app));
	}

	/**
	 * Test the api_get_user() function with an user ID in the API path.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithNumericCalledApi()
	{
		global $called_api;
		$called_api = ['api_path'];
		$this->app->argv[1] = $this->otherUser['id'].'.json';
		$this->assertOtherUser(api_get_user($this->app));
	}

	/**
	 * Test the api_get_user() function with the $called_api global variable.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithCalledApi()
	{
		global $called_api;
		$called_api = ['api', 'api_path'];
		$this->assertSelfUser(api_get_user($this->app));
	}

	/**
	 * Test the api_get_user() function with a valid user.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithCorrectUser()
	{
		$this->assertOtherUser(api_get_user($this->app, $this->otherUser['id']));
	}

	/**
	 * Test the api_get_user() function with a wrong user ID.
	 * @return void
	 * @runInSeparateProcess
	 * @expectedException Friendica\Network\HTTPException\BadRequestException
	 */
	public function testApiGetUserWithWrongUser()
	{
		$this->assertOtherUser(api_get_user($this->app, $this->wrongUserId));
	}

	/**
	 * Test the api_get_user() function with a 0 user ID.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithZeroUser()
	{
		$this->assertSelfUser(api_get_user($this->app, 0));
	}

	/**
	 * Test the api_item_get_user() function.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiItemGetUser()
	{
		$users = api_item_get_user($this->app, []);
		$this->assertSelfUser($users[0]);
	}

	/**
	 * Test the api_item_get_user() function with a different item parent.
	 * @return void
	 */
	public function testApiItemGetUserWithDifferentParent()
	{
		$users = api_item_get_user($this->app, ['thr-parent' => 'item_parent', 'uri' => 'item_uri']);
		$this->assertSelfUser($users[0]);
		$this->assertEquals($users[0], $users[1]);
	}

	/**
	 * Test the api_walk_recursive() function.
	 * @return void
	 */
	public function testApiWalkRecursive()
	{
		$array = ['item1'];
		$this->assertEquals(
			$array,
			api_walk_recursive(
				$array,
				function () {
					// Should we test this with a callback that actually does something?
					return true;
				}
			)
		);
	}

	/**
	 * Test the api_walk_recursive() function with an array.
	 * @return void
	 */
	public function testApiWalkRecursiveWithArray()
	{
		$array = [['item1'], ['item2']];
		$this->assertEquals(
			$array,
			api_walk_recursive(
				$array,
				function () {
					// Should we test this with a callback that actually does something?
					return true;
				}
			)
		);
	}

	/**
	 * Test the api_reformat_xml() function.
	 * @return void
	 */
	public function testApiReformatXml()
	{
		$item = true;
		$key = '';
		$this->assertTrue(api_reformat_xml($item, $key));
		$this->assertEquals('true', $item);
	}

	/**
	 * Test the api_reformat_xml() function with a statusnet_api key.
	 * @return void
	 */
	public function testApiReformatXmlWithStatusnetKey()
	{
		$item = '';
		$key = 'statusnet_api';
		$this->assertTrue(api_reformat_xml($item, $key));
		$this->assertEquals('statusnet:api', $key);
	}

	/**
	 * Test the api_reformat_xml() function with a friendica_api key.
	 * @return void
	 */
	public function testApiReformatXmlWithFriendicaKey()
	{
		$item = '';
		$key = 'friendica_api';
		$this->assertTrue(api_reformat_xml($item, $key));
		$this->assertEquals('friendica:api', $key);
	}

	/**
	 * Test the api_create_xml() function.
	 * @return void
	 */
	public function testApiCreateXml()
	{
		$this->assertEquals(
			'<?xml version="1.0"?>'."\n".
			'<root_element xmlns="http://api.twitter.com" xmlns:statusnet="http://status.net/schema/api/1/" '.
				'xmlns:friendica="http://friendi.ca/schema/api/1/" '.
				'xmlns:georss="http://www.georss.org/georss">'."\n".
				'  <data>some_data</data>'."\n".
			'</root_element>'."\n",
			api_create_xml(['data' => ['some_data']], 'root_element')
		);
	}

	/**
	 * Test the api_create_xml() function without any XML namespace.
	 * @return void
	 */
	public function testApiCreateXmlWithoutNamespaces()
	{
		$this->assertEquals(
			'<?xml version="1.0"?>'."\n".
			'<ok>'."\n".
				'  <data>some_data</data>'."\n".
			'</ok>'."\n",
			api_create_xml(['data' => ['some_data']], 'ok')
		);
	}

	/**
	 * Test the api_format_data() function.
	 * @return void
	 */
	public function testApiFormatData()
	{
		$data = ['some_data'];
		$this->assertEquals($data, api_format_data('root_element', 'json', $data));
	}

	/**
	 * Test the api_format_data() function with an XML result.
	 * @return void
	 */
	public function testApiFormatDataWithXml()
	{
		$this->assertEquals(
			'<?xml version="1.0"?>'."\n".
			'<root_element xmlns="http://api.twitter.com" xmlns:statusnet="http://status.net/schema/api/1/" '.
				'xmlns:friendica="http://friendi.ca/schema/api/1/" '.
				'xmlns:georss="http://www.georss.org/georss">'."\n".
				'  <data>some_data</data>'."\n".
			'</root_element>'."\n",
			api_format_data('root_element', 'xml', ['data' => ['some_data']])
		);
	}

	/**
	 * Test the api_account_verify_credentials() function.
	 * @return void
	 */
	public function testApiAccountVerifyCredentials()
	{
		$this->assertArrayHasKey('user', api_account_verify_credentials('json'));
	}

	/**
	 * Test the api_account_verify_credentials() function without an authenticated user.
	 * @return void
	 * @expectedException Friendica\Network\HTTPException\ForbiddenException
	 */
	public function testApiAccountVerifyCredentialsWithoutAuthenticatedUser()
	{
		$_SESSION['authenticated'] = false;
		api_account_verify_credentials('json');
	}

	/**
	 * Test the requestdata() function.
	 * @return void
	 */
	public function testRequestdata()
	{
		$this->assertNull(requestdata('variable_name'));
	}

	/**
	 * Test the requestdata() function with a POST parameter.
	 * @return void
	 */
	public function testRequestdataWithPost()
	{
		$_POST['variable_name'] = 'variable_value';
		$this->assertEquals('variable_value', requestdata('variable_name'));
	}

	/**
	 * Test the requestdata() function with a GET parameter.
	 * @return void
	 */
	public function testRequestdataWithGet()
	{
		$_GET['variable_name'] = 'variable_value';
		$this->assertEquals('variable_value', requestdata('variable_name'));
	}

	/**
	 * Test the api_statuses_mediap() function.
	 * @return void
	 */
	public function testApiStatusesMediap()
	{
		$this->app->argc = 2;

		$_FILES = [
			'media' => [
				'id' => 666,
				'size' => 666,
				'width' => 666,
				'height' => 666,
				'tmp_name' => $this->getTempImage(),
				'name' => 'spacer.png',
				'type' => 'image/png'
			]
		];
		$_GET['status'] = '<b>Status content</b>';

		$result = api_statuses_mediap('json');
		$this->assertStatus($result['status']);
	}

	/**
	 * Test the api_statuses_mediap() function without an authenticated user.
	 * @return void
	 * @expectedException Friendica\Network\HTTPException\ForbiddenException
	 */
	public function testApiStatusesMediapWithoutAuthenticatedUser()
	{
		$_SESSION['authenticated'] = false;
		api_statuses_mediap('json');
	}

	/**
	 * Test the api_statuses_update() function.
	 * @return void
	 */
	public function testApiStatusesUpdate()
	{
		$_GET['status'] = 'Status content #friendica';
		$_GET['in_reply_to_status_id'] = -1;
		$_GET['lat'] = 48;
		$_GET['long'] = 7;
		$_FILES = [
			'media' => [
				'id' => 666,
				'size' => 666,
				'width' => 666,
				'height' => 666,
				'tmp_name' => $this->getTempImage(),
				'name' => 'spacer.png',
				'type' => 'image/png'
			]
		];

		$result = api_statuses_update('json');
		$this->assertStatus($result['status']);
	}

	/**
	 * Test the api_statuses_update() function with an HTML status.
	 * @return void
	 */
	public function testApiStatusesUpdateWithHtml()
	{
		$_GET['htmlstatus'] = '<b>Status content</b>';

		$result = api_statuses_update('json');
		$this->assertStatus($result['status']);
	}

	/**
	 * Test the api_statuses_update() function without an authenticated user.
	 * @return void
	 * @expectedException Friendica\Network\HTTPException\ForbiddenException
	 */
	public function testApiStatusesUpdateWithoutAuthenticatedUser()
	{
		$_SESSION['authenticated'] = false;
		api_statuses_update('json');
	}

	/**
	 * Test the api_statuses_update() function with a parent status.
	 * @return void
	 */
	public function testApiStatusesUpdateWithParent()
	{
		$this->markTestIncomplete('This triggers an exit() somewhere and kills PHPUnit.');
	}

	/**
	 * Test the api_statuses_update() function with a media_ids parameter.
	 * @return void
	 */
	public function testApiStatusesUpdateWithMediaIds()
	{
		$this->markTestIncomplete();
	}

	/**
	 * Test the api_statuses_update() function with the throttle limit reached.
	 * @return void
	 */
	public function testApiStatusesUpdateWithDayThrottleReached()
	{
		$this->markTestIncomplete();
	}

	/**
	 * Test the api_media_upload() function.
	 * @return void
	 * @expectedException Friendica\Network\HTTPException\BadRequestException
	 */
	public function testApiMediaUpload()
	{
		api_media_upload();
	}

	/**
	 * Test the api_media_upload() function without an authenticated user.
	 * @return void
	 * @expectedException Friendica\Network\HTTPException\ForbiddenException
	 */
	public function testApiMediaUploadWithoutAuthenticatedUser()
	{
		$_SESSION['authenticated'] = false;
		api_media_upload();
	}

	/**
	 * Test the api_media_upload() function with an invalid uploaded media.
	 * @return void
	 * @expectedException Friendica\Network\HTTPException\InternalServerErrorException
	 */
	public function testApiMediaUploadWithMedia()
	{
		$_FILES = [
			'media' => [
				'id' => 666,
				'tmp_name' => 'tmp_name'
			]
		];
		api_media_upload();
	}

	/**
	 * Test the api_media_upload() function with an valid uploaded media.
	 * @return void
	 */
	public function testApiMediaUploadWithValidMedia()
	{
		$_FILES = [
			'media' => [
				'id' => 666,
				'size' => 666,
				'width' => 666,
				'height' => 666,
				'tmp_name' => $this->getTempImage(),
				'name' => 'spacer.png',
				'type' => 'image/png'
			]
		];
		$app = \get_app();
		$app->argc = 2;

		$result = api_media_upload();
		$this->assertEquals('image/png', $result['media']['image']['image_type']);
		$this->assertEquals(1, $result['media']['image']['w']);
		$this->assertEquals(1, $result['media']['image']['h']);
		$this->assertNotEmpty($result['media']['image']['friendica_preview_url']);
	}

	/**
	 * Test the api_status_show() function.
	 * @return void
	 */
	public function testApiStatusShow()
	{
		$result = api_status_show('json');
		$this->assertStatus($result['status']);
	}

	/**
	 * Test the api_status_show() function with an XML result.
	 * @return void
	 */
	public function testApiStatusShowWithXml()
	{
		$result = api_status_show('xml');
		$this->assertXml($result, 'statuses');
	}

	/**
	 * Test the api_status_show() function with a raw result.
	 * @return void
	 */
	public function testApiStatusShowWithRaw()
	{
		$this->assertStatus(api_status_show('raw'));
	}

	/**
	 * Test the api_users_show() function.
	 * @return void
	 */
	public function testApiUsersShow()
	{
		$result = api_users_show('json');
		// We can't use assertSelfUser() here because the user object is missing some properties.
		$this->assertEquals($this->selfUser['id'], $result['user']['cid']);
		$this->assertEquals('DFRN', $result['user']['location']);
		$this->assertEquals($this->selfUser['name'], $result['user']['name']);
		$this->assertEquals($this->selfUser['nick'], $result['user']['screen_name']);
		$this->assertEquals('dfrn', $result['user']['network']);
		$this->assertTrue($result['user']['verified']);
	}

	/**
	 * Test the api_users_show() function with an XML result.
	 * @return void
	 */
	public function testApiUsersShowWithXml()
	{
		$result = api_users_show('xml');
		$this->assertXml($result, 'statuses');
	}

	/**
	 * Test the api_users_search() function.
	 * @return void
	 */
	public function testApiUsersSearch()
	{
		$_GET['q'] = 'othercontact';
		$result = api_users_search('json');
		$this->assertOtherUser($result['users'][0]);
	}

	/**
	 * Test the api_users_search() function with an XML result.
	 * @return void
	 */
	public function testApiUsersSearchWithXml()
	{
		$_GET['q'] = 'othercontact';
		$result = api_users_search('xml');
		$this->assertXml($result, 'users');
	}

	/**
	 * Test the api_users_search() function without a GET q parameter.
	 * @return void
	 * @expectedException Friendica\Network\HTTPException\BadRequestException
	 */
	public function testApiUsersSearchWithoutQuery()
	{
		api_users_search('json');
	}

	/**
	 * Test the api_users_lookup() function.
	 * @return void
	 * @expectedException Friendica\Network\HTTPException\NotFoundException
	 */
	public function testApiUsersLookup()
	{
		api_users_lookup('json');
	}

	/**
	 * Test the api_users_lookup() function with an user ID.
	 * @return void
	 */
	public function testApiUsersLookupWithUserId()
	{
		$_REQUEST['user_id'] = $this->otherUser['id'];
		$result = api_users_lookup('json');
		$this->assertOtherUser($result['users'][0]);
	}

	/**
	 * Test the api_search() function.
	 * @return void
	 */
	public function testApiSearch()
	{
		$_REQUEST['q'] = 'reply';
		$_REQUEST['max_id'] = 10;
		$result = api_search('json');
		foreach ($result['status'] as $status) {
			$this->assertStatus($status);
			$this->assertContains('reply', $status['text'], null, true);
		}
	}

	/**
	 * Test the api_search() function a count parameter.
	 * @return void
	 */
	public function testApiSearchWithCount()
	{
		$_REQUEST['q'] = 'reply';
		$_REQUEST['count'] = 20;
		$result = api_search('json');
		foreach ($result['status'] as $status) {
			$this->assertStatus($status);
			$this->assertContains('reply', $status['text'], null, true);
		}
	}

	/**
	 * Test the api_search() function with an rpp parameter.
	 * @return void
	 */
	public function testApiSearchWithRpp()
	{
		$_REQUEST['q'] = 'reply';
		$_REQUEST['rpp'] = 20;
		$result = api_search('json');
		foreach ($result['status'] as $status) {
			$this->assertStatus($status);
			$this->assertContains('reply', $status['text'], null, true);
		}
	}

	/**
	 * Test the api_search() function with an q parameter contains hashtag.
	 * @return void
	 */
	public function testApiSearchWithHashtag()
	{
		$_REQUEST['q'] = '#friendica';
		$result = api_search('json');<commit_msg># should urlencode to %23<commit_after><?php
/**
 * ApiTest class.
 */

namespace Friendica\Test;

use Friendica\BaseObject;
use Friendica\Core\Config;
use Friendica\Core\PConfig;
use Friendica\Core\Protocol;
use Friendica\Core\System;
use Friendica\Network\HTTPException;

require_once __DIR__ . '/../../include/api.php';

/**
 * Tests for the API functions.
 *
 * Functions that use header() need to be tested in a separate process.
 * @see https://phpunit.de/manual/5.7/en/appendixes.annotations.html#appendixes.annotations.runTestsInSeparateProcesses
 */
class ApiTest extends DatabaseTest
{
	/**
	 * Create variables used by tests.
	 */
	public function setUp()
	{
		parent::setUp();

		$this->app = BaseObject::getApp();

		// User data that the test database is populated with
		$this->selfUser = [
			'id' => 42,
			'name' => '<NAME>',
			'nick' => 'selfcontact',
			'nurl' => 'http://localhost/profile/selfcontact'
		];
		$this->friendUser = [
			'id' => 44,
			'name' => '<NAME>',
			'nick' => 'friendcontact',
			'nurl' => 'http://localhost/profile/friendcontact'
		];
		$this->otherUser = [
			'id' => 43,
			'name' => 'othercontact',
			'nick' => 'othercontact',
			'nurl' => 'http://localhost/profile/othercontact'
		];

		// User ID that we know is not in the database
		$this->wrongUserId = 666;

		// Most API require login so we force the session
		$_SESSION = [
			'allow_api' => true,
			'authenticated' => true,
			'uid' => $this->selfUser['id']
		];

		Config::set('system', 'url', 'http://localhost');
		Config::set('system', 'hostname', 'localhost');
		Config::set('system', 'worker_dont_fork', true);

		// Default config
		Config::set('config', 'hostname', 'localhost');
		Config::set('system', 'throttle_limit_day', 100);
		Config::set('system', 'throttle_limit_week', 100);
		Config::set('system', 'throttle_limit_month', 100);
		Config::set('system', 'theme', 'system_theme');
	}

	/**
	 * Cleanup variables used by tests.
	 */
	protected function tearDown()
	{
		parent::tearDown();

		$this->app->argc = 1;
		$this->app->argv = ['home'];
	}

	/**
	 * Assert that an user array contains expected keys.
	 * @param array $user User array
	 * @return void
	 */
	private function assertSelfUser(array $user)
	{
		$this->assertEquals($this->selfUser['id'], $user['uid']);
		$this->assertEquals($this->selfUser['id'], $user['cid']);
		$this->assertEquals(1, $user['self']);
		$this->assertEquals('DFRN', $user['location']);
		$this->assertEquals($this->selfUser['name'], $user['name']);
		$this->assertEquals($this->selfUser['nick'], $user['screen_name']);
		$this->assertEquals('dfrn', $user['network']);
		$this->assertTrue($user['verified']);
	}

	/**
	 * Assert that an user array contains expected keys.
	 * @param array $user User array
	 * @return void
	 */
	private function assertOtherUser(array $user)
	{
		$this->assertEquals($this->otherUser['id'], $user['id']);
		$this->assertEquals($this->otherUser['id'], $user['id_str']);
		$this->assertEquals(0, $user['self']);
		$this->assertEquals($this->otherUser['name'], $user['name']);
		$this->assertEquals($this->otherUser['nick'], $user['screen_name']);
		$this->assertFalse($user['verified']);
	}

	/**
	 * Assert that a status array contains expected keys.
	 * @param array $status Status array
	 * @return void
	 */
	private function assertStatus(array $status)
	{
		$this->assertInternalType('string', $status['text']);
		$this->assertInternalType('int', $status['id']);
		// We could probably do more checks here.
	}

	/**
	 * Assert that a list array contains expected keys.
	 * @param array $list List array
	 * @return void
	 */
	private function assertList(array $list)
	{
		$this->assertInternalType('string', $list['name']);
		$this->assertInternalType('int', $list['id']);
		$this->assertInternalType('string', $list['id_str']);
		$this->assertContains($list['mode'], ['public', 'private']);
		// We could probably do more checks here.
	}

	/**
	 * Assert that the string is XML and contain the root element.
	 * @param string $result       XML string
	 * @param string $root_element Root element name
	 * @return void
	 */
	private function assertXml($result, $root_element)
	{
		$this->assertStringStartsWith('<?xml version="1.0"?>', $result);
		$this->assertContains('<'.$root_element, $result);
		// We could probably do more checks here.
	}

	/**
	 * Get the path to a temporary empty PNG image.
	 * @return string Path
	 */
	private function getTempImage()
	{
		$tmpFile = tempnam(sys_get_temp_dir(), 'tmp_file');
		file_put_contents(
			$tmpFile,
			base64_decode(
				// Empty 1x1 px PNG image
				'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg=='
			)
		);

		return $tmpFile;
	}

	/**
	 * Test the api_user() function.
	 * @return void
	 */
	public function testApiUser()
	{
		$this->assertEquals($this->selfUser['id'], api_user());
	}

	/**
	 * Test the api_user() function with an unallowed user.
	 * @return void
	 */
	public function testApiUserWithUnallowedUser()
	{
		$_SESSION = ['allow_api' => false];
		$this->assertEquals(false, api_user());
	}

	/**
	 * Test the api_source() function.
	 * @return void
	 */
	public function testApiSource()
	{
		$this->assertEquals('api', api_source());
	}

	/**
	 * Test the api_source() function with a Twidere user agent.
	 * @return void
	 */
	public function testApiSourceWithTwidere()
	{
		$_SERVER['HTTP_USER_AGENT'] = 'Twidere';
		$this->assertEquals('Twidere', api_source());
	}

	/**
	 * Test the api_source() function with a GET parameter.
	 * @return void
	 */
	public function testApiSourceWithGet()
	{
		$_GET['source'] = 'source_name';
		$this->assertEquals('source_name', api_source());
	}

	/**
	 * Test the api_date() function.
	 * @return void
	 */
	public function testApiDate()
	{
		$this->assertEquals('Wed Oct 10 00:00:00 +0000 1990', api_date('1990-10-10'));
	}

	/**
	 * Test the api_register_func() function.
	 * @return void
	 */
	public function testApiRegisterFunc()
	{
		global $API;
		$this->assertNull(
			api_register_func(
				'api_path',
				function () {
				},
				true,
				'method'
			)
		);
		$this->assertTrue($API['api_path']['auth']);
		$this->assertEquals('method', $API['api_path']['method']);
		$this->assertTrue(is_callable($API['api_path']['func']));
	}

	/**
	 * Test the api_login() function without any login.
	 * @return void
	 * @runInSeparateProcess
	 * @expectedException Friendica\Network\HTTPException\UnauthorizedException
	 */
	public function testApiLoginWithoutLogin()
	{
		api_login($this->app);
	}

	/**
	 * Test the api_login() function with a bad login.
	 * @return void
	 * @runInSeparateProcess
	 * @expectedException Friendica\Network\HTTPException\UnauthorizedException
	 */
	public function testApiLoginWithBadLogin()
	{
		$_SERVER['PHP_AUTH_USER'] = 'user@server';
		api_login($this->app);
	}

	/**
	 * Test the api_login() function with oAuth.
	 * @return void
	 */
	public function testApiLoginWithOauth()
	{
		$this->markTestIncomplete('Can we test this easily?');
	}

	/**
	 * Test the api_login() function with authentication provided by an addon.
	 * @return void
	 */
	public function testApiLoginWithAddonAuth()
	{
		$this->markTestIncomplete('Can we test this easily?');
	}

	/**
	 * Test the api_login() function with a correct login.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiLoginWithCorrectLogin()
	{
		$_SERVER['PHP_AUTH_USER'] = 'Test user';
		$_SERVER['PHP_AUTH_PW'] = 'password';
		api_login($this->app);
	}

	/**
	 * Test the api_login() function with a remote user.
	 * @return void
	 * @runInSeparateProcess
	 * @expectedException Friendica\Network\HTTPException\UnauthorizedException
	 */
	public function testApiLoginWithRemoteUser()
	{
		$_SERVER['REDIRECT_REMOTE_USER'] = '123456dXNlcjpwYXNzd29yZA==';
		api_login($this->app);
	}

	/**
	 * Test the api_check_method() function.
	 * @return void
	 */
	public function testApiCheckMethod()
	{
		$this->assertFalse(api_check_method('method'));
	}

	/**
	 * Test the api_check_method() function with a correct method.
	 * @return void
	 */
	public function testApiCheckMethodWithCorrectMethod()
	{
		$_SERVER['REQUEST_METHOD'] = 'method';
		$this->assertTrue(api_check_method('method'));
	}

	/**
	 * Test the api_check_method() function with a wildcard.
	 * @return void
	 */
	public function testApiCheckMethodWithWildcard()
	{
		$this->assertTrue(api_check_method('*'));
	}

	/**
	 * Test the api_call() function.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCall()
	{
		global $API;
		$API['api_path'] = [
			'method' => 'method',
			'func' => function () {
				return ['data' => ['some_data']];
			}
		];
		$_SERVER['REQUEST_METHOD'] = 'method';
		$_GET['callback'] = 'callback_name';

		$this->app->query_string = 'api_path';
		$this->assertEquals(
			'callback_name(["some_data"])',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_call() function with the profiled enabled.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCallWithProfiler()
	{
		global $API;
		$API['api_path'] = [
			'method' => 'method',
			'func' => function () {
				return ['data' => ['some_data']];
			}
		];
		$_SERVER['REQUEST_METHOD'] = 'method';
		Config::set('system', 'profiler', true);
		Config::set('rendertime', 'callstack', true);
		$this->app->callstack = [
			'database' => ['some_function' => 200],
			'database_write' => ['some_function' => 200],
			'cache' => ['some_function' => 200],
			'cache_write' => ['some_function' => 200],
			'network' => ['some_function' => 200]
		];

		$this->app->query_string = 'api_path';
		$this->assertEquals(
			'["some_data"]',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_call() function without any result.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCallWithNoResult()
	{
		global $API;
		$API['api_path'] = [
			'method' => 'method',
			'func' => function () {
				return false;
			}
		];
		$_SERVER['REQUEST_METHOD'] = 'method';

		$this->app->query_string = 'api_path';
		$this->assertEquals(
			'{"status":{"error":"Internal Server Error","code":"500 Internal Server Error","request":"api_path"}}',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_call() function with an unimplemented API.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCallWithUninplementedApi()
	{
		$this->assertEquals(
			'{"status":{"error":"Not Implemented","code":"501 Not Implemented","request":""}}',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_call() function with a JSON result.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCallWithJson()
	{
		global $API;
		$API['api_path'] = [
			'method' => 'method',
			'func' => function () {
				return ['data' => ['some_data']];
			}
		];
		$_SERVER['REQUEST_METHOD'] = 'method';

		$this->app->query_string = 'api_path.json';
		$this->assertEquals(
			'["some_data"]',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_call() function with an XML result.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCallWithXml()
	{
		global $API;
		$API['api_path'] = [
			'method' => 'method',
			'func' => function () {
				return 'some_data';
			}
		];
		$_SERVER['REQUEST_METHOD'] = 'method';

		$this->app->query_string = 'api_path.xml';
		$this->assertEquals(
			'some_data',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_call() function with an RSS result.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCallWithRss()
	{
		global $API;
		$API['api_path'] = [
			'method' => 'method',
			'func' => function () {
				return 'some_data';
			}
		];
		$_SERVER['REQUEST_METHOD'] = 'method';

		$this->app->query_string = 'api_path.rss';
		$this->assertEquals(
			'<?xml version="1.0" encoding="UTF-8"?>'."\n".
				'some_data',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_call() function with an Atom result.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCallWithAtom()
	{
		global $API;
		$API['api_path'] = [
			'method' => 'method',
			'func' => function () {
				return 'some_data';
			}
		];
		$_SERVER['REQUEST_METHOD'] = 'method';

		$this->app->query_string = 'api_path.atom';
		$this->assertEquals(
			'<?xml version="1.0" encoding="UTF-8"?>'."\n".
				'some_data',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_call() function with an unallowed method.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCallWithWrongMethod()
	{
		global $API;
		$API['api_path'] = ['method' => 'method'];

		$this->app->query_string = 'api_path';
		$this->assertEquals(
			'{"status":{"error":"Method Not Allowed","code":"405 Method Not Allowed","request":"api_path"}}',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_call() function with an unauthorized user.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiCallWithWrongAuth()
	{
		global $API;
		$API['api_path'] = [
			'method' => 'method',
			'auth' => true
		];
		$_SERVER['REQUEST_METHOD'] = 'method';
		$_SESSION['authenticated'] = false;

		$this->app->query_string = 'api_path';
		$this->assertEquals(
			'{"status":{"error":"This API requires login","code":"401 Unauthorized","request":"api_path"}}',
			api_call($this->app)
		);
	}

	/**
	 * Test the api_error() function with a JSON result.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiErrorWithJson()
	{
		$this->assertEquals(
			'{"status":{"error":"error_message","code":"200 Friendica\\\\Network\\\\HTTP","request":""}}',
			api_error('json', new HTTPException('error_message'))
		);
	}

	/**
	 * Test the api_error() function with an XML result.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiErrorWithXml()
	{
		$this->assertEquals(
			'<?xml version="1.0"?>'."\n".
			'<status xmlns="http://api.twitter.com" xmlns:statusnet="http://status.net/schema/api/1/" '.
				'xmlns:friendica="http://friendi.ca/schema/api/1/" '.
				'xmlns:georss="http://www.georss.org/georss">'."\n".
			'  <error>error_message</error>'."\n".
			'  <code>200 Friendica\Network\HTTP</code>'."\n".
			'  <request/>'."\n".
			'</status>'."\n",
			api_error('xml', new HTTPException('error_message'))
		);
	}

	/**
	 * Test the api_error() function with an RSS result.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiErrorWithRss()
	{
		$this->assertEquals(
			'<?xml version="1.0"?>'."\n".
			'<status xmlns="http://api.twitter.com" xmlns:statusnet="http://status.net/schema/api/1/" '.
				'xmlns:friendica="http://friendi.ca/schema/api/1/" '.
				'xmlns:georss="http://www.georss.org/georss">'."\n".
			'  <error>error_message</error>'."\n".
			'  <code>200 Friendica\Network\HTTP</code>'."\n".
			'  <request/>'."\n".
			'</status>'."\n",
			api_error('rss', new HTTPException('error_message'))
		);
	}

	/**
	 * Test the api_error() function with an Atom result.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiErrorWithAtom()
	{
		$this->assertEquals(
			'<?xml version="1.0"?>'."\n".
			'<status xmlns="http://api.twitter.com" xmlns:statusnet="http://status.net/schema/api/1/" '.
				'xmlns:friendica="http://friendi.ca/schema/api/1/" '.
				'xmlns:georss="http://www.georss.org/georss">'."\n".
			'  <error>error_message</error>'."\n".
			'  <code>200 Friendica\Network\HTTP</code>'."\n".
			'  <request/>'."\n".
			'</status>'."\n",
			api_error('atom', new HTTPException('error_message'))
		);
	}

	/**
	 * Test the api_rss_extra() function.
	 * @return void
	 */
	public function testApiRssExtra()
	{
		$user_info = ['url' => 'user_url', 'lang' => 'en'];
		$result = api_rss_extra($this->app, [], $user_info);
		$this->assertEquals($user_info, $result['$user']);
		$this->assertEquals($user_info['url'], $result['$rss']['alternate']);
		$this->assertArrayHasKey('self', $result['$rss']);
		$this->assertArrayHasKey('base', $result['$rss']);
		$this->assertArrayHasKey('updated', $result['$rss']);
		$this->assertArrayHasKey('atom_updated', $result['$rss']);
		$this->assertArrayHasKey('language', $result['$rss']);
		$this->assertArrayHasKey('logo', $result['$rss']);
	}

	/**
	 * Test the api_rss_extra() function without any user info.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiRssExtraWithoutUserInfo()
	{
		$result = api_rss_extra($this->app, [], null);
		$this->assertInternalType('array', $result['$user']);
		$this->assertArrayHasKey('alternate', $result['$rss']);
		$this->assertArrayHasKey('self', $result['$rss']);
		$this->assertArrayHasKey('base', $result['$rss']);
		$this->assertArrayHasKey('updated', $result['$rss']);
		$this->assertArrayHasKey('atom_updated', $result['$rss']);
		$this->assertArrayHasKey('language', $result['$rss']);
		$this->assertArrayHasKey('logo', $result['$rss']);
	}

	/**
	 * Test the api_unique_id_to_nurl() function.
	 * @return void
	 */
	public function testApiUniqueIdToNurl()
	{
		$this->assertFalse(api_unique_id_to_nurl($this->wrongUserId));
	}

	/**
	 * Test the api_unique_id_to_nurl() function with a correct ID.
	 * @return void
	 */
	public function testApiUniqueIdToNurlWithCorrectId()
	{
		$this->assertEquals($this->otherUser['nurl'], api_unique_id_to_nurl($this->otherUser['id']));
	}

	/**
	 * Test the api_get_user() function.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUser()
	{
		$user = api_get_user($this->app);
		$this->assertSelfUser($user);
		$this->assertEquals('708fa0', $user['profile_sidebar_fill_color']);
		$this->assertEquals('6fdbe8', $user['profile_link_color']);
		$this->assertEquals('ededed', $user['profile_background_color']);
	}

	/**
	 * Test the api_get_user() function with a Frio schema.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithFrioSchema()
	{
		PConfig::set($this->selfUser['id'], 'frio', 'schema', 'red');
		$user = api_get_user($this->app);
		$this->assertSelfUser($user);
		$this->assertEquals('708fa0', $user['profile_sidebar_fill_color']);
		$this->assertEquals('6fdbe8', $user['profile_link_color']);
		$this->assertEquals('ededed', $user['profile_background_color']);
	}

	/**
	 * Test the api_get_user() function with a custom Frio schema.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithCustomFrioSchema()
	{
		$ret1 = PConfig::set($this->selfUser['id'], 'frio', 'schema', '---');
		$ret2 = PConfig::set($this->selfUser['id'], 'frio', 'nav_bg', '#123456');
		$ret3 = PConfig::set($this->selfUser['id'], 'frio', 'link_color', '#123456');
		$ret4 = PConfig::set($this->selfUser['id'], 'frio', 'background_color', '#123456');
		$user = api_get_user($this->app);
		$this->assertSelfUser($user);
		$this->assertEquals('123456', $user['profile_sidebar_fill_color']);
		$this->assertEquals('123456', $user['profile_link_color']);
		$this->assertEquals('123456', $user['profile_background_color']);
	}

	/**
	 * Test the api_get_user() function with an empty Frio schema.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithEmptyFrioSchema()
	{
		PConfig::set($this->selfUser['id'], 'frio', 'schema', '---');
		$user = api_get_user($this->app);
		$this->assertSelfUser($user);
		$this->assertEquals('708fa0', $user['profile_sidebar_fill_color']);
		$this->assertEquals('6fdbe8', $user['profile_link_color']);
		$this->assertEquals('ededed', $user['profile_background_color']);
	}

	/**
	 * Test the api_get_user() function with an user that is not allowed to use the API.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithoutApiUser()
	{
		$_SERVER['PHP_AUTH_USER'] = 'Test user';
		$_SERVER['PHP_AUTH_PW'] = 'password';
		$_SESSION['allow_api'] = false;
		$this->assertFalse(api_get_user($this->app));
	}

	/**
	 * Test the api_get_user() function with an user ID in a GET parameter.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithGetId()
	{
		$_GET['user_id'] = $this->otherUser['id'];
		$this->assertOtherUser(api_get_user($this->app));
	}

	/**
	 * Test the api_get_user() function with a wrong user ID in a GET parameter.
	 * @return void
	 * @runInSeparateProcess
	 * @expectedException Friendica\Network\HTTPException\BadRequestException
	 */
	public function testApiGetUserWithWrongGetId()
	{
		$_GET['user_id'] = $this->wrongUserId;
		$this->assertOtherUser(api_get_user($this->app));
	}

	/**
	 * Test the api_get_user() function with an user name in a GET parameter.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithGetName()
	{
		$_GET['screen_name'] = $this->selfUser['nick'];
		$this->assertSelfUser(api_get_user($this->app));
	}

	/**
	 * Test the api_get_user() function with a profile URL in a GET parameter.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithGetUrl()
	{
		$_GET['profileurl'] = $this->selfUser['nurl'];
		$this->assertSelfUser(api_get_user($this->app));
	}

	/**
	 * Test the api_get_user() function with an user ID in the API path.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithNumericCalledApi()
	{
		global $called_api;
		$called_api = ['api_path'];
		$this->app->argv[1] = $this->otherUser['id'].'.json';
		$this->assertOtherUser(api_get_user($this->app));
	}

	/**
	 * Test the api_get_user() function with the $called_api global variable.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithCalledApi()
	{
		global $called_api;
		$called_api = ['api', 'api_path'];
		$this->assertSelfUser(api_get_user($this->app));
	}

	/**
	 * Test the api_get_user() function with a valid user.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithCorrectUser()
	{
		$this->assertOtherUser(api_get_user($this->app, $this->otherUser['id']));
	}

	/**
	 * Test the api_get_user() function with a wrong user ID.
	 * @return void
	 * @runInSeparateProcess
	 * @expectedException Friendica\Network\HTTPException\BadRequestException
	 */
	public function testApiGetUserWithWrongUser()
	{
		$this->assertOtherUser(api_get_user($this->app, $this->wrongUserId));
	}

	/**
	 * Test the api_get_user() function with a 0 user ID.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiGetUserWithZeroUser()
	{
		$this->assertSelfUser(api_get_user($this->app, 0));
	}

	/**
	 * Test the api_item_get_user() function.
	 * @return void
	 * @runInSeparateProcess
	 */
	public function testApiItemGetUser()
	{
		$users = api_item_get_user($this->app, []);
		$this->assertSelfUser($users[0]);
	}

	/**
	 * Test the api_item_get_user() function with a different item parent.
	 * @return void
	 */
	public function testApiItemGetUserWithDifferentParent()
	{
		$users = api_item_get_user($this->app, ['thr-parent' => 'item_parent', 'uri' => 'item_uri']);
		$this->assertSelfUser($users[0]);
		$this->assertEquals($users[0], $users[1]);
	}

	/**
	 * Test the api_walk_recursive() function.
	 * @return void
	 */
	public function testApiWalkRecursive()
	{
		$array = ['item1'];
		$this->assertEquals(
			$array,
			api_walk_recursive(
				$array,
				function () {
					// Should we test this with a callback that actually does something?
					return true;
				}
			)
		);
	}

	/**
	 * Test the api_walk_recursive() function with an array.
	 * @return void
	 */
	public function testApiWalkRecursiveWithArray()
	{
		$array = [['item1'], ['item2']];
		$this->assertEquals(
			$array,
			api_walk_recursive(
				$array,
				function () {
					// Should we test this with a callback that actually does something?
					return true;
				}
			)
		);
	}

	/**
	 * Test the api_reformat_xml() function.
	 * @return void
	 */
	public function testApiReformatXml()
	{
		$item = true;
		$key = '';
		$this->assertTrue(api_reformat_xml($item, $key));
		$this->assertEquals('true', $item);
	}

	/**
	 * Test the api_reformat_xml() function with a statusnet_api key.
	 * @return void
	 */
	public function testApiReformatXmlWithStatusnetKey()
	{
		$item = '';
		$key = 'statusnet_api';
		$this->assertTrue(api_reformat_xml($item, $key));
		$this->assertEquals('statusnet:api', $key);
	}

	/**
	 * Test the api_reformat_xml() function with a friendica_api key.
	 * @return void
	 */
	public function testApiReformatXmlWithFriendicaKey()
	{
		$item = '';
		$key = 'friendica_api';
		$this->assertTrue(api_reformat_xml($item, $key));
		$this->assertEquals('friendica:api', $key);
	}

	/**
	 * Test the api_create_xml() function.
	 * @return void
	 */
	public function testApiCreateXml()
	{
		$this->assertEquals(
			'<?xml version="1.0"?>'."\n".
			'<root_element xmlns="http://api.twitter.com" xmlns:statusnet="http://status.net/schema/api/1/" '.
				'xmlns:friendica="http://friendi.ca/schema/api/1/" '.
				'xmlns:georss="http://www.georss.org/georss">'."\n".
				'  <data>some_data</data>'."\n".
			'</root_element>'."\n",
			api_create_xml(['data' => ['some_data']], 'root_element')
		);
	}

	/**
	 * Test the api_create_xml() function without any XML namespace.
	 * @return void
	 */
	public function testApiCreateXmlWithoutNamespaces()
	{
		$this->assertEquals(
			'<?xml version="1.0"?>'."\n".
			'<ok>'."\n".
				'  <data>some_data</data>'."\n".
			'</ok>'."\n",
			api_create_xml(['data' => ['some_data']], 'ok')
		);
	}

	/**
	 * Test the api_format_data() function.
	 * @return void
	 */
	public function testApiFormatData()
	{
		$data = ['some_data'];
		$this->assertEquals($data, api_format_data('root_element', 'json', $data));
	}

	/**
	 * Test the api_format_data() function with an XML result.
	 * @return void
	 */
	public function testApiFormatDataWithXml()
	{
		$this->assertEquals(
			'<?xml version="1.0"?>'."\n".
			'<root_element xmlns="http://api.twitter.com" xmlns:statusnet="http://status.net/schema/api/1/" '.
				'xmlns:friendica="http://friendi.ca/schema/api/1/" '.
				'xmlns:georss="http://www.georss.org/georss">'."\n".
				'  <data>some_data</data>'."\n".
			'</root_element>'."\n",
			api_format_data('root_element', 'xml', ['data' => ['some_data']])
		);
	}

	/**
	 * Test the api_account_verify_credentials() function.
	 * @return void
	 */
	public function testApiAccountVerifyCredentials()
	{
		$this->assertArrayHasKey('user', api_account_verify_credentials('json'));
	}

	/**
	 * Test the api_account_verify_credentials() function without an authenticated user.
	 * @return void
	 * @expectedException Friendica\Network\HTTPException\ForbiddenException
	 */
	public function testApiAccountVerifyCredentialsWithoutAuthenticatedUser()
	{
		$_SESSION['authenticated'] = false;
		api_account_verify_credentials('json');
	}

	/**
	 * Test the requestdata() function.
	 * @return void
	 */
	public function testRequestdata()
	{
		$this->assertNull(requestdata('variable_name'));
	}

	/**
	 * Test the requestdata() function with a POST parameter.
	 * @return void
	 */
	public function testRequestdataWithPost()
	{
		$_POST['variable_name'] = 'variable_value';
		$this->assertEquals('variable_value', requestdata('variable_name'));
	}

	/**
	 * Test the requestdata() function with a GET parameter.
	 * @return void
	 */
	public function testRequestdataWithGet()
	{
		$_GET['variable_name'] = 'variable_value';
		$this->assertEquals('variable_value', requestdata('variable_name'));
	}

	/**
	 * Test the api_statuses_mediap() function.
	 * @return void
	 */
	public function testApiStatusesMediap()
	{
		$this->app->argc = 2;

		$_FILES = [
			'media' => [
				'id' => 666,
				'size' => 666,
				'width' => 666,
				'height' => 666,
				'tmp_name' => $this->getTempImage(),
				'name' => 'spacer.png',
				'type' => 'image/png'
			]
		];
		$_GET['status'] = '<b>Status content</b>';

		$result = api_statuses_mediap('json');
		$this->assertStatus($result['status']);
	}

	/**
	 * Test the api_statuses_mediap() function without an authenticated user.
	 * @return void
	 * @expectedException Friendica\Network\HTTPException\ForbiddenException
	 */
	public function testApiStatusesMediapWithoutAuthenticatedUser()
	{
		$_SESSION['authenticated'] = false;
		api_statuses_mediap('json');
	}

	/**
	 * Test the api_statuses_update() function.
	 * @return void
	 */
	public function testApiStatusesUpdate()
	{
		$_GET['status'] = 'Status content #friendica';
		$_GET['in_reply_to_status_id'] = -1;
		$_GET['lat'] = 48;
		$_GET['long'] = 7;
		$_FILES = [
			'media' => [
				'id' => 666,
				'size' => 666,
				'width' => 666,
				'height' => 666,
				'tmp_name' => $this->getTempImage(),
				'name' => 'spacer.png',
				'type' => 'image/png'
			]
		];

		$result = api_statuses_update('json');
		$this->assertStatus($result['status']);
	}

	/**
	 * Test the api_statuses_update() function with an HTML status.
	 * @return void
	 */
	public function testApiStatusesUpdateWithHtml()
	{
		$_GET['htmlstatus'] = '<b>Status content</b>';

		$result = api_statuses_update('json');
		$this->assertStatus($result['status']);
	}

	/**
	 * Test the api_statuses_update() function without an authenticated user.
	 * @return void
	 * @expectedException Friendica\Network\HTTPException\ForbiddenException
	 */
	public function testApiStatusesUpdateWithoutAuthenticatedUser()
	{
		$_SESSION['authenticated'] = false;
		api_statuses_update('json');
	}

	/**
	 * Test the api_statuses_update() function with a parent status.
	 * @return void
	 */
	public function testApiStatusesUpdateWithParent()
	{
		$this->markTestIncomplete('This triggers an exit() somewhere and kills PHPUnit.');
	}

	/**
	 * Test the api_statuses_update() function with a media_ids parameter.
	 * @return void
	 */
	public function testApiStatusesUpdateWithMediaIds()
	{
		$this->markTestIncomplete();
	}

	/**
	 * Test the api_statuses_update() function with the throttle limit reached.
	 * @return void
	 */
	public function testApiStatusesUpdateWithDayThrottleReached()
	{
		$this->markTestIncomplete();
	}

	/**
	 * Test the api_media_upload() function.
	 * @return void
	 * @expectedException Friendica\Network\HTTPException\BadRequestException
	 */
	public function testApiMediaUpload()
	{
		api_media_upload();
	}

	/**
	 * Test the api_media_upload() function without an authenticated user.
	 * @return void
	 * @expectedException Friendica\Network\HTTPException\ForbiddenException
	 */
	public function testApiMediaUploadWithoutAuthenticatedUser()
	{
		$_SESSION['authenticated'] = false;
		api_media_upload();
	}

	/**
	 * Test the api_media_upload() function with an invalid uploaded media.
	 * @return void
	 * @expectedException Friendica\Network\HTTPException\InternalServerErrorException
	 */
	public function testApiMediaUploadWithMedia()
	{
		$_FILES = [
			'media' => [
				'id' => 666,
				'tmp_name' => 'tmp_name'
			]
		];
		api_media_upload();
	}

	/**
	 * Test the api_media_upload() function with an valid uploaded media.
	 * @return void
	 */
	public function testApiMediaUploadWithValidMedia()
	{
		$_FILES = [
			'media' => [
				'id' => 666,
				'size' => 666,
				'width' => 666,
				'height' => 666,
				'tmp_name' => $this->getTempImage(),
				'name' => 'spacer.png',
				'type' => 'image/png'
			]
		];
		$app = \get_app();
		$app->argc = 2;

		$result = api_media_upload();
		$this->assertEquals('image/png', $result['media']['image']['image_type']);
		$this->assertEquals(1, $result['media']['image']['w']);
		$this->assertEquals(1, $result['media']['image']['h']);
		$this->assertNotEmpty($result['media']['image']['friendica_preview_url']);
	}

	/**
	 * Test the api_status_show() function.
	 * @return void
	 */
	public function testApiStatusShow()
	{
		$result = api_status_show('json');
		$this->assertStatus($result['status']);
	}

	/**
	 * Test the api_status_show() function with an XML result.
	 * @return void
	 */
	public function testApiStatusShowWithXml()
	{
		$result = api_status_show('xml');
		$this->assertXml($result, 'statuses');
	}

	/**
	 * Test the api_status_show() function with a raw result.
	 * @return void
	 */
	public function testApiStatusShowWithRaw()
	{
		$this->assertStatus(api_status_show('raw'));
	}

	/**
	 * Test the api_users_show() function.
	 * @return void
	 */
	public function testApiUsersShow()
	{
		$result = api_users_show('json');
		// We can't use assertSelfUser() here because the user object is missing some properties.
		$this->assertEquals($this->selfUser['id'], $result['user']['cid']);
		$this->assertEquals('DFRN', $result['user']['location']);
		$this->assertEquals($this->selfUser['name'], $result['user']['name']);
		$this->assertEquals($this->selfUser['nick'], $result['user']['screen_name']);
		$this->assertEquals('dfrn', $result['user']['network']);
		$this->assertTrue($result['user']['verified']);
	}

	/**
	 * Test the api_users_show() function with an XML result.
	 * @return void
	 */
	public function testApiUsersShowWithXml()
	{
		$result = api_users_show('xml');
		$this->assertXml($result, 'statuses');
	}

	/**
	 * Test the api_users_search() function.
	 * @return void
	 */
	public function testApiUsersSearch()
	{
		$_GET['q'] = 'othercontact';
		$result = api_users_search('json');
		$this->assertOtherUser($result['users'][0]);
	}

	/**
	 * Test the api_users_search() function with an XML result.
	 * @return void
	 */
	public function testApiUsersSearchWithXml()
	{
		$_GET['q'] = 'othercontact';
		$result = api_users_search('xml');
		$this->assertXml($result, 'users');
	}

	/**
	 * Test the api_users_search() function without a GET q parameter.
	 * @return void
	 * @expectedException Friendica\Network\HTTPException\BadRequestException
	 */
	public function testApiUsersSearchWithoutQuery()
	{
		api_users_search('json');
	}

	/**
	 * Test the api_users_lookup() function.
	 * @return void
	 * @expectedException Friendica\Network\HTTPException\NotFoundException
	 */
	public function testApiUsersLookup()
	{
		api_users_lookup('json');
	}

	/**
	 * Test the api_users_lookup() function with an user ID.
	 * @return void
	 */
	public function testApiUsersLookupWithUserId()
	{
		$_REQUEST['user_id'] = $this->otherUser['id'];
		$result = api_users_lookup('json');
		$this->assertOtherUser($result['users'][0]);
	}

	/**
	 * Test the api_search() function.
	 * @return void
	 */
	public function testApiSearch()
	{
		$_REQUEST['q'] = 'reply';
		$_REQUEST['max_id'] = 10;
		$result = api_search('json');
		foreach ($result['status'] as $status) {
			$this->assertStatus($status);
			$this->assertContains('reply', $status['text'], null, true);
		}
	}

	/**
	 * Test the api_search() function a count parameter.
	 * @return void
	 */
	public function testApiSearchWithCount()
	{
		$_REQUEST['q'] = 'reply';
		$_REQUEST['count'] = 20;
		$result = api_search('json');
		foreach ($result['status'] as $status) {
			$this->assertStatus($status);
			$this->assertContains('reply', $status['text'], null, true);
		}
	}

	/**
	 * Test the api_search() function with an rpp parameter.
	 * @return void
	 */
	public function testApiSearchWithRpp()
	{
		$_REQUEST['q'] = 'reply';
		$_REQUEST['rpp'] = 20;
		$result = api_search('json');
		foreach ($result['status'] as $status) {
			$this->assertStatus($status);
			$this->assertContains('reply', $status['text'], null, true);
		}
	}

	/**
	 * Test the api_search() function with an q parameter contains hashtag.
	 * @return void
	 */
	public function testApiSearchWithHashtag()
	{
		$_REQUEST['q'] = '%23friendica';
		$result = api_search('json');
{'fraction_non_alphanumeric': 0.18120994833865003, 'fraction_numerical': 0.0073398639302148325, 'mean_word_length': 4.191397376712831, 'pattern_counts': {'":': 40, '<': 103, '<?xml version=': 18, '>': 743, 'https://': 2, 'lorem ipsum': 0, 'www.': 10, 'xml': 90}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2101672', 'n_tokens_mistral': 26606, 'n_tokens_neox': 23613, 'n_words': 4665}