Addshore has uploaded a new change for review. https://gerrit.wikimedia.org/r/81003
Change subject: Refactor GetEntities Test ...................................................................... Refactor GetEntities Test Change-Id: I8660112f8aae634e835d614a148fb40c52284556 --- M repo/tests/phpunit/includes/api/EntityTestHelper.php M repo/tests/phpunit/includes/api/GetEntitiesTest.php M repo/tests/phpunit/includes/api/WikibaseApiTestCase.php 3 files changed, 253 insertions(+), 631 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Wikibase refs/changes/03/81003/6 diff --git a/repo/tests/phpunit/includes/api/EntityTestHelper.php b/repo/tests/phpunit/includes/api/EntityTestHelper.php index 886548b..d064c24 100644 --- a/repo/tests/phpunit/includes/api/EntityTestHelper.php +++ b/repo/tests/phpunit/includes/api/EntityTestHelper.php @@ -34,6 +34,10 @@ */ private static $activeHandles = array(); /** + * @var array of currently active ids and their current handles + */ + private static $activeIds; + /** * @var array handles and any registered default output data */ private static $entityOutput = array(); @@ -225,21 +229,51 @@ return self::$entityData[ $handle ]['data']; } - public static function getEntityOutput( $handle ){ + public static function getEntityOutput( $handle, $props = null, $langs = null ){ if( !array_key_exists( $handle, self::$entityOutput ) ){ throw new \MWException( "No entity output defined with handle {$handle}" ); } - return self::$entityOutput[ $handle ]; + if( !is_array( $props ) ){ + return self::$entityOutput[ $handle ]; + } else { + $entityProps = array(); + foreach( $props as $prop ){ + if( array_key_exists( $prop, self::$entityOutput[ $handle ] ) ){ + $entityProps[ $prop ] = array( self::$entityOutput[ $handle ][ $prop ] ); + } + } + foreach( $entityProps as $prop => $value ){ + $value = $value[0]; + if( ( $prop == 'aliases' || $prop == 'labels' || $prop == 'descriptions' ) && $langs != null && is_array( $langs ) ){ + $langValues = array(); + foreach( $langs as $langCode ){ + if( array_key_exists( $langCode, $value ) ){ + $langValues[ $langCode ] = $value[ $langCode ]; + } + } + if( $langValues === array() ){ + unset( $entityProps[ $prop ] ); + } else { + $entityProps[ $prop ] = $langValues; + } + + } + } + return $entityProps; + } + } public static function registerEntity( $handle, $id, $entity = null) { self::$activeHandles[ $handle ] = $id; + self::$activeIds[ $id ] = $handle; if( $entity ){ self::$entityOutput[ $handle ] = $entity; } } private static function unRegisterEntity( $handle ) { + unset( self::$activeIds[ self::$activeHandles[ $handle ] ] ); unset( self::$activeHandles[ $handle ] ); } @@ -262,4 +296,15 @@ return null; } + /** + * @param $id string of entityid + * @return null|string id of current handle (if active) + */ + public static function getHandle( $id ){ + if( array_key_exists( $id, self::$activeIds ) ){ + return self::$activeIds[ $id ]; + } + return null; + } + } \ No newline at end of file diff --git a/repo/tests/phpunit/includes/api/GetEntitiesTest.php b/repo/tests/phpunit/includes/api/GetEntitiesTest.php index 00372a7..4bd97a2 100644 --- a/repo/tests/phpunit/includes/api/GetEntitiesTest.php +++ b/repo/tests/phpunit/includes/api/GetEntitiesTest.php @@ -4,7 +4,7 @@ use ApiTestCase; /** - * Tests for the ApiWikibase class. + * Tests for the ApiWikibase Getentities class. * * 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 @@ -28,9 +28,7 @@ * @ingroup Test * * @licence GNU GPL v2+ - * @author John Erling Blad < jeb...@gmail.com > - * @author Daniel Kinzler - * @author Marius Hoch < h...@online.de > + * @author Adam Shorland * * @group API * @group Wikibase @@ -50,7 +48,7 @@ class GetEntitiesTest extends WikibaseApiTestCase { private static $hasSetup; - private static $usedHandles = array( 'Berlin', 'London', 'Oslo', 'Leipzig', 'Empty' ); + private static $usedHandles = array( 'Berlin', 'London', 'Oslo' ); public function setup() { parent::setup(); @@ -62,663 +60,243 @@ } /** - * @dataProvider provideEntityHandles + * Test cases are generated using the data provided in the various static arrays below + * Adding one extra element to any of the arrays (except format) will generate 4 new tests */ - function testGetItemById( $handle ) { - $id = EntityTestHelper::getId( $handle ); - $item = EntityTestHelper::getEntityOutput( $handle ); + protected static $goodItems = array( + //p = params, e = expected + //handles will automatically be converted to ids + array( 'p' => array( 'ids' => 'q999999999' ), 'e' => array( 'count' => 1, 'missing' => 1 ) ), + array( 'p' => array( 'ids' => 'q999999999|q7777777' ), 'e' => array( 'count' => 2, 'missing' => 2 ) ), + array( 'p' => array( 'sites' => 'enwiki', 'titles' => 'IDoNotExist' ), 'e' => array( 'count' => 1, 'missing' => 1 ) ), + array( 'p' => array( 'sites' => 'enwiki', 'titles' => 'IDoNotExist|ImNotHere' ), 'e' => array( 'count' => 2, 'missing' => 2 ) ), + array( 'p' => array( 'handles' => array( 'Berlin' ) ), 'e' => array( 'count' => 1 ) ), + array( 'p' => array( 'handles' => array( 'London', 'Oslo' ) ), 'e' => array( 'count' => 2 ) ), + array( 'p' => array( 'handles' => array( 'London', 'London' ) ), 'e' => array( 'count' => 1 ) ), + array( 'p' => array( 'sites' => 'dewiki', 'titles' => 'Berlin' ), 'e' => array( 'count' => 1 ) ), + array( 'p' => array( 'sites' => 'dewiki', 'titles' => 'Berlin|London' ), 'e' => array( 'count' => 2 ) ), + array( 'p' => array( 'sites' => 'dewiki|enwiki', 'titles' => 'Oslo' ), 'e' => array( 'count' => 1 ) ), + array( 'p' => array( 'sites' => 'dewiki|enwiki', 'titles' => 'Oslo|London' ), 'e' => array( 'count' => 2 ) ), + array( 'p' => array( 'handles' => array( 'Berlin' ), 'sites' => 'dewiki|enwiki', 'titles' => 'Oslo|London' ), 'e' => array( 'count' => 3 ) ), + array( 'p' => array( 'sites' => 'dewiki', 'titles' => 'berlin', 'normalize' => '' ), 'e' => array( 'count' => 1, 'normalized' => true ) ), + array( 'p' => array( 'sites' => 'dewiki', 'titles' => 'Berlin', 'normalize' => '' ), 'e' => array( 'count' => 1, 'normalized' => false ) ), + ); + protected static $goodProps = array( 'info', 'sitelinks', 'aliases', 'labels', 'descriptions', 'claims', 'datatype', 'labels|sitelinks', 'info|aliases|labels|claims' ); + protected static $goodLangs = array( 'de', 'zh', 'it|es|zh|ar', 'de|nn|no|en|en-gb|it|es|zh|ar' ); + protected static $goodSorts = array( array( 'sort' => 'sitelinks', 'dir' => 'descending' ), array( 'sort' => 'sitelinks', 'dir' => 'ascending' ) ); + protected static $goodFormats = array( 'json', 'php', 'wddx', 'xml', 'yaml', 'txt', 'dbg', 'dump' ); - list($res,,) = $this->doApiRequest( - array( - 'action' => 'wbgetentities', - 'format' => 'json', // make sure IDs are used as keys - 'ids' => $id ) - ); + public static function provideData() { + $testCases = array(); - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $id ); - $this->assertEntityEquals( $item, $res['entities'][$id] ); - $this->assertEquals( 1, count( $res['entities'] ), "requesting a single item should return exactly one item entry" ); - // This should be correct for all items we are testing - $this->assertEquals( \Wikibase\Item::ENTITY_TYPE, $res['entities'][$id]['type'] ); - // The following comes from the props=info which is included by default - // Only check if they are there and seems valid, can't do much more for the moment (or could for title but then we are testing assumptions) - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $id, 'pageid' ); - $this->assertTrue( is_integer( $res['entities'][$id]['pageid'] ) ); - $this->assertTrue( 0 < $res['entities'][$id]['pageid'] ); - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $id, 'ns' ); - $this->assertTrue( is_integer( $res['entities'][$id]['ns'] ) ); - $this->assertTrue( 0 <= $res['entities'][$id]['ns'] ); - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $id, 'title' ); - $this->assertTrue( is_string( $res['entities'][$id]['title'] ) ); - $this->assertTrue( 0 < strlen( $res['entities'][$id]['title'] ) ); - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $id, 'lastrevid' ); - $this->assertTrue( is_integer( $res['entities'][$id]['lastrevid'] ) ); - $this->assertTrue( 0 < $res['entities'][$id]['lastrevid'] ); - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $id, 'modified' ); - $this->assertTrue( is_string( $res['entities'][$id]['modified'] ) ); - $this->assertTrue( 0 < strlen( $res['entities'][$id]['modified'] ) ); - $this->assertRegExp( '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/', - $res['entities'][$id]['modified'], "should be in ISO 8601 format" ); - - //@todo: check non-item - } - - /** - * Verify that we only get one item if we ask for the same item several - * times in different forms. - * - * @dataProvider provideEntityHandles - */ - function testGetItemByIdUnique( $handle ) { - $id = EntityTestHelper::getId( $handle ); - $item = EntityTestHelper::getEntityOutput( $handle ); - - $ids = array( - strtoupper( $id ), - strtolower( $id ), - $id, $id - ); - $ids = join( '|', $ids ); - - list($res,,) = $this->doApiRequest( - array( - 'action' => 'wbgetentities', - 'format' => 'json', // make sure IDs are used as keys - 'ids' => $ids ) - ); - - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $id ); - $this->assertEntityEquals( $item, $res['entities'][$id] ); - $this->assertEquals( 1, count( $res['entities'] ) ); - } - - /** - * data provider for passing each entity handle to the test function. - */ - public static function provideEntityHandles() { - $handles = array(); - foreach( self::$usedHandles as $handle ){ - $handles[] = array( $handle ); - } - return $handles; - } - - public static function provideGetItemByTitle() { - $calls = array(); - - foreach ( self::$usedHandles as $handle ) { - $item = EntityTestHelper::getEntityData( $handle ); - - if ( !array_key_exists( 'sitelinks', $item ) ) { - continue; - } - - foreach ( $item['sitelinks'] as $sitelink ) { - $calls[] = array( $handle, $sitelink['site'], $sitelink['title'] ); + // Generate test cases based on the static information provided in arrays above + foreach( self::$goodItems as $itemData ){ + foreach( self::$goodProps as $propData ){ + foreach( self::$goodLangs as $langData ){ + foreach( self::$goodSorts as $sortData ){ + $testCase['p'] = $itemData['p']; + $testCase['e'] = $itemData['e']; + $testCase['p']['props'] = $propData; + $testCase['p']['languages'] = $langData; + $testCase['p'] = array_merge( $testCase['p'], $sortData ); + $testCases[] = $testCase; + } + } } } - return $calls; + // We only want to test each format once so don't include this in the main generation loop + foreach( self::$goodFormats as $formatData ){ + $testCase = $testCases[0]; + $testCase['p']['format'] = $formatData; + $testCases[] = $testCase; + } + + return $testCases; } /** - * Test basic lookup of items to get the id. - * This is really a fast lookup without reparsing the stringified item. - * - * @dataProvider provideGetItemByTitle + * This method tests all valid API requests + * @dataProvider provideData */ - public function testGetItemByTitle( $handle, $site, $title ) { - list($res,,) = $this->doApiRequest( array( - 'action' => 'wbgetentities', - 'sites' => $site, - 'titles' => $title, - 'format' => 'json', // make sure IDs are used as keys - ) ); - - $id = EntityTestHelper::getId( $handle ); - $item = EntityTestHelper::getEntityOutput( $handle ); - - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $id ); - $this->assertEntityEquals( $item, $res['entities'][$id] ); - $this->assertEquals( 1, count( $res['entities'] ), "requesting a single item should return exactly one item entry" ); - } - - /** - * Test basic lookup of items to get the id. - * This is really a fast lookup without reparsing the stringified item. - * - * @dataProvider provideGetItemByTitle - */ - public function testGetItemByTitleNormalized( $handle, $site, $title ) { - $title = lcfirst( $title ); - list($res,,) = $this->doApiRequest( array( - 'action' => 'wbgetentities', - 'sites' => $site, - 'titles' => $title, - 'normalize' => true, - 'format' => 'json', // make sure IDs are used as keys - ) ); - - $id = EntityTestHelper::getId( $handle ); - $item = EntityTestHelper::getEntityOutput( $handle ); - - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $id ); - $this->assertEntityEquals( $item, $res['entities'][$id] ); - $this->assertEquals( 1, count( $res['entities'] ), "requesting a single item should return exactly one item entry" ); - - // The normalization that has been applied should be noted - $this->assertEquals( - $title, - $res['normalized']['n']['from'] - ); - - $this->assertEquals( - // Normalization in unit tests is actually using Title::getPrefixedText instead of a real API call - \Title::newFromText( $title )->getPrefixedText(), - $res['normalized']['n']['to'] - ); - } - - /** - * @return array - */ - public static function provideGetEntitiesNormalizationNotAllowed() { - return array( - array( - // Two sites one page - array( 'enwiki', 'dewiki' ), - array( 'Foo' ) - ), - array( - // Two pages one site - array( 'enwiki' ), - array( 'Foo', 'Bar' ) - ), - array( - // Two sites two pages - array( 'enwiki', 'dewiki' ), - array( 'Foo', 'Bar' ) - ) - ); - } - - /** - * Test that the API is throwing an error if the users wants to - * normalize with multiple sites/ pages. - * - * @group API - * @dataProvider provideGetEntitiesNormalizationNotAllowed - * - * @param array $sites - * @param array $titles - */ - public function testGetEntitiesNormalizationNotAllowed( $sites, $titles ) { - $this->setExpectedException( 'UsageException' ); - - list($res,,) = $this->doApiRequest( array( - 'action' => 'wbgetentities', - 'sites' => join( '|', $sites ), - 'titles' => join( '|', $titles ), - 'normalize' => true - ) ); - } - - /** - * Test that the API isn't showing the normalization note in case nothing changed. - * - * @group API - */ - public function testGetEntitiesNoNormalizationApplied( ) { - list($res,,) = $this->doApiRequest( array( - 'action' => 'wbgetentities', - 'sites' => 'enwiki', - 'titles' => 'HasNoItemAndIsNormalized', - 'normalize' => true - ) ); - - $this->assertFalse( isset( $res['normalized'] ) ); - } - - /** - * Testing if we can get missing items if we do lookup with single fake ids. - * Note that this makes assumptions about which ids have been assigned. - * - * @group API - */ - public function testGetEntitiesByBadId( ) { - $badid = 'q123456789'; - list( $res,, ) = $this->doApiRequest( array( - 'action' => 'wbgetentities', - 'ids' => $badid, - ) ); - - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $badid, 'missing' ); - $this->assertEquals( 1, count( $res['entities'] ), "requesting a single item should return exactly one item entry" ); - } - - /** - * Testing behavior for malformed entity ids. - * - * @group API - */ - public function testGetEntitiesByMalformedId( ) { - $this->setExpectedException( 'UsageException' ); - $badid = 'xyz123+++'; - $this->doApiRequest( array( - 'action' => 'wbgetentities', - 'ids' => $badid, - ) ); - } - - /** - * Testing if we can get an item using a site that is not part of the group supported for sitelinks. - * Note that this makes assumptions about which sitelinks exist in the test items. - * - * @group API - */ - public function testGetEntitiesByBadSite( ) { - $this->setExpectedException( 'UsageException' ); - $this->doApiRequest( array( - 'action' => 'wbgetentities', - 'sites' => 'enwiktionary', - 'titles' => 'Berlin', - ) ); - } - - /** - * Testing if we can get missing items if we do lookup with failing titles. - * Note that this makes assumptions about which sitelinks they have been assigned. - * - * @group API - */ - public function testGetEntitiesByBadTitle( ) { - list( $res,, ) = $this->doApiRequest( array( - 'action' => 'wbgetentities', - 'sites' => 'enwiki', - 'titles' => 'klaijehrnqowienxcqopweiu', - ) ); - - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities' ); - - $keys = array_keys( $res['entities'] ); - $this->assertEquals( 1, count( $keys ), "requesting a single item should return exactly one item entry" ); - - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $keys[0], 'missing' ); - } - - /** - * Testing if we can get missing items if we do lookup with failing titles and - * that the normalization that has been applied is being noted correctly. - * - * Note that this makes assumptions about which sitelinks they have been assigned. - * - * @group API - */ - public function testGetEntitiesByBadTitleNormalized( ) { - $pageTitle = 'klaijehr qowienx_qopweiu'; - list( $res,, ) = $this->doApiRequest( array( - 'action' => 'wbgetentities', - 'sites' => 'enwiki', - 'titles' => $pageTitle, - 'normalize' => true - ) ); - - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities' ); - - $keys = array_keys( $res['entities'] ); - $this->assertEquals( 1, count( $keys ), "requesting a single item should return exactly one item entry" ); - - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $keys[0], 'missing' ); - - // The normalization that has been applied should be noted - $this->assertEquals( - $pageTitle, - $res['normalized']['n']['from'] - ); - - $this->assertEquals( - // Normalization in unit tests is actually using Title::getPrefixedText instead of a real API call - \Title::newFromText( $pageTitle )->getPrefixedText(), - $res['normalized']['n']['to'] - ); - } - - /** - * Testing if we can get all the complete stringified items if we do lookup with multiple ids. - * - * @group API - */ - public function testGetEntitiesMultipleIds() { + function testGetEntities( $params, $expected ){ + // -- set any defaults params -------------------------------------------------------- + //set the action + $params['action'] = 'wbgetentities'; + //get any ids from handles $ids = array(); - foreach( self::$usedHandles as $handle ){ - $ids[] = EntityTestHelper::getId( $handle ); - } - - list( $res,, ) = $this->doApiRequest( array( - 'action' => 'wbgetentities', - 'format' => 'json', // make sure IDs are used as keys - 'ids' => join( '|', $ids ), - ) ); - - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities' ); - $this->assertEquals( count( $ids ), count( $res['entities'] ), "the actual number of items differs from the number of requested items" ); - - foreach ( $ids as $id ) { - $this->assertArrayHasKey( $id, $res['entities'], "missing item" ); - $this->assertEquals( $id, $res['entities'][$id]['id'], "bad ID" ); - } - } - - /** - * Testing if we can get all the complete stringified items if we do lookup with multiple site-title pairs. - * - * @group API - */ - public function testGetEntitiesMultipleSiteLinks() { - $handles = array( 'Berlin', 'London', 'Oslo' ); - $sites = array( 'dewiki', 'enwiki', 'nlwiki' ); - $titles = array( 'Berlin', 'London', 'Oslo' ); - - list( $res,, ) = $this->doApiRequest( array( - 'action' => 'wbgetentities', - 'format' => 'json', // make sure IDs are used as keys - 'sites' => join( '|', $sites ), - 'titles' => join( '|', $titles ) - ) ); - - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities' ); - $this->assertEquals( count( $titles ), count( $res['entities'] ), "the actual number of items differs from the number of requested items" ); - - foreach ( $handles as $handle ) { - $id = EntityTestHelper::getId( $handle ); - - $this->assertArrayHasKey( $id, $res['entities'], "missing item" ); - $this->assertEquals( $id, $res['entities'][$id]['id'], "bad ID" ); - } - } - - function provideLanguages() { - return array( - array( 'Berlin', array( 'en', 'de' ) ), - array( 'Leipzig', array( 'en', 'de' ) ), - array( 'Leipzig', array( 'fr', 'nl' ) ), - array( 'London', array( 'nl' ) ), - array( 'Empty', array( 'nl' ) ), - ); - } - - /** - * @dataProvider provideLanguages - */ - function testLanguages( $handle, $languages ) { - $id = EntityTestHelper::getId( $handle ); - - list($res,,) = $this->doApiRequest( - array( - 'action' => 'wbgetentities', - 'languages' => join( '|', $languages ), - 'format' => 'json', // make sure IDs are used as keys - 'ids' => $id ) - ); - - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $id ); - - $item = $res['entities'][$id]; - - foreach ( $item as $prop => $values ) { - if ( !is_array( $values ) ) { - continue; + if( array_key_exists( 'handles', $params ) ){ + foreach( $params['handles'] as $handle ){ + $ids[] = strtolower( EntityTestHelper::getId( $handle ) ); + $ids[] = strtoupper( EntityTestHelper::getId( $handle ) ); } - - if ( $prop === 'sitelinks' ) { - continue; - } - - $values = static::flattenArray( $values, 'language', 'value' ); - - $this->assertEmpty( array_diff( array_keys( $values ), $languages ), - "found unexpected language in property $prop: " . var_export( $values, true ) ); + $params['ids'] = implode( '|', $ids ); + unset( $params['handles'] ); } - } - function provideProps() { - return array( - array( 'Berlin', '', array( 'id', 'type' ) ), - array( 'Berlin', 'labels', array( 'id', 'type', 'labels' ) ), - array( 'Berlin', 'labels|descriptions', array( 'id', 'type', 'labels', 'descriptions' ) ), - array( 'Berlin', 'aliases|sitelinks', array( 'id', 'type', 'aliases', 'sitelinks' ) ), - - array( 'Leipzig', '', array( 'id', 'type' ) ), - array( 'Leipzig', 'labels|descriptions', array( 'id', 'type', 'labels', 'descriptions' ) ), - array( 'Leipzig', 'labels|aliases', array( 'id', 'type', 'labels' ) ), // aliases are omitted because empty - array( 'Leipzig', 'sitelinks|descriptions', array( 'id', 'type', 'descriptions' ) ), // sitelinks are omitted because empty - - // TODO: test this for property entities props, e.g. 'datatype' - - array( 'Berlin', 'xyz', false ), - ); - } - - /** - * @dataProvider provideProps - */ - function testProps( $handle, $props, $expectedProps ) { - $id = EntityTestHelper::getId( $handle ); - - list($res,,) = $this->doApiRequest( - array( - 'action' => 'wbgetentities', - 'props' => $props, - 'format' => 'json', // make sure IDs are used as keys - 'ids' => $id ) - ); - - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $id ); - - if ( $expectedProps === false ) { - $this->assertArrayHasKey( 'warnings', $res ); - $this->assertArrayHasKey( 'wbgetentities', $res['warnings'] ); + // -- calculate any expected data -------------------------------------------------------- + //expect props + if( array_key_exists( 'props', $params ) ){ + $expected['props'] = explode( '|', $params['props'] ); } else { - $this->assertArrayEquals( $expectedProps, array_keys( $res['entities'][$id] ) ); + $expected['props'] = array( 'info', 'sitelinks', 'aliases', 'labels', 'descriptions', 'claims', 'datatype' ); } - } - - function provideSitelinkUrls() { - return array( - array( 'Berlin' ), - array( 'London' ), - ); - } - - /** - * @dataProvider provideSitelinkUrls - */ - function testSitelinkUrls( $handle ) { - $id = EntityTestHelper::getId( $handle ); - - list($res,,) = $this->doApiRequest( - array( - 'action' => 'wbgetentities', - 'props' => 'sitelinks', - 'format' => 'json', // make sure IDs are used as keys - 'ids' => $id ) - ); - - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $id, 'sitelinks' ); - - foreach ( $res['entities'][$id]['sitelinks'] as $link ) { - $this->assertArrayNotHasKey( 'url', $link ); + //expect languages + if( array_key_exists( 'languages', $params ) ){ + $expected['languages'] = explode( '|', $params['languages'] ); + } else { + $expected['languages'] = null; + } + //expect order + if( array_key_exists( 'dir', $params ) ){ + $expected['dir'] = $params['dir']; + } else { + $expected['dir'] = 'ascending'; } - // ------------------------------------------- - list($res,,) = $this->doApiRequest( - array( - 'action' => 'wbgetentities', - 'props' => 'sitelinks|sitelinks/urls', - 'format' => 'json', // make sure IDs are used as keys - 'ids' => $id ) - ); + // -- do the request -------------------------------------------------------- + list( $result,, ) = $this->doApiRequest( $params ); - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $id, 'sitelinks' ); + // -- check the result -------------------------------------------------------- + //check the result as a whole + $this->assertArrayHasKey( 'success', $result, "Missing 'success' marker in response." ); + $this->assertArrayHasKey( 'entities', $result, "Missing 'entities' section in response." ); + $this->assertEquals( $expected['count'], count( $result['entities'] ), "Request returned incorrect number of entities" ); - foreach ( $res['entities'][$id]['sitelinks'] as $link ) { - $this->assertArrayHasKey( 'url', $link, "SiteLinks in the result must have the 'url' key set!" ); - $this->assertNotEmpty( $link['url'], "The value of the 'url' key is not allowed to be empty!" ); - } - } + //check the each entity + foreach( $result['entities'] as $entityKey => $entity ){ - function provideSitelinkSorting() { - return array( - array( 'Berlin' ), - array( 'London' ), - ); - } + //check for missing entities + if( array_key_exists( 'missing', $expected ) && $expected['missing'] > 0 && array_key_exists( 'missing', $entity ) ){ + $this->assertArrayHasKey( 'missing', $entity ); + $this->assertGreaterThanOrEqual( 0, $expected['missing'], 'Got missing entity but not expecting any more' ); + $expected['missing']--; + } else { - /** - * @dataProvider provideSitelinkSorting - */ - function testSitelinkSorting( $handle ) { - $id = EntityTestHelper::getId( $handle ); + //check expected props + if( in_array( 'info', $expected['props'] ) ){ + //check all keys exist + $this->assertArrayHasKey( 'pageid', $entity, 'An entity is missing the pageid value' ); + $this->assertType( 'integer', $entity['pageid'] ); + $this->assertGreaterThan( 0, $entity['pageid'] ); + $this->assertArrayHasKey( 'ns', $entity, 'An entity is missing the ns value' ); + $this->assertType( 'integer', $entity['ns'] ); + $this->assertGreaterThanOrEqual( 0, $entity['ns'] ); + $this->assertArrayHasKey( 'title', $entity, 'An entity is missing the title value' ); + $this->assertType( 'string', $entity['title'] ); + $this->assertNotEmpty( $entity['title'] ); + $this->assertArrayHasKey( 'lastrevid', $entity, 'An entity is missing the lastrevid value' ); + $this->assertType( 'integer', $entity['lastrevid'] ); + $this->assertGreaterThanOrEqual( 0, $entity['lastrevid'] ); + $this->assertArrayHasKey( 'modified', $entity, 'An entity is missing the modified value' ); + $this->assertRegExp( '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/', $entity['modified'], "should be in ISO 8601 format" ); + $this->assertArrayHasKey( 'id', $entity, 'An entity is missing the id value' ); + $this->assertArrayHasKey( 'type', $entity, 'An entity is missing the type value' ); + } + if( in_array( 'datatype', $expected['props'] ) ){ + $this->assertArrayHasKey( 'type', $entity, 'An entity is missing the type value' ); + } - list($res,,) = $this->doApiRequest( - array( - 'action' => 'wbgetentities', - 'props' => 'sitelinks', - 'sort' => 'sitelinks', - 'dir' => 'ascending', - 'format' => 'json', // make sure IDs are used as keys - 'ids' => $id ) - ); + //check the attributes (sitelinks, labels, aliases, descriptions) + $this->assertEntityEquals( + EntityTestHelper::getEntityOutput( + EntityTestHelper::getHandle( $entity['id'] ), + $expected['props'], + $expected['languages'] + ), + $entity + ); - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $id, 'sitelinks' ); - $last = ''; - foreach ( $res['entities'][$id]['sitelinks'] as $link ) { - $this->assertArrayHasKey( 'site', $link ); - $this->assertTrue(strcmp( $last, $link['site'] ) <= 0 ); - $last = $link['site']; - } + //check the direction + if( array_key_exists( 'dir', $expected ) && array_key_exists( 'sitelinks', $entity ) ){ + $last = ''; + if( $expected['dir'] == 'descending' ){ + $last = 'zzzzzzzz'; + } - // ------------------------------------------- - list($res,,) = $this->doApiRequest( - array( - 'action' => 'wbgetentities', - 'props' => 'sitelinks', - 'sort' => 'sitelinks', - 'dir' => 'descending', - 'format' => 'json', // make sure IDs are used as keys - 'ids' => $id ) - ); - - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $id, 'sitelinks' ); - $last = 'zzzzzzzz'; - foreach ( $res['entities'][$id]['sitelinks'] as $link ) { - $this->assertArrayHasKey( 'site', $link ); - $this->assertTrue(strcmp( $last, $link['site'] ) >= 0 ); - $last = $link['site']; - } - } - - /** - * @dataProvider providerGetItemFormat - */ - function testGetItemFormat( $format, $usekeys ) { - $id = EntityTestHelper::getId( 'Berlin' ); - - list($res,,) = $this->doApiRequest( - array( - 'action' => 'wbgetentities', - 'format' => $format, - 'ids' => $id ) - ); - - if ( $usekeys ) { - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities', $id ); - foreach ( array( 'sitelinks' => 'site', 'alias' => false, 'labels' => 'language', 'descriptions' => 'language' ) as $prop => $field) { - if ( array_key_exists( $prop, $res['entities'][$id] ) ) { - foreach ( $res['entities'][$id][$prop] as $key => $value ) { - $this->assertTrue( is_string( $key ) ); - if ( $field !== false ) { - $this->assertArrayHasKey( $field, $value ); - $this->assertTrue( is_string( $value[$field] ) ); - $this->assertEquals( $key, $value[$field] ); + foreach( $entity['sitelinks'] as $link ){ + $site = $link['site']; + if( $expected['dir'] == 'ascending' ){ + $this->assertTrue( strcmp( $last, $site ) <= 0 , "Failed to assert order of sitelinks, ('{$last}' vs '{$site}') <=0"); + } else { + $this->assertTrue( strcmp( $last, $site ) >= 0 , "Failed to assert order of sitelinks, ('{$last}' vs '{$site}') >=0"); } + $last = $site; } } } } - else { - $this->assertResultSuccess( $res ); - $this->assertResultHasKeyInPath( $res, 'entities' ); - $keys = array_keys( $res['entities'] ); - $n = $keys[0]; - - foreach ( array( 'sitelinks' => 'site', 'alias' => false, 'labels' => 'language', 'descriptions' => 'language' ) as $prop => $field) { - if ( array_key_exists( $prop, $res['entities'][$n] ) ) { - foreach ( $res['entities'][$n][$prop] as $key => $value ) { - $this->assertTrue( is_string( $key ) ); - if ( $field !== false ) { - $this->assertArrayHasKey( $field, $value ); - $this->assertTrue( is_string( $value[$field] ) ); - } - } - } - } - } - } - - function providerGetItemFormat() { - $formats = array( 'json', 'jsonfm', 'php', 'phpfm', 'wddx', 'wddxfm', 'xml', - 'xmlfm', 'yaml', 'yamlfm', 'rawfm', 'txt', 'txtfm', 'dbg', 'dbgfm', - 'dump', 'dumpfm', 'none', ); - $calls = array(); - - foreach ( $formats as $format ) { - $calls[] = array( $format, self::usekeys( $format ) ); + //make sure we have seen all expected missing entities + if( array_key_exists( 'missing', $expected ) ){ + $this->assertEquals( 0, $expected['missing'] ); } - return $calls; - } - - protected static function usekeys( $format ) { - static $withKeys = false; - - if ( $withKeys === false ) { - // Which formats to inject keys into, undefined entries are interpreted as true - // TODO: This array must be patched if awailable formats that does NOT support - // usekeys are added, changed or removed. - $withKeys = array( - 'wddx' => false, - 'wddxfm' => false, - 'xml' => false, - 'xmlfm' => false, + //check normalization result + if( array_key_exists( 'normalized', $expected ) && $expected['normalized'] === true ){ + $this->assertArrayHasKey( 'normalized', $result ); + $this->assertEquals( + $params['titles'], + $result['normalized']['n']['from'] ); + $this->assertEquals( + // Normalization in unit tests is actually using Title::getPrefixedText instead of a real API call + \Title::newFromText( $params['titles'] )->getPrefixedText(), + $result['normalized']['n']['to'] + ); + } else { + $this->assertArrayNotHasKey( 'normalized', $result ); } + } - return isset( $withKeys[$format] ) ? $withKeys[$format] : true; + public static function provideExceptionData() { + //todo more exception checks should be added once Bug:53038 is resolved + return array( + array( //0 no params + 'p' => array( ), + 'e' => array( 'exception' => array( 'type' => 'UsageException', 'code' => 'param-missing' ) ) ), + array( //1 bad id + 'p' => array( 'ids' => 'ABCD' ), + 'e' => array( 'exception' => array( 'type' => 'UsageException', 'code' => 'no-such-entity' ) ) ), + array( //2 bad site + 'p' => array( 'sites' => 'qwertyuiop', 'titles' => 'Berlin' ), + 'e' => array( 'exception' => array( 'type' => 'UsageException', 'code' => 'param-missing' ) ) ), + array( //3 bad and good id + 'p' => array( 'ids' => 'q1|aaaa' ), + 'e' => array( 'exception' => array( 'type' => 'UsageException', 'code' => 'no-such-entity' ) ) ), + array( //4 site and no title + 'p' => array( 'sites' => 'enwiki' ), + 'e' => array( 'exception' => array( 'type' => 'UsageException', 'code' => 'param-missing' ) ) ), + array( //5 title and no site + 'p' => array( 'titles' => 'Berlin' ), + 'e' => array( 'exception' => array( 'type' => 'UsageException', 'code' => 'param-missing' ) ) ), + array( //6 normalization fails with 2 titles + 'p' => array( 'sites' => 'enwiki', 'titles' => 'Foo|Bar' ,'normalize' => '' ), + 'e' => array( 'exception' => array( 'type' => 'UsageException', 'code' => 'params-illegal' ) ) ), + array( //7 normalization fails with 2 sites + 'p' => array( 'sites' => 'enwiki|dewiki', 'titles' => 'Boo' ,'normalize' => '' ), + 'e' => array( 'exception' => array( 'type' => 'UsageException', 'code' => 'params-illegal' ) ) ), + array( //8 normalization fails with 2 sites nd 2 titles + 'p' => array( 'sites' => 'enwiki|dewiki', 'titles' => 'Foo|Bar' ,'normalize' => '' ), + 'e' => array( 'exception' => array( 'type' => 'UsageException', 'code' => 'params-illegal' ) ) ), + ); + } + + /** + * @dataProvider provideExceptionData + */ + public function testGetEntitiesExceptions( $params, $expected ){ + // -- set any defaults ------------------------------------ + $params['action'] = 'wbgetentities'; + if( array_key_exists( 'handles', $params ) ){ + $ids = array(); + foreach( $params['handles'] as $handle ){ + $ids[ $handle ] = EntityTestHelper::getId( $handle ); + } + $params['ids'] = implode( '|', $ids ); + unset( $params['handles'] ); + } + $this->doTestQueryExceptions( $params, $expected['exception'] ); } } diff --git a/repo/tests/phpunit/includes/api/WikibaseApiTestCase.php b/repo/tests/phpunit/includes/api/WikibaseApiTestCase.php index 8bc215b..c40577e 100644 --- a/repo/tests/phpunit/includes/api/WikibaseApiTestCase.php +++ b/repo/tests/phpunit/includes/api/WikibaseApiTestCase.php @@ -159,10 +159,9 @@ } else { $this->doApiRequestWithToken( $params ); } - $this->fail( "Failed to throw exception, {$exception['type']} " ); + $this->fail( "Failed to throw UsageException" ); - } catch( \Exception $e ){ - /** @var $e \UsageException */ // trick IDEs into not showing errors + } catch( \UsageException $e ){ if( array_key_exists( 'type', $exception ) ){ $this->assertInstanceOf( $exception['type'], $e ); } -- To view, visit https://gerrit.wikimedia.org/r/81003 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I8660112f8aae634e835d614a148fb40c52284556 Gerrit-PatchSet: 6 Gerrit-Project: mediawiki/extensions/Wikibase Gerrit-Branch: master Gerrit-Owner: Addshore <addshorew...@gmail.com> Gerrit-Reviewer: Hoo man <h...@online.de> Gerrit-Reviewer: Tobias Gritschacher <tobias.gritschac...@wikimedia.de> Gerrit-Reviewer: jenkins-bot _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits