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

Reply via email to