jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/322632 )
Change subject: Adding unit test base classes ...................................................................... Adding unit test base classes ... and first API unit tests as reference for further development Change-Id: Ie2a7de98daac279a0450f6914ee2d2c82a81a351 --- M BlueSpice.hooks.php M extension.json M includes/CoreHooks.php M includes/api/BSApiWikiPageTasks.php A tests/BSApiExtJSStoreTestBase.php A tests/BSApiTasksTestBase.php A tests/BSPageFixturesProvider.php A tests/api/BSApiTitleQueryStoreTest.php A tests/api/BSApiWikiPageTasksTest.php 9 files changed, 410 insertions(+), 24 deletions(-) Approvals: Mglaser: Looks good to me, approved jenkins-bot: Verified diff --git a/BlueSpice.hooks.php b/BlueSpice.hooks.php index 12f9235..af7869a 100644 --- a/BlueSpice.hooks.php +++ b/BlueSpice.hooks.php @@ -1,6 +1,4 @@ <?php -#$wgHooks['UnitTestsList'][] = 'BsCoreHooks::onUnitTestsList'; - // START cache invalidation hooks $wgHooks['PageContentSaveComplete'][] = 'BsCacheHelper::onPageContentSaveComplete'; // END cache invalidation hooks diff --git a/extension.json b/extension.json index 4cfc8e0..e948cad 100644 --- a/extension.json +++ b/extension.json @@ -368,7 +368,8 @@ "ParserFirstCallInit": "BsCoreHooks::onParserFirstCallInit", "UserAddGroup": "BsGroupHelper::addTemporaryGroupToUserHelper", "ExtensionTypes": "BsCoreHooks::onExtensionTypes", - "PageContentSaveComplete": "BsCoreHooks::onPageContentSaveComplete" + "PageContentSaveComplete": "BsCoreHooks::onPageContentSaveComplete", + "UnitTestsList": "BsCoreHooks::onUnitTestsList" }, "config": { "BlueSpiceExtInfo": { @@ -529,7 +530,10 @@ "BSEntityRegistry": "includes/EntityRegistry.php", "BSEntityConfig": "includes/entityconfigs/EntityConfig.php", "BSEntityContent": "includes/content/EntityContent.php", - "BSEntityContentHandler": "includes/content/EntityContentHandler.php" + "BSEntityContentHandler": "includes/content/EntityContentHandler.php", + "BSPageFixturesProvider": "tests/BSPageFixturesProvider.php", + "BSApiExtJSStoreTestBase": "tests/BSApiExtJSStoreTestBase.php", + "BSApiTasksTestBase": "tests/BSApiTasksTestBase.php" }, "bsgConfigFiles": [], "load_composer_autoloader": true, diff --git a/includes/CoreHooks.php b/includes/CoreHooks.php index f082889..e867475 100755 --- a/includes/CoreHooks.php +++ b/includes/CoreHooks.php @@ -643,17 +643,7 @@ * @return boolean Always true to keep hook running */ public static function onUnitTestsList( &$files ) { - $oIterator = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator( dirname( __DIR__ ) . '/tests/' ) - ); - /** - * @var SplFileInfo $oFileInfo - */ - foreach ( $oIterator as $oFileInfo ) { - if ( substr( $oFileInfo->getFilename(), -8 ) === 'Test.php' ) { - $files[] = $oFileInfo->getPathname(); - } - } + $files[] = dirname( __DIR__ ) . '/tests/'; return true; } diff --git a/includes/api/BSApiWikiPageTasks.php b/includes/api/BSApiWikiPageTasks.php index 6850e46..9aeb7fb 100644 --- a/includes/api/BSApiWikiPageTasks.php +++ b/includes/api/BSApiWikiPageTasks.php @@ -27,7 +27,7 @@ */ /** - * Provides common tasks that can pe performed on a WikiPage + * Provides common tasks that can be performed on a WikiPage * @package BlueSpice_Foundation */ class BSApiWikiPageTasks extends BSApiTasksBase { @@ -170,20 +170,18 @@ return $oResponse; } - //Patern for Category tags + //Pattern for Category tags $sCanonicalNSName = MWNamespace::getCanonicalName( NS_CATEGORY ); $sLocalNSName = BsNamespaceHelper::getNamespaceName( NS_CATEGORY ); $sPattern = "#\[\[($sLocalNSName|$sCanonicalNSName):(.*?)\]\]#si"; $matches = []; - $matchCount = 0; $matchCount = preg_match_all($sPattern, $sText, $matches, PREG_PATTERN_ORDER); $aCategories = []; //normalize - foreach ($matches[2] as $match){ - $sMatch = preg_replace('!_+!', ' ', $match); - $sMatch = preg_replace('!\s+!', ' ', $sMatch); - array_push($aCategories, $sMatch); + foreach ( $matches[2] as $match ){ + $oCategoryTitle = Title::newFromText( $match, NS_CATEGORY ); + array_push( $aCategories, $oCategoryTitle->getText() ); } $oResponse->success = true; @@ -276,6 +274,7 @@ DataUpdate::runUpdates( $oUpdates ); $oResponse->success = true; $oResponse->message = wfMessage( 'bs-wikipage-tasks-setcategories-success' )->plain(); + $oResponse->payload = $this->makeCategoryTaskPayload( $oTitle->getArticleID() ); } return $oResponse; @@ -398,7 +397,13 @@ return $oTitle; } - public function needsToken() { - return parent::needsToken(); + protected function makeCategoryTaskPayload( $pageId ) { + $oTitle = Title::newFromID( $pageId ); + $result = $this->task_getExplicitCategories( (object)[ 'page_id' => $pageId ], [] ); + return array( + 'page_id' => $oTitle->getArticleID(), + 'page_prefixed_text' => $oTitle->getPrefixedText(), + 'categories' => $result->payload + ); } } diff --git a/tests/BSApiExtJSStoreTestBase.php b/tests/BSApiExtJSStoreTestBase.php new file mode 100644 index 0000000..683e938 --- /dev/null +++ b/tests/BSApiExtJSStoreTestBase.php @@ -0,0 +1,169 @@ +<?php + +/** + * + * Class BSApiExtJSStoreTestBase + */ +abstract class BSApiExtJSStoreTestBase extends ApiTestCase { + + protected $iFixtureTotal = 0; + abstract protected function getStoreSchema(); + abstract protected function createStoreFixtureData(); + abstract protected function getModuleName(); + + protected function setUp() { + parent::setUp(); + + $this->doLogin(); + } + + public function addDBDataOnce() { + $this->createStoreFixtureData(); + } + + public function testSchema() { + $results = $this->doApiRequest( + $this->makeRequestParams() + ); + + $response = $results[0]; + $firstRow = (object)$response['results'][0]; + $schema = $this->getStoreSchema(); + foreach( $schema as $schemaFieldName => $config ) { + $this->assertObjectHasAttribute( $schemaFieldName, $firstRow, "Dataset misses field '$schemaFieldName'' from schema definition!" ); + $value = $firstRow->{$schemaFieldName}; + + switch( $config['type'] ) { + case 'string': + $this->assertEquals( true, is_string( $value ), "Value of field '$schemaFieldName' is not a string" ); + break; + case 'list': + $this->assertEquals( true, is_array( $value ), "Value of field '$schemaFieldName' is not a list" ); + break; + case 'numeric': + $this->assertEquals( true, is_numeric( $value ), "Value of field '$schemaFieldName' is not a number" ); + break; + case 'boolean': + $this->assertEquals( true, is_bool( $value ), "Value of field '$schemaFieldName' is not a boolean" ); + break; + case 'date': + $this->assertNotEquals( -1, strtotime(), "Value of field '$schemaFieldName' is not a valid date format" ); + break; + case 'title': + $this->assertNotNull( Title::newFromText( $value ), "Value of field '$schemaFieldName' is not a valid title" ); + break; + case 'templatetitle': + $this->assertNotNull( Title::newFromText( $value, NS_TEMPLATE ), "Value of field '$schemaFieldName' is not a valid template title" ); + break; + } + } + } + + /** + * @param $limit + * @param $offset + * + * @dataProvider providePagingData + */ + public function testPaging( $limit, $offset ) { + $results = $this->doApiRequest([ + 'action' => $this->getModuleName(), + 'limit' => $limit, + 'offset' => $offset + ]); + $response = $results[0]; + + $this->assertAttributeEquals( + $this->iFixtureTotal, + 'total', + (object)$response, + 'Field "total" contains wrong value' + ); + + $this->assertLessThanOrEqual( $limit, count($response['results']), 'Number of results exceeds limit' ); + } + + public function providePagingData() { + return array( + [ 2, 0 ], + [ 2, 2 ], + [ 2, 4 ], + [ 4, 0 ], + [ 4, 4 ], + [ 4, 8 ] + ); + } + + /** + * [ + * { + * "type":"string", + * "comparison":"ct", + * "value":"some text ...", + * "field":"someField" + * } + * ] + * + * @param $type + * @param $field + * @param $value + * @param $comparison + * @param $expectedTotal + * + * @dataProvider provideSingleFilterData + */ + public function testSingleFilter( $type, $comparison, $field, $value, $expectedTotal ) { + $results = $this->doApiRequest([ + 'action' => $this->getModuleName(), + 'filter' => FormatJson::encode([ + [ + 'type' => $type, + 'comparison' => $comparison, + 'field' => $field, + 'value' => $value + ] + ]) + ]); + + $response = $results[0]; + + $this->assertAttributeEquals( + $expectedTotal, + 'total', + (object)$response, + 'Field "total" contains wrong value' + ); + } + + abstract public function provideSingleFilterData(); + + /** + * @param $filters + * @param $expectedTotal + * + * @dataProvider provideMultipleFilterData + */ + public function testMultipleFilter( $filters, $expectedTotal ) { + $results = $this->doApiRequest([ + 'action' => $this->getModuleName(), + 'filter' => FormatJson::encode( $filters ) + ]); + + $response = $results[0]; + + $this->assertAttributeEquals( + $expectedTotal, + 'total', + (object)$response, + 'Field "total" contains wrong value' + ); + } + + abstract public function provideMultipleFilterData(); + + protected function makeRequestParams() { + return [ + 'action' => $this->getModuleName() + ]; + } +} \ No newline at end of file diff --git a/tests/BSApiTasksTestBase.php b/tests/BSApiTasksTestBase.php new file mode 100644 index 0000000..63fdb60 --- /dev/null +++ b/tests/BSApiTasksTestBase.php @@ -0,0 +1,22 @@ +<?php + +abstract class BSApiTasksTestBase extends ApiTestCase { + + abstract protected function getModuleName(); + + protected function setUp() { + parent::setUp(); + + $this->doLogin(); + } + + protected function executeTask( $taskName, $taskData ) { + $results = $this->doApiRequestWithToken([ + 'action' => $this->getModuleName(), + 'task' => $taskName, + 'taskData' => FormatJson::encode( $taskData ) + ]); + + return (object)$results[0]; + } +} \ No newline at end of file diff --git a/tests/BSPageFixturesProvider.php b/tests/BSPageFixturesProvider.php new file mode 100644 index 0000000..9779a75 --- /dev/null +++ b/tests/BSPageFixturesProvider.php @@ -0,0 +1,12 @@ +<?php + +class BSPageFixturesProvider { + + /** + * @return array[] + */ + public function getFixtureData() { + $oData = FormatJson::decode( file_get_contents( __DIR__."/data/pages.json" ) ); + return $oData->pages; + } +} \ No newline at end of file diff --git a/tests/api/BSApiTitleQueryStoreTest.php b/tests/api/BSApiTitleQueryStoreTest.php new file mode 100644 index 0000000..b80840d --- /dev/null +++ b/tests/api/BSApiTitleQueryStoreTest.php @@ -0,0 +1,70 @@ +<?php + +/** + * @group medium + * + * Class BSApiTitleQueryStoreTest + */ +class BSApiTitleQueryStoreTest extends BSApiExtJSStoreTestBase { + protected function getStoreSchema() { + return [ + 'page_id' => [ + 'type' => 'numeric' + ], + 'page_namespace' => [ + 'type' => 'numeric' + ], + 'page_title' => [ + 'type' => 'string' + ], + 'prefixedText' => [ + 'type' => 'string' + ], + 'displayText' => [ + 'type' => 'string' + ], + 'type' => [ + 'type' => 'string' + ] + ]; + } + + protected function createStoreFixtureData() { + $oPageFixtures = new BSPageFixturesProvider(); + $aFixtures = $oPageFixtures->getFixtureData(); + foreach( $aFixtures as $aFixture ) { + $this->insertPage( $aFixture[0], $aFixture[1] ); + } + } + + protected function getModuleName() { + return 'bs-titlequery-store'; + } + + protected function makeRequestParams() { + $aParams = parent::makeRequestParams(); + $aParams['options'] = FormatJson::encode([ + 'namespaces' => [ NS_MAIN, NS_TEMPLATE ] + ]); + + return $aParams; + } + + public function provideSingleFilterData() { + return [ + 'Filter by page_id' => [ 'numeric', 'eq', 'page_id', -1, 0 ] + ]; + } + + public function provideMultipleFilterData() { + return [ + 'Filter by page_name and page_namespace' => [ + [ + + ], + 0 + ] + ]; + } + +} \ No newline at end of file diff --git a/tests/api/BSApiWikiPageTasksTest.php b/tests/api/BSApiWikiPageTasksTest.php new file mode 100644 index 0000000..122da1c --- /dev/null +++ b/tests/api/BSApiWikiPageTasksTest.php @@ -0,0 +1,116 @@ +<?php + +/** + * @group medium + * + * Class BSApiWikiPageTasksTest + */ +class BSApiWikiPageTasksTest extends BSApiTasksTestBase { + protected function getModuleName() { + return 'bs-wikipage-tasks'; + } + + public function setUp() { + parent::setUp(); + + $this->insertPage( 'Category Test', '[[Category:Mouse]] [[Category:Bird]] [[Category:Pink unicorn]]' ); + } + + public function testAddCategoriesSucceeds() { + $oTitle = Title::newFromText( 'Category Test' ); + $aTestCategories = [ + 'CatA' => Title::newFromText( 'CatA', NS_CATEGORY )->getText(), + 'cAT_B' => Title::newFromText( 'cAT_B', NS_CATEGORY )->getText(), + 'CAT C' => Title::newFromText( 'CAT C', NS_CATEGORY )->getText(), + 'cat____d'=> Title::newFromText( 'cat____d', NS_CATEGORY )->getText(), + 'CAT f g' => Title::newFromText( 'CAT f g', NS_CATEGORY )->getText() + ]; + + $response = $this->executeTask( + 'addCategories', + [ + 'page_id' => $oTitle->getArticleID(), + 'categories' => array_keys( $aTestCategories ) + ] + ); + + $this->assertEquals( true, $response->success, "Adding categories failed where it should have succeeded" ); + + $oWikiPage = WikiPage::factory( $oTitle ); + $aCategoryTitles = $oWikiPage->getCategories(); + $aActualCategories = []; + foreach( $aCategoryTitles as $oCategoryTitle ) { + $aActualCategories[] = $oCategoryTitle->getText(); + } + + $aNormalizedTestCategories = array_values( $aTestCategories ); + sort( $aNormalizedTestCategories ); + $aIntersection = array_intersect( $aActualCategories, $aNormalizedTestCategories ); + sort( $aIntersection ); + $this->assertArrayEquals( + $aIntersection, + $aNormalizedTestCategories, + 'Not all categories were set' + ); + } + + public function testSetCategoriesSucceeds() { + $oTitle = Title::newFromText( 'Category Test' ); + $aTestCategories = [ + 'Cat' => Title::newFromText( 'Cat', NS_CATEGORY )->getText(), + ]; + + $response = $this->executeTask( + 'setCategories', + [ + 'page_id' => $oTitle->getArticleID(), + 'categories' => array_keys( $aTestCategories ) + ] + ); + + $this->assertEquals( true, $response->success, "Setting categories failed where it should have succeeded" ); + + $oWikiPage = WikiPage::factory( $oTitle ); + $aCategoryTitles = $oWikiPage->getCategories(); + $aActualCategories = []; + foreach( $aCategoryTitles as $oCategoryTitle ) { + $aActualCategories[] = $oCategoryTitle->getText(); + } + + $aNormalizedTestCategories = array_values( $aTestCategories ); + + sort( $aNormalizedTestCategories ); + sort( $aActualCategories ); + + $this->assertArrayEquals( + $aActualCategories, + $aNormalizedTestCategories, + 'Not all categories were set' + ); + } + + public function testRemoveCategoriesSucceeds() { + $oTitle = Title::newFromText( 'Category Test' ); + + $response = $this->executeTask( + 'removeCategories', + [ + 'page_id' => $oTitle->getArticleID(), + 'categories' => [ 'Mouse' ] + ] + ); + + $this->assertEquals( true, $response->success, "Removing categories failed where it should have succeeded" ); + + $oWikiPage = WikiPage::factory( $oTitle ); + $aCategoryTitles = $oWikiPage->getCategories(); + $aActualCategories = []; + foreach( $aCategoryTitles as $oCategoryTitle ) { + $aActualCategories[] = $oCategoryTitle->getText(); + } + + sort( $aActualCategories ); + + $this->assertFalse( in_array( 'Mouse', $aActualCategories ) ); + } +} \ No newline at end of file -- To view, visit https://gerrit.wikimedia.org/r/322632 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Ie2a7de98daac279a0450f6914ee2d2c82a81a351 Gerrit-PatchSet: 4 Gerrit-Project: mediawiki/extensions/BlueSpiceFoundation Gerrit-Branch: master Gerrit-Owner: Robert Vogel <vo...@hallowelt.biz> Gerrit-Reviewer: Dvogel hallowelt <daniel.vo...@hallowelt.com> Gerrit-Reviewer: Ljonka <l.verhovs...@gmail.com> Gerrit-Reviewer: Mglaser <gla...@hallowelt.biz> Gerrit-Reviewer: Pwirth <wi...@hallowelt.biz> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits