jenkins-bot has submitted this change and it was merged.
Change subject: J2f - Add minimal topic list API for TOC
......................................................................
J2f - Add minimal topic list API for TOC
Support a TOC-only mode for the topic list, and related
refactoring to tests and topic list code:
* This is implemented by reusing data already calculated by the Pager
(since that's all that should be needed).
It then formats that in an alternate formatter. There is now a base
class, BaseTopicListFormatter, and two subclasses.
* Add tests for ViewTopicList, for the TOC formatter (however, it also
catches some issues that are common to both TOC-only and regular).
* Fix a bug in TopicListLastUpdatedStorage.
It was embedding a table name directly into the query. In that position,
MediaWiki core didn't know to auto-prefix it. That meant that if
wgDBprefix was set (A prefix is used in the tests ('unittest_'),
but this is also a user-facing setting), the query would fail.
* Changing to the testing base class to support an intentionally blank
board, and arbitrary topic titles.
* Add back blank pagination array for empty boards (this time in parent
class). I removed this in 2d6d9b4 since it's overwritten for
non-blank boards, but it is used when buildEmptyResult
is called directly by TopicList.
* Remove obsolete constructor parameter
* Some small doc changes in TopicListQuery
* Changes max limit to use the wgFlowMaxLimit variable (note, should
be tested in production).
This will improve performance for the TOC use case.
Change-Id: I3615817edb1500bd63dc4cbd64820fa25c943e21
---
M autoload.php
M container.php
M includes/Block/TopicList.php
M includes/Data/Storage/TopicListLastUpdatedStorage.php
A includes/Formatter/BaseTopicListFormatter.php
A includes/Formatter/TocTopicListFormatter.php
M includes/Formatter/TopicListFormatter.php
M includes/Formatter/TopicListQuery.php
M includes/api/ApiFlowViewTopicList.php
A tests/phpunit/api/ApiFlowViewTopicListTest.php
M tests/phpunit/api/ApiTestCase.php
11 files changed, 444 insertions(+), 58 deletions(-)
Approvals:
EBernhardson: Looks good to me, approved
jenkins-bot: Verified
diff --git a/autoload.php b/autoload.php
index 72ae15a..fbe226b 100644
--- a/autoload.php
+++ b/autoload.php
@@ -133,6 +133,7 @@
$wgAutoloadClasses['Flow\\FlowActions'] = __DIR__ .
'/includes/FlowActions.php';
$wgAutoloadClasses['Flow\\Formatter\\AbstractFormatter'] = __DIR__ .
'/includes/Formatter/AbstractFormatter.php';
$wgAutoloadClasses['Flow\\Formatter\\AbstractQuery'] = __DIR__ .
'/includes/Formatter/AbstractQuery.php';
+$wgAutoloadClasses['Flow\\Formatter\\BaseTopicListFormatter'] = __DIR__ .
'/includes/Formatter/BaseTopicListFormatter.php';
$wgAutoloadClasses['Flow\\Formatter\\BoardHistoryQuery'] = __DIR__ .
'/includes/Formatter/BoardHistoryQuery.php';
$wgAutoloadClasses['Flow\\Formatter\\CheckUserFormatter'] = __DIR__ .
'/includes/Formatter/CheckUserFormatter.php';
$wgAutoloadClasses['Flow\\Formatter\\CheckUserQuery'] = __DIR__ .
'/includes/Formatter/CheckUserQuery.php';
@@ -155,6 +156,7 @@
$wgAutoloadClasses['Flow\\Formatter\\RevisionViewFormatter'] = __DIR__ .
'/includes/Formatter/RevisionViewFormatter.php';
$wgAutoloadClasses['Flow\\Formatter\\RevisionViewQuery'] = __DIR__ .
'/includes/Formatter/RevisionViewQuery.php';
$wgAutoloadClasses['Flow\\Formatter\\SinglePostQuery'] = __DIR__ .
'/includes/Formatter/SinglePostQuery.php';
+$wgAutoloadClasses['Flow\\Formatter\\TocTopicListFormatter'] = __DIR__ .
'/includes/Formatter/TocTopicListFormatter.php';
$wgAutoloadClasses['Flow\\Formatter\\TopicFormatter'] = __DIR__ .
'/includes/Formatter/TopicFormatter.php';
$wgAutoloadClasses['Flow\\Formatter\\TopicHistoryQuery'] = __DIR__ .
'/includes/Formatter/TopicHistoryQuery.php';
$wgAutoloadClasses['Flow\\Formatter\\TopicListFormatter'] = __DIR__ .
'/includes/Formatter/TopicListFormatter.php';
@@ -227,6 +229,7 @@
$wgAutoloadClasses['Flow\\Tests\\Api\\ApiFlowModerateTopicTest'] = __DIR__ .
'/tests/phpunit/api/ApiFlowModerateTopicTest.php';
$wgAutoloadClasses['Flow\\Tests\\Api\\ApiFlowReplyTest'] = __DIR__ .
'/tests/phpunit/api/ApiFlowReplyTest.php';
$wgAutoloadClasses['Flow\\Tests\\Api\\ApiFlowViewHeaderTest'] = __DIR__ .
'/tests/phpunit/api/ApiFlowViewHeaderTest.php';
+$wgAutoloadClasses['Flow\\Tests\\Api\\ApiFlowViewTopicListTest'] = __DIR__ .
'/tests/phpunit/api/ApiFlowViewTopicListTest.php';
$wgAutoloadClasses['Flow\\Tests\\Api\\ApiTestCase'] = __DIR__ .
'/tests/phpunit/api/ApiTestCase.php';
$wgAutoloadClasses['Flow\\Tests\\Api\\ApiWatchTopicTest'] = __DIR__ .
'/tests/phpunit/api/ApiWatchTopicTest.php';
$wgAutoloadClasses['Flow\\Tests\\BlockFactoryTest'] = __DIR__ .
'/tests/phpunit/BlockFactoryTest.php';
diff --git a/container.php b/container.php
index 328cb2c..8397433 100644
--- a/container.php
+++ b/container.php
@@ -975,7 +975,11 @@
$c['formatter.topiclist'] = $c->share( function( $c ) {
return new Flow\Formatter\TopicListFormatter(
$c['url_generator'],
- $c['formatter.revision'],
+ $c['formatter.revision']
+ );
+} );
+$c['formatter.topiclist.toc'] = $c->share( function ( $c ) {
+ return new Flow\Formatter\TocTopicListFormatter(
$c['templating']
);
} );
diff --git a/includes/Block/TopicList.php b/includes/Block/TopicList.php
index ad3d2fa..2fd3428 100644
--- a/includes/Block/TopicList.php
+++ b/includes/Block/TopicList.php
@@ -52,6 +52,13 @@
*/
protected $firstPost;
+ /**
+ * @var array
+ *
+ * Associative array mapping topic ID (in alphadecimal form) to
PostRevision for the topic root.
+ */
+ protected $topicRootRevisionCache = array();
+
protected function validate() {
// for now, new topic is considered a new post; perhaps some
day topic creation should get it's own permissions?
if ( !$this->permissions->isAllowed( null, 'new-post' ) ) {
@@ -154,12 +161,23 @@
}
public function renderApi( array $options ) {
- /** @var TopicListFormatter $serializer */
- $serializer = Container::get( 'formatter.topiclist' );
$response = array(
'submitted' => $this->wasSubmitted() ? $this->submitted
: $options,
'errors' => $this->errors,
);
+
+ // Repeating the default until we use the API for everything
(bug 72659)
+ // Also, if this is removed other APIs (i.e. ApiFlowNewTopic)
may need
+ // to be adjusted if they trigger a rendering of this block.
+ $isTocOnly = isset( $options['toconly'] ) ? $options['toconly']
: false;
+
+ if ( $isTocOnly ) {
+ /** @var TocTopicListFormatter $serializer */
+ $serializer = Container::get( 'formatter.topiclist.toc'
);
+ } else {
+ /** @var TopicListFormatter $serializer */
+ $serializer = Container::get( 'formatter.topiclist' );
+ }
if ( $this->workflow->isNew() ) {
return $response + $serializer->buildEmptyResult(
$this->workflow );
@@ -183,7 +201,20 @@
$workflowIds[] = $topicListEntry->getId();
}
+ if ( $isTocOnly ) {
+ // We don't need any further data, so we skip the
TopicListQuery.
+
+ $mapping = array();
+ foreach ( $workflowIds as $workflowId ) {
+ $alphaWorkflowId =
$workflowId->getAlphadecimal();
+ $mapping[$alphaWorkflowId] =
$this->topicRootRevisionCache[$alphaWorkflowId];
+ }
+
+ return $response + $serializer->formatApi(
$this->workflow, $mapping, $page );
+ }
+
$workflows = $this->storage->getMulti( 'Workflow', $workflowIds
);
+
/** @var TopicListQuery $query */
$query = Container::get( 'query.topiclist' );
$found = $query->getResults( $page->getResults() );
@@ -270,6 +301,9 @@
* Gets a set of workflow IDs
* This filters result to only include unmoderated and locked topics.
*
+ * Also populates topicRootRevisionCache with a mapping from topic ID
to the
+ * PostRevision for the topic root.
+ *
* @param array $findOptions
* @return PagerPage
*/
@@ -281,7 +315,11 @@
);
$postStorage = $this->storage->getStorage( 'PostRevision' );
- return $pager->getPage( function( array $found ) use (
$postStorage ) {
+
+ // Work around lack of $this in closures until we can use PHP
5.4+ features.
+ $topicRootRevisionCache =& $this->topicRootRevisionCache;
+
+ return $pager->getPage( function( array $found ) use (
$postStorage, &$topicRootRevisionCache ) {
$queries = array();
/** @var TopicListEntry[] $found */
foreach ( $found as $entry ) {
@@ -296,11 +334,13 @@
foreach ( $posts as $queryResult ) {
$post = reset( $queryResult );
if ( !$post->isModerated() || $post->isLocked()
) {
-
$allowed[$post->getPostId()->getAlphadecimal()] = true;
+
$allowed[$post->getPostId()->getAlphadecimal()] = $post;
}
}
foreach ( $found as $idx => $entry ) {
- if ( !isset(
$allowed[$entry->getId()->getAlphadecimal()] ) ) {
+ if ( isset(
$allowed[$entry->getId()->getAlphadecimal()] ) ) {
+
$topicRootRevisionCache[$entry->getId()->getAlphadecimal()] =
$allowed[$entry->getId()->getAlphadecimal()];
+ } else {
unset( $found[$idx] );
}
}
diff --git a/includes/Data/Storage/TopicListLastUpdatedStorage.php
b/includes/Data/Storage/TopicListLastUpdatedStorage.php
index 350d9f7..51e4b03 100644
--- a/includes/Data/Storage/TopicListLastUpdatedStorage.php
+++ b/includes/Data/Storage/TopicListLastUpdatedStorage.php
@@ -23,12 +23,13 @@
$res = $this->dbFactory->getDB( DB_MASTER )->select(
array( $this->table, 'flow_workflow' ),
- $this->table . '.*, workflow_last_update_timestamp',
- $attributes + array( 'topic_id = workflow_id' ),
+ 'topic_list_id, topic_id,
workflow_last_update_timestamp',
+ array_merge( $attributes, array( 'topic_id =
workflow_id' ) ),
__METHOD__ . " ({$this->table})",
$options
);
if ( ! $res ) {
+ // TODO: This should probably not silently fail on
database errors.
return null;
}
diff --git a/includes/Formatter/BaseTopicListFormatter.php
b/includes/Formatter/BaseTopicListFormatter.php
new file mode 100644
index 0000000..0247ffd
--- /dev/null
+++ b/includes/Formatter/BaseTopicListFormatter.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Flow\Formatter;
+
+use Flow\Data\Pager\PagerPage;
+use Flow\Model\Anchor;
+use Flow\Model\Workflow;
+
+class BaseTopicListFormatter {
+ /**
+ * Builds the results for an empty topic.
+ *
+ * @param Workflow $workflow Workflow for topic list
+ * @return array Associative array with the the result
+ */
+ public function buildEmptyResult( Workflow $workflow ) {
+ return array(
+ 'type' => 'topiclist',
+ 'roots' => array(),
+ 'posts' => array(),
+ 'revisions' => array(),
+ 'links' => array( 'pagination' => array() ),
+ );
+ }
+
+ /**
+ * @param Workflow $workflow Topic list workflow
+ * @param array $links pagination link data
+ *
+ * @return array link structure
+ */
+ protected function buildPaginationLinks( Workflow $workflow, array
$links ) {
+ $res = array();
+ $title = $workflow->getArticleTitle();
+ foreach ( $links as $key => $options ) {
+ // prefix all options with topiclist_
+ $realOptions = array();
+ foreach ( $options as $k => $v ) {
+ $realOptions["topiclist_$k"] = $v;
+ }
+ $res[$key] = new Anchor(
+ $key, // @todo i18n
+ $title,
+ $realOptions
+ );
+ }
+
+ return $res;
+ }
+}
diff --git a/includes/Formatter/TocTopicListFormatter.php
b/includes/Formatter/TocTopicListFormatter.php
new file mode 100644
index 0000000..6b4467d
--- /dev/null
+++ b/includes/Formatter/TocTopicListFormatter.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Flow\Formatter;
+
+use Flow\Data\Pager\PagerPage;
+use Flow\Model\Workflow;
+use Flow\Templating;
+
+// The output of this is a strict subset of TopicListFormatter.
+// Anything accessible from the output of this should be accessible with the
same path
+// from the output of TopicListFormatter. However, this output is much more
minimal.
+class TocTopicListFormatter extends BaseTopicListFormatter {
+ /**
+ * @var Templating
+ */
+ protected $templating;
+
+ public function __construct( Templating $templating ) {
+ $this->templating = $templating;
+ }
+
+ /**
+ * Formats the response
+ *
+ * @param Workflow $listWorkflow Workflow corresponding to board/list
of topics
+ * @param array $mapping Associative array mapping topic ID (in
alphadecimal form)
+ * to PostRevision for the topic root.
+ * @param PagerPage $page page from query, to support pagination
+ *
+ * @return array Array formatted for response
+ */
+ public function formatApi( Workflow $listWorkflow, $mapping, PagerPage
$page ) {
+ $result = $this->buildEmptyResult( $listWorkflow );
+
+ foreach ( $mapping as $topicId => $postRevision ) {
+ $result['roots'][] = $topicId;
+ $revisionId =
$postRevision->getRevisionId()->getAlphadecimal();
+ $result['posts'][$topicId] = array( $revisionId );
+
+ $contentFormat = 'plaintext';
+ $result['revisions'][$revisionId] = array(
+ // Keep this as a minimal subset of
+ // RevisionFormatter->formatApi, and keep the
same content
+ // format for topic titles as specified in that
class for
+ // topic titles.
+
+ 'content' => array(
+ 'content' =>
$this->templating->getContent(
+ $postRevision,
+ $contentFormat
+ ),
+ 'format' => $contentFormat,
+ ),
+ );
+ }
+
+ $pagingOption = $page->getPagingLinksOptions();
+ $result['links']['pagination'] = $this->buildPaginationLinks(
+ $listWorkflow,
+ $pagingOption
+ );
+
+ return $result;
+ }
+}
diff --git a/includes/Formatter/TopicListFormatter.php
b/includes/Formatter/TopicListFormatter.php
index 72c7e81..1668e64 100644
--- a/includes/Formatter/TopicListFormatter.php
+++ b/includes/Formatter/TopicListFormatter.php
@@ -3,13 +3,11 @@
namespace Flow\Formatter;
use Flow\Data\Pager\PagerPage;
-use Flow\Model\Anchor;
use Flow\Model\Workflow;
-use Flow\Templating;
use Flow\UrlGenerator;
use IContextSource;
-class TopicListFormatter {
+class TopicListFormatter extends BaseTopicListFormatter {
/**
* @var UrlGenerator
*/
@@ -20,29 +18,17 @@
*/
protected $serializer;
- /**
- * @var Templating
- */
- protected $templating;
-
- public function __construct( UrlGenerator $urlGenerator,
RevisionFormatter $serializer, Templating $templating ) {
+ public function __construct( UrlGenerator $urlGenerator,
RevisionFormatter $serializer ) {
$this->urlGenerator = $urlGenerator;
$this->serializer = $serializer;
- $this->templating = $templating;
}
public function buildEmptyResult( Workflow $workflow ) {
$title = $workflow->getArticleTitle();
return array(
- 'type' => 'topiclist',
'title' => $title->getPrefixedText(),
- 'roots' => array(),
- 'posts' => array(),
- 'revisions' => array(),
- 'links' => array(),
'actions' => $this->buildApiActions( $workflow ),
- 'submitted' => array(),
- );
+ ) + parent::buildEmptyResult( $workflow );
}
public function formatApi(
@@ -68,25 +54,6 @@
// Link to designated new-topic page, for no-JS users
$res['links']['newtopic'] =
$this->urlGenerator->newTopicAction( $title, $listWorkflow->getId()
)->getLinkURL();
-
- return $res;
- }
-
- protected function buildPaginationLinks( Workflow $workflow, array
$links ) {
- $res = array();
- $title = $workflow->getArticleTitle();
- foreach ( $links as $key => $options ) {
- // prefix all options with topiclist_
- $realOptions = array();
- foreach ( $options as $k => $v ) {
- $realOptions["topiclist_$k"] = $v;
- }
- $res[$key] = new Anchor(
- $key, // @todo i18n
- $title,
- $realOptions
- );
- }
return $res;
}
diff --git a/includes/Formatter/TopicListQuery.php
b/includes/Formatter/TopicListQuery.php
index d470796..a1bb743 100644
--- a/includes/Formatter/TopicListQuery.php
+++ b/includes/Formatter/TopicListQuery.php
@@ -32,13 +32,13 @@
}
/**
- * @param UUID[]|TopicListEntry[] $topicRevisionIds
+ * @param UUID[]|TopicListEntry[] $topicIdsOrEntries
* @return FormatterRow[]
*/
- public function getResults( array $topicRevisionIds ) {
+ public function getResults( array $topicIdsOrEntries ) {
/** @noinspection PhpUnusedLocalVariableInspection */
$section = new \ProfileSection( __METHOD__ );
- $topicIds = $this->getTopicIds( $topicRevisionIds );
+ $topicIds = $this->getTopicIds( $topicIdsOrEntries );
$allPostIds = $this->collectPostIds( $topicIds );
$topicSummary = $this->collectSummary( $topicIds );
$posts = $this->collectRevisions( $allPostIds );
@@ -98,12 +98,13 @@
}
/**
- * @param TopicListEntry[] $topicListEntries
+ * @param TopicListEntry[]|UUID[] $topicsIdsOrEntries Topic IDs as UUID
entries or
+ * TopicListEntry objects
* @return UUID[]
*/
- protected function getTopicIds( array $topicListEntries ) {
+ protected function getTopicIds( array $topicsIdsOrEntries ) {
$topicIds = array();
- foreach ( $topicListEntries as $entry ) {
+ foreach ( $topicsIdsOrEntries as $entry ) {
if ( $entry instanceof UUID ) {
$topicIds[] = $entry;
} elseif ( $entry instanceof TopicListEntry ) {
diff --git a/includes/api/ApiFlowViewTopicList.php
b/includes/api/ApiFlowViewTopicList.php
index 06e9cd8..53623de 100644
--- a/includes/api/ApiFlowViewTopicList.php
+++ b/includes/api/ApiFlowViewTopicList.php
@@ -19,7 +19,7 @@
}
public function getAllowedParams() {
- global $wgFlowDefaultLimit;
+ global $wgFlowDefaultLimit, $wgFlowMaxLimit;
return array(
'offset-dir' => array(
@@ -43,16 +43,18 @@
ApiBase::PARAM_REQUIRED => false,
),
'limit' => array(
- // @todo once we have a better idea of the
performance of this
- // adjust these to sane defaults
ApiBase::PARAM_TYPE => 'limit',
ApiBase::PARAM_DFLT => $wgFlowDefaultLimit,
- ApiBase::PARAM_MAX => $wgFlowDefaultLimit,
- ApiBase::PARAM_MAX2 => $wgFlowDefaultLimit,
+ ApiBase::PARAM_MAX => $wgFlowMaxLimit,
+ ApiBase::PARAM_MAX2 => $wgFlowMaxLimit,
),
// @todo: I assume render parameter will soon be
removed, after
// frontend rewrite
'render' => array(
+ ApiBase::PARAM_TYPE => 'boolean',
+ ApiBase::PARAM_DFLT => false,
+ ),
+ 'toconly' => array(
ApiBase::PARAM_TYPE => 'boolean',
ApiBase::PARAM_DFLT => false,
),
@@ -68,6 +70,7 @@
'offset' => 'Offset value to start fetching topics at',
'limit' => 'Amount of topics to fetch',
'render' => 'Renders (in HTML) the topics, if set',
+ 'toconly' => 'Whether to respond with only the
information required for the TOC',
);
}
diff --git a/tests/phpunit/api/ApiFlowViewTopicListTest.php
b/tests/phpunit/api/ApiFlowViewTopicListTest.php
new file mode 100644
index 0000000..e23725b
--- /dev/null
+++ b/tests/phpunit/api/ApiFlowViewTopicListTest.php
@@ -0,0 +1,245 @@
+<?php
+
+namespace Flow\Tests\Api;
+
+use Title;
+
+/**
+ * @group Flow
+ * @group medium
+ */
+class ApiFlowViewTopicListTest extends ApiTestCase {
+ const TITLE_PREFIX = 'VTL Test ';
+
+ public function testTocOnly() {
+ $topicData = array();
+ for ( $i = 0; $i < 3; $i++ ) {
+ $title = self::TITLE_PREFIX . $i;
+ $topicData[$i]['response'] = $this->createTopic(
'result', $title );
+ $topicData[$i]['id'] =
$topicData[$i]['response']['roots'][0];
+ $topicData[$i]['revisionId'] =
$topicData[$i]['response']['posts'][$topicData[$i]['id']][0];
+ $topicData[$i]['expectedRevision'] = array(
+ 'content' => array(
+ 'content' => $title,
+ 'format' => 'plaintext'
+ ),
+ );
+ }
+
+ $flowQaTitle = Title::newFromText( 'Talk:Flow_QA' );
+
+ $expectedCommonResponse = array(
+ 'flow' => array(
+ 'view-topiclist' => array(
+ 'result' => array(
+ 'topiclist' => array(
+ 'submitted' => array(
+ 'savesortby' =>
false,
+ 'offset-dir' =>
'fwd',
+ 'offset-id' =>
null,
+ 'offset' =>
null,
+ 'limit' => 2,
+ 'render' =>
false,
+ 'toconly' =>
true
+ ),
+ 'errors' => array(),
+ 'type' => 'topiclist',
+ ),
+ ),
+ 'status' => 'ok',
+ ),
+ ),
+ );
+
+ $expectedEmptyPageResponse = array_merge_recursive( array(
+ 'flow' => array(
+ 'view-topiclist' => array(
+ 'result' => array(
+ 'topiclist' => array(
+ 'submitted' => array(
+ 'sortby' =>
'newest',
+ ),
+ 'roots' => array(),
+ 'posts' => array(),
+ 'revisions' => array(),
+ 'links' => array(
+ 'pagination' =>
array(),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ), $expectedCommonResponse );
+
+ $actualEmptyPageResponse = $this->doApiRequest(
+ array(
+ 'action' => 'flow',
+ 'page' => 'Talk:Intentionally blank',
+ 'submodule' => 'view-topiclist',
+ 'vtllimit' => 2,
+ 'vtltoconly' => true,
+ )
+ );
+ $actualEmptyPageResponse = $actualEmptyPageResponse[0];
+
+ $this->assertEquals(
+ $expectedEmptyPageResponse,
+ $actualEmptyPageResponse,
+ 'TOC-only output for an empty, but occupied, Flow board'
+ );
+
+ $expectedNewestResponse = array_merge_recursive( array(
+ 'flow' => array(
+ 'view-topiclist' => array(
+ 'result' => array(
+ 'topiclist' => array(
+ 'submitted' => array(
+ 'sortby' =>
'newest',
+ ),
+ 'sortby' => '',
+ 'roots' => array(
+
$topicData[2]['id'],
+
$topicData[1]['id'],
+ ),
+ 'posts' => array(
+
$topicData[2]['id'] => $topicData[2]['response']['posts'][$topicData[2]['id']],
+
$topicData[1]['id'] => $topicData[1]['response']['posts'][$topicData[1]['id']],
+ ),
+ 'revisions' => array(
+
$topicData[2]['revisionId'] => $topicData[2]['expectedRevision'],
+
$topicData[1]['revisionId'] => $topicData[1]['expectedRevision'],
+ ),
+ 'links' => array(
+ 'pagination' =>
array(
+ 'fwd'
=> array(
+
'url' => $flowQaTitle->getLinkURL( array(
+
'topiclist_offset-dir' => 'fwd',
+
'topiclist_limit' => '2',
+
'topiclist_offset-id' => $topicData[1]['id'],
+
) ),
+
'title' => 'fwd',
+ ),
+ ),
+ ),
+ ),
+ )
+ )
+ )
+ ), $expectedCommonResponse );
+
+ $actualNewestResponse = $this->doApiRequest(
+ array(
+ 'action' => 'flow',
+ 'page' => 'Talk:Flow QA',
+ 'submodule' => 'view-topiclist',
+ 'vtllimit' => 2,
+ 'vtlsortby' => 'newest',
+ 'vtltoconly' => true,
+ )
+ );
+ $actualNewestResponse = $actualNewestResponse[0];
+
+ $this->assertEquals(
+ $expectedNewestResponse,
+ $actualNewestResponse,
+ 'TOC-only output for "newest" order'
+ );
+
+ // Make it so update order is chronologically (1, 0, 2)
+ // We then expect it to be returned reverse chronologically (2,
0)
+
+ $updateList = array( 1, 0, 2);
+
+ foreach ( $updateList as $updateListInd => $topicDataInd ) {
+ $replyResponse = $this->doApiRequest(
+ array(
+ 'action' => 'flow',
+ 'page' => Title::makeTitle( NS_TOPIC,
$topicData[$topicDataInd]['id'] )->getPrefixedText(),
+ 'submodule' => 'reply',
+ 'token' => $this->getEditToken(),
+ 'repreplyTo' =>
$topicData[$topicDataInd]['id'],
+ 'repcontent' => "Reply to topic
$topicDataInd",
+ )
+ );
+
+ // This is because we use timestamps with second
granularity.
+ // Without this, the timestamp can be exactly the same
+ // for two topics, which means the ordering is
undefined (and thus
+ // untestable). This was causing failures on Jenkins.
+ //
+ // Possible improvement: Make a simple class for
getting the current
+ // time that normally calls wfTimestampNow. Have an
alternative
+ // implementation for tests that can be controlled by
an API like
+ // http://sinonjs.org/ (which we use on the client
side).
+ // Pimple can be in charge of which is used.
+ if ( $updateListInd !== ( count( $updateList ) - 1 ) ) {
+ sleep( 1 );
+ }
+
+ $replyResponse = $replyResponse[0];
+
+ $responseTopic =
$replyResponse['flow']['reply']['result']['topic'];
+ $topicRevisionId =
$topicData[$topicDataInd]['revisionId'];
+ $newPostId = end(
$responseTopic['revisions'][$topicRevisionId]['replies'] );
+ $topicData[$topicDataInd]['updateTimestamp'] =
$responseTopic['revisions'][$newPostId]['timestamp'];
+ }
+
+ $expectedUpdatedResponse = array_merge_recursive( array(
+ 'flow' => array(
+ 'view-topiclist' => array(
+ 'result' => array(
+ 'topiclist' => array(
+ 'submitted' => array(
+ 'sortby' =>
'updated',
+ ),
+ 'sortby' => 'updated',
+ 'roots' => array(
+
$topicData[2]['id'],
+
$topicData[0]['id'],
+ ),
+ 'posts' => array(
+
$topicData[2]['id'] => $topicData[2]['response']['posts'][$topicData[2]['id']],
+
$topicData[0]['id'] => $topicData[0]['response']['posts'][$topicData[0]['id']],
+ ),
+ 'revisions' => array(
+
$topicData[2]['revisionId'] => $topicData[2]['expectedRevision'],
+
$topicData[0]['revisionId'] => $topicData[0]['expectedRevision'],
+ ),
+ 'links' => array(
+ 'pagination' =>
array(
+ 'fwd'
=> array(
+
'url' => $flowQaTitle->getLinkURL( array(
+
'topiclist_offset-dir' => 'fwd',
+
'topiclist_limit' => '2',
+
'topiclist_offset' => $topicData[0]['updateTimestamp'],
+
'topiclist_sortby' => 'updated',
+
) ),
+
'title' => 'fwd',
+ ),
+ ),
+ ),
+ ),
+ )
+ )
+ )
+ ), $expectedCommonResponse );
+
+ $actualUpdatedResponse = $this->doApiRequest(
+ array(
+ 'action' => 'flow',
+ 'page' => 'Talk:Flow QA',
+ 'submodule' => 'view-topiclist',
+ 'vtllimit' => 2,
+ 'vtlsortby' => 'updated',
+ 'vtltoconly' => true,
+ )
+ );
+ $actualUpdatedResponse = $actualUpdatedResponse[0];
+
+ $this->assertEquals(
+ $expectedUpdatedResponse,
+ $actualUpdatedResponse,
+ 'TOC-only output for "updated" order'
+ );
+ }
+}
diff --git a/tests/phpunit/api/ApiTestCase.php
b/tests/phpunit/api/ApiTestCase.php
index fe4a05e..030c44f 100644
--- a/tests/phpunit/api/ApiTestCase.php
+++ b/tests/phpunit/api/ApiTestCase.php
@@ -14,7 +14,14 @@
*/
abstract class ApiTestCase extends BaseApiTestCase {
protected function setUp() {
- $this->setMwGlobals( 'wgFlowOccupyPages', array( 'Talk:Flow QA'
) );
+ $this->setMwGlobals( 'wgFlowOccupyPages', array(
+ // For testing use; shared with browser tests
+ 'Talk:Flow QA',
+
+ // Don't do any write operations on this. It's
intentionally left
+ // blank for testing read operations on unused (but
occupied) pages.
+ 'Talk:Intentionally blank',
+ ) );
parent::setUp();
}
@@ -49,13 +56,13 @@
/**
* Create a topic on a board using the default user
*/
- protected function createTopic( $return = '' ) {
+ protected function createTopic( $return = '', $topicTitle = 'Hi there!'
) {
$data = $this->doApiRequest( array(
'page' => 'Talk:Flow QA',
'token' => $this->getEditToken(),
'action' => 'flow',
'submodule' => 'new-topic',
- 'nttopic' => 'Hi there!',
+ 'nttopic' => $topicTitle,
'ntcontent' => '...',
) );
$this->assertTrue(
--
To view, visit https://gerrit.wikimedia.org/r/169310
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I3615817edb1500bd63dc4cbd64820fa25c943e21
Gerrit-PatchSet: 15
Gerrit-Project: mediawiki/extensions/Flow
Gerrit-Branch: master
Gerrit-Owner: Mattflaschen <[email protected]>
Gerrit-Reviewer: EBernhardson <[email protected]>
Gerrit-Reviewer: Legoktm <[email protected]>
Gerrit-Reviewer: Mattflaschen <[email protected]>
Gerrit-Reviewer: SG <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits