oldAuthAcl = $AUTH_ACL; $this->userinfo = $USERINFO; $auth = new AuthPlugin(); $conf['remote'] = 1; $conf['remoteuser'] = '@user'; $conf['useacl'] = 0; $this->remote = new Api(); } public function tearDown(): void { parent::tearDown(); global $USERINFO; global $AUTH_ACL; $USERINFO = $this->userinfo; $AUTH_ACL = $this->oldAuthAcl; } /** * Do an assertion that converts to JSON inbetween * * This lets us compare result objects with arrays */ protected function assertEqualResult($expected, $actual, $msg = '') { // sort object arrays if (is_array($actual) && array_key_exists(0, $actual) && is_object($actual[0])) { sort($actual); sort($expected); } $expected = json_decode(json_encode($expected), true); $actual = json_decode(json_encode($actual), true); $this->assertEquals($expected, $actual, $msg); } // region info // core.getAPIVersion public function testGetAPIVersion() { $this->assertEqualResult( ApiCore::API_VERSION, $this->remote->call('core.getAPIVersion') ); } // core.getWikiVersion public function testGetWikiVersion() { $this->assertEqualResult( getVersion(), $this->remote->call('core.getWikiVersion') ); } // core.getWikiTitle public function testGetWikiTitle() { global $conf; $this->assertEqualResult( $conf['title'], $this->remote->call('core.getWikiTitle') ); } // core.getWikiTime public function testGetWikiTime() { $this->assertEqualsWithDelta( time(), $this->remote->call('core.getWikiTime'), 1 // allow 1 second difference ); } // endregion // region user // core.login public function testLogin() { $this->markTestIncomplete('Missing test for core.login API Call'); } // core.logoff public function testLogoff() { $this->markTestIncomplete('Missing test for core.logoff API Call'); } // core.whoAmI public function testWhoAmI() { $this->markTestIncomplete('Missing test for core.whoAmI API Call'); } // core.aclCheck -> See also ApiCoreAclCheckTest.php public function testAclCheck() { $id = 'aclpage'; $this->assertEquals(AUTH_UPLOAD, $this->remote->call('core.aclCheck', ['page' => $id])); global $conf; global $AUTH_ACL; global $USERINFO; $conf['useacl'] = 1; $_SERVER['REMOTE_USER'] = 'john'; $USERINFO['grps'] = ['user']; $AUTH_ACL = [ '* @ALL 0', '* @user 2', //edit ]; $this->assertEquals(AUTH_EDIT, $this->remote->call('core.aclCheck', ['page' => $id])); } // endregion // region pages // core.listPages public function testlistPagesAll() { // all pages depends on index idx_addPage('wiki:syntax'); idx_addPage('wiki:dokuwiki'); $file1 = wikiFN('wiki:syntax'); $file2 = wikiFN('wiki:dokuwiki'); $expected = [ [ 'id' => 'wiki:syntax', 'title' => 'wiki:syntax', 'permission' => 8, 'size' => filesize($file1), 'revision' => filemtime($file1), 'hash' => md5(io_readFile($file1)), 'author' => '', ], [ 'id' => 'wiki:dokuwiki', 'title' => 'wiki:dokuwiki', 'permission' => 8, 'size' => filesize($file2), 'revision' => filemtime($file2), 'hash' => md5(io_readFile($file2)), 'author' => '', ] ]; $this->assertEqualResult( $expected, $this->remote->call( 'core.listPages', [ 'namespace' => '', 'depth' => 0, // 0 for all 'hash' => true ] ) ); } // core.listPages public function testListPagesNamespace() { $file1 = wikiFN('wiki:syntax'); $file2 = wikiFN('wiki:dokuwiki'); // no indexing needed here global $conf; $conf['useheading'] = 1; $expected = [ [ 'id' => 'wiki:syntax', 'title' => 'Formatting Syntax', 'permission' => 8, 'size' => filesize($file1), 'revision' => filemtime($file1), 'hash' => '', 'author' => '', ], [ 'id' => 'wiki:dokuwiki', 'title' => 'DokuWiki', 'permission' => 8, 'size' => filesize($file2), 'revision' => filemtime($file2), 'hash' => '', 'author' => '', ], ]; $this->assertEqualResult( $expected, $this->remote->call( 'core.listPages', [ 'namespace' => 'wiki:', 'depth' => 1, ] ) ); } // core.searchPages public function testSearchPages() { $id = 'wiki:syntax'; $file = wikiFN($id); idx_addPage($id); //full text search depends on index $expected = [ [ 'id' => $id, 'score' => 1, 'revision' => filemtime($file), 'permission' => 8, 'size' => filesize($file), 'snippet' => ' a footnote)) by using double parentheses. ===== Sectioning ===== You can use up to five different levels of', 'title' => 'wiki:syntax', 'author' => '', 'hash' => '', ] ]; $this->assertEqualResult( $expected, $this->remote->call( 'core.searchPages', [ 'query' => 'Sectioning' ] ) ); } //core.getRecentPageChanges public function testGetRecentPageChanges() { $_SERVER['REMOTE_USER'] = 'testuser'; saveWikiText('pageone', 'test', 'test one'); $rev1 = filemtime(wikiFN('pageone')); saveWikiText('pagetwo', 'test', 'test two'); $rev2 = filemtime(wikiFN('pagetwo')); $expected = [ [ 'id' => 'pageone', 'revision' => $rev1, 'author' => 'testuser', 'sizechange' => 4, 'summary' => 'test one', 'type' => 'C', 'ip' => clientIP(), ], [ 'id' => 'pagetwo', 'revision' => $rev2, 'author' => 'testuser', 'sizechange' => 4, 'summary' => 'test two', 'type' => 'C', 'ip' => clientIP(), ] ]; $this->assertEqualResult( $expected, $this->remote->call( 'core.getRecentPageChanges', [ 'timestamp' => 0 // all recent changes ] ) ); } // core.getPage public function testGetPage() { $id = 'pageversion'; $file = wikiFN($id); saveWikiText($id, 'first version', 'first'); $rev1 = filemtime($file); clearstatcache(false, $file); $this->waitForTick(true); saveWikiText($id, 'second version', 'second'); $rev2 = filemtime($file); $this->assertEqualResult( 'second version', $this->remote->call('core.getPage', ['page' => $id, 'rev' => 0]), 'no revision given -> current' ); $this->assertEqualResult( 'first version', $this->remote->call('core.getPage', ['page' => $id, 'rev' => $rev1]), '1st revision given' ); $this->assertEqualResult( 'second version', $this->remote->call('core.getPage', ['page' => $id, 'rev' => $rev2]), '2nd revision given' ); $this->assertEqualResult( '', $this->remote->call('core.getPage', ['page' => $id, 'rev' => 1234]), 'Non existing revision given' ); $this->assertEqualResult( '', $this->remote->call('core.getPage', ['page' => 'foobar', 'rev' => 1234]), 'Non existing page given' ); } //core.getPageHTML public function testGetPageHTMLVersion() { $id = 'htmltest'; $file = wikiFN($id); $content1 = "====Title====\nText"; $html1 = "\n

Title

\n
\n\n

\nText\n

\n\n
\n"; $content2 = "====Foobar====\nText Bamm"; $html2 = "\n

Foobar

\n
\n\n

\nText Bamm\n

\n\n
\n"; saveWikiText($id, $content1, 'first'); $rev1 = filemtime($file); clearstatcache(false, $file); $this->waitForTick(true); saveWikiText($id, $content2, 'second'); $rev2 = filemtime($file); $this->assertEqualResult( $html2, $this->remote->call('core.getPageHTML', ['page' => $id, 'rev' => 0]), 'no revision given -> current' ); $this->assertEqualResult( $html1, $this->remote->call('core.getPageHTML', ['page' => $id, 'rev' => $rev1]), '1st revision given' ); $this->assertEqualResult( $html2, $this->remote->call('core.getPageHTML', ['page' => $id, 'rev' => $rev2]), '2nd revision given' ); $e = null; try { $this->remote->call('core.getPageHTML', ['page' => $id, 'rev' => 1234]); } catch (RemoteException $e) { } $this->assertInstanceOf(RemoteException::class, $e); $this->assertEquals(121, $e->getCode(), 'Non existing revision given'); $e = null; try { $this->remote->call('core.getPageHTML', ['page' => 'foobar', 'rev' => 1234]); } catch (RemoteException $e) { } $this->assertInstanceOf(RemoteException::class, $e); $this->assertEquals(121, $e->getCode(), 'Non existing page given'); } //core.getPageInfo public function testGetPageInfo() { $id = 'pageinfo'; $file = wikiFN($id); $_SERVER['REMOTE_USER'] = 'testuser'; saveWikiText($id, 'first version', 'first'); $rev1 = filemtime($file); clearstatcache(false, $file); $this->waitForTick(true); saveWikiText($id, 'second version', 'second'); $rev2 = filemtime($file); $expected = [ 'id' => $id, 'revision' => $rev2, 'author' => 'testuser', 'hash' => md5(io_readFile($file)), 'title' => $id, 'size' => filesize($file), 'permission' => 8, ]; $this->assertEqualResult( $expected, $this->remote->call('core.getPageInfo', ['page' => $id, 'rev' => 0, 'hash' => true, 'author' => true]), 'no revision given -> current' ); $expected = [ 'id' => $id, 'revision' => $rev1, 'author' => '', 'hash' => '', 'title' => $id, 'size' => filesize(wikiFN($id, $rev1)), 'permission' => 8, ]; $this->assertEqualResult( $expected, $this->remote->call('core.getPageInfo', ['page' => $id, 'rev' => $rev1]), '1st revision given' ); $expected = [ 'id' => $id, 'revision' => $rev2, 'author' => '', 'hash' => '', 'title' => $id, 'size' => filesize(wikiFN($id, $rev2)), 'permission' => 8, ]; $this->assertEqualResult( $expected, $this->remote->call('core.getPageInfo', ['page' => $id, 'rev' => $rev2]), '2nd revision given' ); $e = null; try { $this->remote->call('core.getPageInfo', ['page' => $id, 'rev' => 1234]); } catch (RemoteException $e) { } $this->assertInstanceOf(RemoteException::class, $e); $this->assertEquals(121, $e->getCode(), 'Non existing revision given'); $e = null; try { $this->remote->call('core.getPageInfo', ['page' => 'foobar', 'rev' => 1234]); } catch (RemoteException $e) { } $this->assertInstanceOf(RemoteException::class, $e); $this->assertEquals(121, $e->getCode(), 'Non existing page given'); } //core.getPageHistory public function testGetPageHistory() { global $conf; $id = 'revpage'; $file = wikiFN($id); $rev = []; for ($i = 0; $i < 6; $i++) { $this->waitForTick(); saveWikiText($id, "rev$i", "rev$i"); clearstatcache(false, $file); $rev[$i] = filemtime($file); } $params = ['page' => $id, 'first' => 0]; $versions = $this->remote->call('core.getPageHistory', $params); $versions = json_decode(json_encode($versions), true); $this->assertEquals(6, count($versions)); $this->assertEquals($rev[5], $versions[0]['revision']); $this->assertEquals($rev[4], $versions[1]['revision']); $this->assertEquals($rev[3], $versions[2]['revision']); $this->assertEquals($rev[2], $versions[3]['revision']); $this->assertEquals($rev[1], $versions[4]['revision']); $this->assertEquals($rev[0], $versions[5]['revision']); $params = ['page' => $id, 'first' => 1]; // offset 1 $versions = $this->remote->call('core.getPageHistory', $params); $versions = json_decode(json_encode($versions), true); $this->assertEquals(5, count($versions)); $this->assertEquals($rev[4], $versions[0]['revision']); $this->assertEquals($rev[3], $versions[1]['revision']); $this->assertEquals($rev[2], $versions[2]['revision']); $this->assertEquals($rev[1], $versions[3]['revision']); $this->assertEquals($rev[0], $versions[4]['revision']); $conf['recent'] = 3; //set number of results per page $params = ['page' => $id, 'first' => 0]; // first page $versions = $this->remote->call('core.getPageHistory', $params); $versions = json_decode(json_encode($versions), true); $this->assertEquals(3, count($versions)); $this->assertEquals($rev[5], $versions[0]['revision']); $this->assertEquals($rev[4], $versions[1]['revision']); $this->assertEquals($rev[3], $versions[2]['revision']); $params = ['page' => $id, 'first' => $conf['recent']]; // second page $versions = $this->remote->call('core.getPageHistory', $params); $versions = json_decode(json_encode($versions), true); $this->assertEquals(3, count($versions)); $this->assertEquals($rev[2], $versions[0]['revision']); $this->assertEquals($rev[1], $versions[1]['revision']); $this->assertEquals($rev[0], $versions[2]['revision']); $params = ['page' => $id, 'first' => $conf['recent'] * 2]; // third page $versions = $this->remote->call('core.getPageHistory', $params); $versions = json_decode(json_encode($versions), true); $this->assertEquals(0, count($versions)); } //core.getPageLinks public function testGetPageLinks() { $localdoku = [ 'type' => 'local', 'page' => 'DokuWiki', 'href' => DOKU_BASE . DOKU_SCRIPT . '?id=DokuWiki' ]; $expected = [ $localdoku, [ 'type' => 'extern', 'page' => 'http://www.freelists.org', 'href' => 'http://www.freelists.org' ], [ 'type' => 'interwiki', 'page' => 'rfc>1855', 'href' => 'https://tools.ietf.org/html/rfc1855' ], [ 'type' => 'extern', 'page' => 'http://www.catb.org/~esr/faqs/smart-questions.html', 'href' => 'http://www.catb.org/~esr/faqs/smart-questions.html' ], $localdoku, $localdoku ]; $this->assertEqualResult( $expected, $this->remote->call('core.getPageLinks', ['page' => 'mailinglist']) ); $this->expectExceptionCode(121); $this->remote->call('core.getPageLinks', ['page' => 'foobar']); } //core.getPageBackLinks public function testGetPageBackLinks() { saveWikiText('linky', '[[wiki:syntax]]', 'test'); // backlinks need index idx_addPage('wiki:syntax'); idx_addPage('linky'); $result = $this->remote->call('core.getPageBackLinks', ['page' => 'wiki:syntax']); $this->assertTrue(count($result) > 0); $this->assertEqualResult(ft_backlinks('wiki:syntax'), $result); $this->assertEquals([], $this->remote->call('core.getPageBackLinks', ['page' => 'foobar'])); } //core.lockPages public function testLockPages() { // lock a first set of pages $_SERVER['REMOTE_USER'] = 'testuser1'; $tolock = ['wiki:dokuwiki', 'nonexisting']; $this->assertEquals( $tolock, $this->remote->call('core.lockPages', ['pages' => $tolock]), 'all pages should lock' ); // now we're someone else $_SERVER['REMOTE_USER'] = 'testuser2'; $tolock = ['wiki:dokuwiki', 'nonexisting', 'wiki:syntax', 'another']; $expected = ['wiki:syntax', 'another']; $this->assertEquals( $expected, $this->remote->call('core.lockPages', ['pages' => $tolock]), 'only half the pages should lock' ); } // core.unlockPages public function testUnlockPages() { $_SERVER['REMOTE_USER'] = 'testuser1'; lock('wiki:dokuwiki'); lock('nonexisting'); $_SERVER['REMOTE_USER'] = 'testuser2'; lock('wiki:syntax'); lock('another'); $tounlock = ['wiki:dokuwiki', 'nonexisting', 'wiki:syntax', 'another', 'notlocked']; $expected = ['wiki:syntax', 'another']; $this->assertEquals( $expected, $this->remote->call('core.unlockPages', ['pages' => $tounlock]) ); } //core.savePage public function testSavePage() { $id = 'putpage'; $content = "====Title====\nText"; $params = [ 'page' => $id, 'text' => $content, 'isminor' => false, 'summary' => 'Summary of nice text' ]; $this->assertTrue($this->remote->call('core.savePage', $params)); $this->assertEquals($content, rawWiki($id)); // remove page $params = [ 'page' => $id, 'text' => '', ]; $this->assertTrue($this->remote->call('core.savePage', $params)); $this->assertFileNotExists(wikiFN($id)); // remove non existing page (reusing above params) $e = null; try { $this->remote->call('core.savePage', $params); } catch (RemoteException $e) { } $this->assertInstanceOf(RemoteException::class, $e); $this->assertEquals(132, $e->getCode()); } //core.appendPage public function testAppendPage() { $id = 'appendpage'; $content = 'a test'; $morecontent = "\nOther text"; saveWikiText($id, $content, 'local'); $params = [ 'page' => $id, 'text' => $morecontent, ]; $this->assertEquals(true, $this->remote->call('core.appendPage', $params)); $this->assertEquals($content . $morecontent, rawWiki($id)); } // endregion // region media // core.listMedia public function testListMedia() { $id = 'wiki:dokuwiki-128.png'; $file = mediaFN($id); $content = file_get_contents($file); $expected = [ [ 'id' => $id, 'size' => filesize($file), 'revision' => filemtime($file), 'isimage' => true, 'hash' => md5($content), 'permission' => 8, 'author' => '', ] ]; $this->assertEqualResult( $expected, $this->remote->call( 'core.listMedia', [ 'namespace' => 'wiki', 'pattern' => '/128/', 'hash' => true, ] ) ); } //core.getRecentMediaChanges public function testGetRecentMediaChanges() { global $conf; $_SERVER['REMOTE_USER'] = 'testuser'; $orig = mediaFN('wiki:dokuwiki-128.png'); $tmp = $conf['tmpdir'] . 'test.png'; $target1 = 'test:image1.png'; $file1 = mediaFN($target1); copy($orig, $tmp); media_save(['name' => $tmp], $target1, true, AUTH_UPLOAD, 'rename'); $target2 = 'test:image2.png'; $file2 = mediaFN($target2); copy($orig, $tmp); media_save(['name' => $tmp], $target2, true, AUTH_UPLOAD, 'rename'); $expected = [ [ 'id' => $target1, 'revision' => filemtime($file1), 'author' => 'testuser', 'ip' => clientIP(), 'sizechange' => filesize($file1), 'summary' => 'created', 'type' => 'C', ], [ 'id' => $target2, 'revision' => filemtime($file2), 'author' => 'testuser', 'ip' => clientIP(), 'sizechange' => filesize($file2), 'summary' => 'created', 'type' => 'C', ] ]; $this->assertEqualResult( $expected, $this->remote->call( 'core.getRecentMediaChanges', [ 'timestamp' => 0 // all recent changes ] ) ); } //core.getMedia public function testGetMedia() { $id = 'wiki:dokuwiki-128.png'; $file = mediaFN($id); $base64 = base64_encode(file_get_contents($file)); $this->assertEquals( $base64, $this->remote->call('core.getMedia', ['media' => $id]) ); $e = null; try { $this->remote->call('core.getMedia', ['media' => $id, 'rev' => 1234]); } catch (RemoteException $e) { } $this->assertInstanceOf(RemoteException::class, $e); $this->assertEquals(221, $e->getCode(), 'Non existing revision given'); $e = null; try { $this->remote->call('core.getMedia', ['media' => 'foobar.png']); } catch (RemoteException $e) { } $this->assertInstanceOf(RemoteException::class, $e); $this->assertEquals(221, $e->getCode(), 'Non existing media id given'); } //core.getMediaInfo public function testGetMediaInfo() { $id = 'wiki:dokuwiki-128.png'; $file = mediaFN($id); $expected = [ 'id' => $id, 'revision' => filemtime($file), 'author' => '', 'hash' => md5(file_get_contents($file)), 'size' => filesize($file), 'permission' => 8, 'isimage' => true, ]; $this->assertEqualResult( $expected, $this->remote->call('core.getMediaInfo', ['media' => $id, 'hash' => true, 'author' => false]) ); $e = null; try { $this->remote->call('core.getMediaInfo', ['media' => $id, 'rev' => 1234]); } catch (RemoteException $e) { } $this->assertInstanceOf(RemoteException::class, $e); $this->assertEquals(221, $e->getCode(), 'Non existing revision given'); $e = null; try { $this->remote->call('core.getMediaInfo', ['media' => 'foobar.png']); } catch (RemoteException $e) { } $this->assertInstanceOf(RemoteException::class, $e); $this->assertEquals(221, $e->getCode(), 'Non existing media id given'); } //core.saveMedia public function testSaveMedia() { $orig = mediaFN('wiki:dokuwiki-128.png'); $base64 = base64_encode(file_get_contents($orig)); $target = 'test:putimage.png'; $targetfile = mediaFN($target); $this->assertTrue($this->remote->call('core.saveMedia', ['media' => $target, 'base64' => $base64])); $this->assertFileExists($targetfile); $this->assertFileEquals($orig, $targetfile); } //core.deleteMedia public function testDeleteMedia() { global $conf; global $AUTH_ACL; global $USERINFO; $id = 'wiki:dokuwiki-128.png'; $file = mediaFN($id); // deletion should fail, we only have AUTH_UPLOAD $e = null; try { $this->remote->call('core.deleteMedia', ['media' => $id]); } catch (AccessDeniedException $e) { } $this->assertInstanceOf(AccessDeniedException::class, $e); $this->assertEquals(212, $e->getCode(), 'No permission to delete'); $this->assertFileExists($file); // setup new ACLs $conf['useacl'] = 1; $_SERVER['REMOTE_USER'] = 'john'; $USERINFO['grps'] = array('user'); $AUTH_ACL = array( '* @ALL 0', '* @user 16', ); // deletion should work now $this->assertTrue($this->remote->call('core.deleteMedia', ['media' => $id])); $this->assertFileNotExists($file); clearstatcache(false, $file); // deleting the file again should not work $e = null; try { $this->remote->call('core.deleteMedia', ['media' => $id]); } catch (RemoteException $e) { } $this->assertInstanceOf(RemoteException::class, $e); $this->assertEquals(221, $e->getCode(), 'Non existing media id given'); } // endregion }