From 33979bd5bc0b5bbf6036f69f98d23e0aadf582c8 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 8 Feb 2013 01:45:19 -0500 Subject: [PATCH] API tests to verify basic query functionality (list & props) * Several tests to verify query with prop=, list=, and generator * Moved query related tests to query\ dir * Added a generic ApiTestCase::editPage() to simplify page creation * Fixed minor warnings complained by jenkins/phpcs Change-Id: I5e3984d797178ae03f048792c7bac8e6a881aa41 --- tests/phpunit/includes/api/ApiTestCase.php | 20 +- .../includes/api/query/ApiQueryBasicTest.php | 392 ++++++++++++++++++ .../api/{ => query}/ApiQueryRevisionsTest.php | 6 +- .../includes/api/{ => query}/ApiQueryTest.php | 0 4 files changed, 413 insertions(+), 5 deletions(-) create mode 100644 tests/phpunit/includes/api/query/ApiQueryBasicTest.php rename tests/phpunit/includes/api/{ => query}/ApiQueryRevisionsTest.php (81%) rename tests/phpunit/includes/api/{ => query}/ApiQueryTest.php (100%) diff --git a/tests/phpunit/includes/api/ApiTestCase.php b/tests/phpunit/includes/api/ApiTestCase.php index fcd581abcc..d152b1b834 100644 --- a/tests/phpunit/includes/api/ApiTestCase.php +++ b/tests/phpunit/includes/api/ApiTestCase.php @@ -41,6 +41,20 @@ abstract class ApiTestCase extends MediaWikiLangTestCase { } + /** + * Edits or creates a page/revision + * @param $pageName string page title + * @param $text string content of the page + * @param $summary string optional summary string for the revision + * @param $defaultNs int optional namespace id + * @return array as returned by WikiPage::doEditContent() + */ + protected function editPage( $pageName, $text, $summary = '', $defaultNs = NS_MAIN ) { + $title = Title::newFromText( $pageName, $defaultNs ); + $page = WikiPage::factory( $title ); + return $page->doEditContent( ContentHandler::makeContent( $text, $title ), $summary ); + } + /** * Does the API request and returns the result. * @@ -102,6 +116,8 @@ abstract class ApiTestCase extends MediaWikiLangTestCase { * @param $params Array: key-value API params * @param $session Array|null: session array * @param $user User|null A User object for the context + * @return result of the API call + * @throws Exception in case wsToken is not set in the session */ protected function doApiRequestWithToken( array $params, array $session = null, User $user = null ) { global $wgRequest; @@ -161,7 +177,9 @@ abstract class ApiTestCase extends MediaWikiLangTestCase { } class UserWrapper { - public $userName, $password, $user; + public $userName; + public $password; + public $user; public function __construct( $userName, $password, $group = '' ) { $this->userName = $userName; diff --git a/tests/phpunit/includes/api/query/ApiQueryBasicTest.php b/tests/phpunit/includes/api/query/ApiQueryBasicTest.php new file mode 100644 index 0000000000..b7dac06091 --- /dev/null +++ b/tests/phpunit/includes/api/query/ApiQueryBasicTest.php @@ -0,0 +1,392 @@ +@gmail.com" + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * + * These tests validate basic functionality of the api query module + * + * @group API + * @group Database + * @group medium + */ +class ApiQueryBasicTest extends ApiTestCase { + + /** + * Create a set of pages. These must not change, otherwise the tests might give wrong results. + * @see MediaWikiTestCase::addDBData() + */ + function addDBData() { + try { + if ( Title::newFromText( 'AQBT-All' )->exists() ) { + return; + } + + // Ordering is important, as it will be returned in the same order as stored in the index + $this->editPage( 'AQBT-All', '[[Category:AQBT-Cat]] [[AQBT-Links]] {{AQBT-T}}' ); + $this->editPage( 'AQBT-Categories', '[[Category:AQBT-Cat]]' ); + $this->editPage( 'AQBT-Links', '[[AQBT-All]] [[AQBT-Categories]] [[AQBT-Templates]]' ); + $this->editPage( 'AQBT-Templates', '{{AQBT-T}}' ); + $this->editPage( 'AQBT-T', 'Content', '', NS_TEMPLATE ); + + // Refresh due to the bug with listing transclusions as links if they don't exist + $this->editPage( 'AQBT-All', '[[Category:AQBT-Cat]] [[AQBT-Links]] {{AQBT-T}}' ); + $this->editPage( 'AQBT-Templates', '{{AQBT-T}}' ); + } catch ( Exception $e ) { + $this->exceptionFromAddDBData = $e; + } + } + + private static $links = array( + array( 'prop' => 'links', 'titles' => 'AQBT-All' ), + array( 'pages' => array( + '1' => array( + 'pageid' => 1, + 'ns' => 0, + 'title' => 'AQBT-All', + 'links' => array( + array( 'ns' => 0, 'title' => 'AQBT-Links' ), + ) ) ) ) ); + + private static $templates = array( + array( 'prop' => 'templates', 'titles' => 'AQBT-All' ), + array( 'pages' => array( + '1' => array( + 'pageid' => 1, + 'ns' => 0, + 'title' => 'AQBT-All', + 'templates' => array( + array( 'ns' => 10, 'title' => 'Template:AQBT-T' ), + ) ) ) ) ); + + private static $categories = array( + array( 'prop' => 'categories', 'titles' => 'AQBT-All' ), + array( 'pages' => array( + '1' => array( + 'pageid' => 1, + 'ns' => 0, + 'title' => 'AQBT-All', + 'categories' => array( + array( 'ns' => 14, 'title' => 'Category:AQBT-Cat' ), + ) ) ) ) ); + + private static $allpages = array( + array( 'list' => 'allpages', 'apprefix' => 'AQBT-' ), + array( 'allpages' => array( + array( 'pageid' => 1, 'ns' => 0, 'title' => 'AQBT-All' ), + array( 'pageid' => 2, 'ns' => 0, 'title' => 'AQBT-Categories' ), + array( 'pageid' => 3, 'ns' => 0, 'title' => 'AQBT-Links' ), + array( 'pageid' => 4, 'ns' => 0, 'title' => 'AQBT-Templates' ), + ) ) ); + + private static $alllinks = array( + array( 'list' => 'alllinks', 'alprefix' => 'AQBT-' ), + array( 'alllinks' => array( + array( 'ns' => 0, 'title' => 'AQBT-All' ), + array( 'ns' => 0, 'title' => 'AQBT-Categories' ), + array( 'ns' => 0, 'title' => 'AQBT-Links' ), + array( 'ns' => 0, 'title' => 'AQBT-Templates' ), + ) ) ); + + private static $alltransclusions = array( + array( 'list' => 'alltransclusions', 'atprefix' => 'AQBT-' ), + array( 'alltransclusions' => array( + array( 'ns' => 10, 'title' => 'Template:AQBT-T' ), + array( 'ns' => 10, 'title' => 'Template:AQBT-T' ), + ) ) ); + + private static $allcategories = array( + array( 'list' => 'allcategories', 'acprefix' => 'AQBT-' ), + array( 'allcategories' => array( + array( '*' => 'AQBT-Cat' ), + ) ) ); + + private static $backlinks = array( + array( 'list' => 'backlinks', 'bltitle' => 'AQBT-Links' ), + array( 'backlinks' => array( + array( 'pageid' => 1, 'ns' => 0, 'title' => 'AQBT-All' ), + ) ) ); + + private static $embeddedin = array( + array( 'list' => 'embeddedin', 'eititle' => 'Template:AQBT-T' ), + array( 'embeddedin' => array( + array( 'pageid' => 1, 'ns' => 0, 'title' => 'AQBT-All' ), + array( 'pageid' => 4, 'ns' => 0, 'title' => 'AQBT-Templates' ), + ) ) ); + + private static $categorymembers = array( + array( 'list' => 'categorymembers', 'cmtitle' => 'Category:AQBT-Cat' ), + array( 'categorymembers' => array( + array( 'pageid' => 1, 'ns' => 0, 'title' => 'AQBT-All' ), + array( 'pageid' => 2, 'ns' => 0, 'title' => 'AQBT-Categories' ), + ) ) ); + + private static $generatorAllpages = array( + array( 'generator' => 'allpages', 'gapprefix' => 'AQBT-' ), + array( 'pages' => array( + '1' => array( + 'pageid' => 1, + 'ns' => 0, + 'title' => 'AQBT-All' ), + '2' => array( + 'pageid' => 2, + 'ns' => 0, + 'title' => 'AQBT-Categories' ), + '3' => array( + 'pageid' => 3, + 'ns' => 0, + 'title' => 'AQBT-Links' ), + '4' => array( + 'pageid' => 4, + 'ns' => 0, + 'title' => 'AQBT-Templates' ), + ) ) ); + + private static $generatorLinks = array( + array( 'generator' => 'links', 'titles' => 'AQBT-Links' ), + array( 'pages' => array( + '1' => array( + 'pageid' => 1, + 'ns' => 0, + 'title' => 'AQBT-All' ), + '2' => array( + 'pageid' => 2, + 'ns' => 0, + 'title' => 'AQBT-Categories' ), + '4' => array( + 'pageid' => 4, + 'ns' => 0, + 'title' => 'AQBT-Templates' ), + ) ) ); + + private static $generatorLinksPropLinks = array( + array( 'prop' => 'links' ), + array( 'pages' => array( + '1' => array( 'links' => array( + array( 'ns' => 0, 'title' => 'AQBT-Links' ), + ) ) ) ) ); + + private static $generatorLinksPropTemplates = array( + array( 'prop' => 'templates' ), + array( 'pages' => array( + '1' => array( 'templates' => array( + array( 'ns' => 10, 'title' => 'Template:AQBT-T' ) ) ), + '4' => array( 'templates' => array( + array( 'ns' => 10, 'title' => 'Template:AQBT-T' ) ) ), + ) ) ); + + /** + * Test basic props + */ + public function testProps() { + $this->check( self::$links ); + $this->check( self::$templates ); + $this->check( self::$categories ); + } + + /** + * Test basic lists + */ + public function testLists() { + $this->check( self::$allpages ); + $this->check( self::$alllinks ); + $this->check( self::$alltransclusions ); + // This test is temporarily disabled until a sqlite bug is fixed + // $this->check( self::$allcategories ); + $this->check( self::$backlinks ); + $this->check( self::$embeddedin ); + $this->check( self::$categorymembers ); + } + + /** + * Test basic lists + */ + public function testAllTogether() { + + // All props together + $this->check( $this->merge( + self::$links, + self::$templates, + self::$categories + ) ); + + // All lists together + $this->check( $this->merge( + self::$allpages, + self::$alllinks, + self::$alltransclusions, + // This test is temporarily disabled until a sqlite bug is fixed + // self::$allcategories, + self::$backlinks, + self::$embeddedin, + self::$categorymembers + ) ); + + // All props+lists together + $this->check( $this->merge( + self::$links, + self::$templates, + self::$categories, + self::$allpages, + self::$alllinks, + self::$alltransclusions, + // This test is temporarily disabled until a sqlite bug is fixed + // self::$allcategories, + self::$backlinks, + self::$embeddedin, + self::$categorymembers + ) ); + } + + /** + * Test basic lists + */ + public function testGenerator() { + // generator=allpages + $this->check( self::$generatorAllpages ); + // generator=allpages & list=allpages + $this->check( $this->merge( + self::$generatorAllpages, + self::$allpages ) ); + // generator=links + $this->check( self::$generatorLinks ); + // generator=links & prop=links + $this->check( $this->merge( + self::$generatorLinks, + self::$generatorLinksPropLinks ) ); + // generator=links & prop=templates + $this->check( $this->merge( + self::$generatorLinks, + self::$generatorLinksPropTemplates ) ); + // generator=links & prop=links|templates + $this->check( $this->merge( + self::$generatorLinks, + self::$generatorLinksPropLinks, + self::$generatorLinksPropTemplates ) ); + // generator=links & prop=links|templates & list=allpages|... + $this->check( $this->merge( + self::$generatorLinks, + self::$generatorLinksPropLinks, + self::$generatorLinksPropTemplates, + self::$allpages, + self::$alllinks, + self::$alltransclusions, + // This test is temporarily disabled until a sqlite bug is fixed + // self::$allcategories, + self::$backlinks, + self::$embeddedin, + self::$categorymembers ) ); + } + + /** + * Merges all requests (parameter arrays) into one + * @return array + */ + private function merge( /*...*/ ) { + $request = array(); + $expected = array(); + foreach ( func_get_args() as $v ) { + $request = array_merge_recursive( $request, $v[0] ); + $this->mergeExpected( $expected, $v[1] ); + } + return array( $request, $expected ); + } + + /** + * Recursively merges the expected values in the $item into the $all + */ + private function mergeExpected( &$all, $item ) { + foreach ( $item as $k => $v ) { + if ( array_key_exists( $k, $all ) ) { + if ( is_array ( $all[$k] ) ) { + $this->mergeExpected( $all[$k], $v ); + } else { + $this->assertEquals( $all[$k], $v ); + } + } else { + $all[$k] = $v; + } + } + } + + /** + * Checks that the request's result matches the expected results. + * @param $values array is a two element array( request, expected_results ) + * @throws Exception + */ + private function check( $values ) { + $request = $values[0]; + $expected = $values[1]; + if ( !array_key_exists( 'action', $request ) ) { + $request['action'] = 'query'; + } + foreach ( $request as &$val ) { + if ( is_array( $val ) ) { + $val = implode( '|', array_unique( $val ) ); + } + } + $result = $this->doApiRequest( $request ); + $result = $result[0]; + $expected = array( 'query' => $expected ); + try { + $this->assertQueryResults( $expected, $result ); + } catch (Exception $e) { + print("\nRequest:\n"); + print_r( $request ); + print("\nExpected:\n"); + print_r( $expected ); + print("\nResult:\n"); + print_r( $result ); + throw $e; // rethrow it + } + } + + /** + * Recursively compare arrays, ignoring mismatches in numeric key and pageids. + * @param $expected array expected values + * @param $result array returned values + */ + private function assertQueryResults( $expected, $result ) { + reset( $expected ); + reset( $result ); + while ( true ) { + $e = each( $expected ); + $r = each( $result ); + // If either of the arrays is shorter, abort. If both are done, success. + $this->assertEquals( (bool)$e, (bool)$r ); + if ( !$e ) { + break; // done + } + // continue only if keys are identical or both keys are numeric + $this->assertTrue( $e['key'] === $r['key'] || ( is_numeric( $e['key'] ) && is_numeric( $r['key'] ) ) ); + // don't compare pageids + if ( $e['key'] !== 'pageid' ) { + // If values are arrays, compare recursively, otherwise compare with === + if ( is_array( $e['value'] ) && is_array( $r['value'] ) ) { + $this->assertQueryResults( $e['value'], $r['value'] ); + } else { + $this->assertEquals( $e['value'], $r['value'] ); + } + } + } + } +} diff --git a/tests/phpunit/includes/api/ApiQueryRevisionsTest.php b/tests/phpunit/includes/api/query/ApiQueryRevisionsTest.php similarity index 81% rename from tests/phpunit/includes/api/ApiQueryRevisionsTest.php rename to tests/phpunit/includes/api/query/ApiQueryRevisionsTest.php index 19da81c737..594dc66b8a 100644 --- a/tests/phpunit/includes/api/ApiQueryRevisionsTest.php +++ b/tests/phpunit/includes/api/query/ApiQueryRevisionsTest.php @@ -29,12 +29,10 @@ class ApiQueryRevisionsTest extends ApiTestCase { $this->assertArrayHasKey( 'revisions', $page ); foreach( $page['revisions'] as $revision ) { $this->assertArrayHasKey( 'contentformat', $revision, - 'contentformat should be included when asking content so' - . ' client knows how to interpretate it' + 'contentformat should be included when asking content so client knows how to interpret it' ); $this->assertArrayHasKey( 'contentmodel', $revision, - 'contentmodel should be included when asking content so' - . ' client knows how to interpretate it' + 'contentmodel should be included when asking content so client knows how to interpret it' ); } } diff --git a/tests/phpunit/includes/api/ApiQueryTest.php b/tests/phpunit/includes/api/query/ApiQueryTest.php similarity index 100% rename from tests/phpunit/includes/api/ApiQueryTest.php rename to tests/phpunit/includes/api/query/ApiQueryTest.php -- 2.20.1