public function testPermissions()
 {
     $t = new ModerationTestsuite();
     $t->loginAs($t->unprivilegedUser);
     $title = $t->html->getTitle($t->getSpecialURL());
     $this->assertRegExp('/\\(permissionserrors\\)/', $title);
 }
 public function testPostEditRedirect()
 {
     $t = new ModerationTestsuite();
     $t->loginAs($t->unprivilegedUser);
     $req = $t->doTestEdit();
     $t->fetchSpecial();
     $this->assertTrue($req->status->isOK());
     $this->assertTrue($req->isRedirect(), "testPostEditRedirect(): User hasn't been redirected after the edit");
     # Check the redirect URL
     $url = $req->getResponseHeader("Location");
     $params = wfCgiToArray(preg_replace('/^.*?\\?/', '', $url));
     $this->assertArrayHasKey('title', $params);
     $this->assertArrayHasKey('modqueued', $params);
     $this->assertCount(2, $params, "testPostEditRedirect(): redirect URL has parameters other than 'title' and 'modqueued'");
     $this->assertEquals($t->lastEdit['Title'], preg_replace('/_/', ' ', $params['title']), "testPostEditRedirect(): Title in the redirect URL doesn't match the title of page we edited");
     $this->assertEquals(1, $params['modqueued'], "testPostEditRedirect(): parameter modqueued=1 not found in the redirect URL");
     # Check the page where the user is being redirected to
     $list = $t->html->getLoaderModulesList($url);
     $this->assertContains('ext.moderation.notify', $list, "testPostEditRedirect(): Module ext.moderation.notify wasn't loaded");
     # Usual checks on whether the edit not via API was intercepted.
     $this->assertCount(1, $t->new_entries, "testPostEditRedirect(): One edit was queued for moderation, but number of added entries in Pending folder isn't 1");
     $this->assertCount(0, $t->deleted_entries, "testPostEditRedirect(): Something was deleted from Pending folder during the queueing");
     $this->assertEquals($t->lastEdit['User'], $t->new_entries[0]->user);
     $this->assertEquals($t->lastEdit['Title'], $t->new_entries[0]->title);
 }
 public function testRejectAll()
 {
     $t = new ModerationTestsuite();
     # We edit with two users:
     #	$t->unprivilegedUser (A)
     #	and $t->unprivilegedUser2 (B)
     # We're applying rejectall to one of the edits by A.
     # Expected result is:
     # 1) All edits by A were rejected,
     # 2) No edits by B were touched during rejectall.
     $t->doNTestEditsWith($t->unprivilegedUser, $t->unprivilegedUser2);
     $t->fetchSpecial();
     # Find edits by user A (they will be rejected)
     $entries = ModerationTestsuiteEntry::findByUser($t->new_entries, $t->unprivilegedUser);
     $this->assertNotNull($entries[0]->rejectAllLink, "testRejectAll(): RejectAll link not found");
     $t->html->loadFromURL($entries[0]->rejectAllLink);
     $this->assertRegExp('/\\(moderation-rejected-ok: ' . $t->TEST_EDITS_COUNT . '\\)/', $t->html->getMainText(), "testRejectAll(): Result page doesn't contain (moderation-rejected-ok: N)");
     $t->fetchSpecial();
     $this->assertCount(0, $t->new_entries, "testRejectAll(): Something was added into Pending folder during modaction=rejectall");
     $this->assertCount($t->TEST_EDITS_COUNT, $t->deleted_entries, "testRejectAll(): Several edits were rejected, but number of deleted entries in Pending folder doesn't match");
     foreach ($entries as $entry) {
         $de = ModerationTestsuiteEntry::findById($t->deleted_entries, $entry->id);
         $this->assertNotNull($de);
         $this->assertEquals($entry->user, $de->user);
         $this->assertEquals($entry->title, $de->title);
     }
     $t->fetchSpecial('rejected');
     $this->assertCount($t->TEST_EDITS_COUNT, $t->new_entries, "testRejectAll(): Several edits were rejected, but number of new entries in Rejected folder doesn't match");
     $this->assertCount(0, $t->deleted_entries, "testRejectAll(): Something was deleted from Rejected folder during modaction=rejectall");
     foreach ($entries as $entry) {
         $de = ModerationTestsuiteEntry::findById($t->new_entries, $entry->id);
         $this->assertNotNull($de);
         $this->assertEquals($entry->user, $de->user);
         $this->assertEquals($entry->title, $de->title);
         $this->assertEquals($t->moderator->getName(), $de->rejected_by_user);
         $this->assertTrue($de->rejected_batch, "testRejectAll(): Edit rejected via modaction=rejectall has rejected_batch flag OFF");
         $this->assertFalse($de->rejected_auto, "testRejectAll(): Manually rejected edit has rejected_auto flag ON");
         $this->assertNull($de->rejectLink, "testRejectAll(): Reject link found for already rejected edit");
         $this->assertNull($de->rejectAllLink, "testRejectAll(): RejectAll link found for already rejected edit");
         $this->assertNull($de->approveAllLink, "testRejectAll(): ApproveAll link found for already rejected edit");
     }
     # Check the log entry: there should be only one 'rejectall'.
     $events = $t->apiLogEntries();
     $this->assertCount(1, $events, "testRejectAll(): Number of log entries isn't 1.");
     $le = $events[0];
     $this->assertEquals('rejectall', $le['action'], "testRejectAll(): Most recent log entry is not 'rejectall'");
     $this->assertEquals($t->moderator->getName(), $le['user']);
     $this->assertEquals($t->unprivilegedUser->getUserPage(), $le['title']);
     $this->assertEquals($t->TEST_EDITS_COUNT, $le['count']);
     $events = $t->nonApiLogEntries(1);
     $this->assertEquals('rejectall', $events[0]['type']);
     $this->assertEquals($t->moderator->getName(), $events[0]['params'][1]);
     $this->assertEquals($t->unprivilegedUser->getUserPage()->getText(), $events[0]['params'][2]);
     $this->assertEquals($t->TEST_EDITS_COUNT, $events[0]['params'][3]);
 }
 public function testAutomoderated()
 {
     $t = new ModerationTestsuite();
     $t->loginAs($t->automoderated);
     $t->editViaAPI = true;
     $ret = $t->doTestEdit();
     $t->fetchSpecial();
     $this->assertArrayHasKey('edit', $ret);
     $this->assertEquals('Success', $ret['edit']['result']);
     $this->assertCount(0, $t->new_entries, "testAutomoderated(): Something was added into Pending folder");
 }
 public function testPreloadSummary()
 {
     $t = new ModerationTestsuite();
     # Summaries are only preloaded for existing pages, so we need
     # to create the page first. (see KNOWN_LIMITATIONS for details)
     $page = "Test page 1";
     $summary = "The quick brown fox jumps over the lazy dog";
     $t->loginAs($t->automoderated);
     $t->doTestEdit($page, "Text 1");
     $t->loginAs($t->unprivilegedUser);
     $t->doTestEdit($page, "Another text", $summary);
     $this->assertEquals($t->lastEdit['Text'], $t->html->getPreloadedText($t->lastEdit['Title']), "testPreloadSummary(): Preloaded text differs from what the user saved before");
     $elem = $t->html->getElementById('wpSummary');
     $this->assertTrue($elem->hasAttribute('value'), "testPreloadSummary(): #wpSummary doesn't have a 'value' attribute");
     $this->assertEquals($summary, $elem->getAttribute('value'), "testPreloadSummary(): Preloaded summary doesn't match");
     $this->assertContains('ext.moderation.edit', $t->html->getLoaderModulesList(), "testPreloadSummary(): Module ext.moderation.edit wasn't loaded");
     $elem = $t->html->getElementById('mw-editing-your-version');
     $this->assertNotNull($elem, "testPreloadSummary(): #mw-editing-your-version not found");
     $this->assertEquals('(moderation-editing-your-version)', $elem->textContent, "testPreloadSummary(): #mw-editing-your-version doesn't contain (moderation-editing-your-version) message");
 }
 /**
 	@covers ModerationCheckUserHook
 */
 public function testPreverveUserAgent()
 {
     global $wgSpecialPages;
     $t = new ModerationTestsuite();
     $dbw = wfGetDB(DB_MASTER);
     if (!array_key_exists('CheckUser', $wgSpecialPages) || !$dbw->tableExists('cu_changes')) {
         $this->markTestIncomplete('Test skipped: CheckUser extension must be installed to run it.');
     }
     $moderatorUA = 'UserAgent of Moderator/1.0';
     $userUA = 'UserAgent of UnprivilegedUser/1.0';
     # When the edit is approved, cu_changes.cuc_agent field should
     # contain UserAgent of user who made the edit,
     # not UserAgent or the moderator who approved it.
     $t->setUserAgent($userUA);
     $entry = $t->getSampleEntry();
     $t->setUserAgent($moderatorUA);
     $t->httpGet($entry->approveLink);
     $row = $dbw->selectRow('cu_changes', array('cuc_agent AS agent'), array('1'), __METHOD__, array('ORDER BY' => 'cuc_id DESC', 'LIMIT' => 1));
     $this->assertNotEquals($moderatorUA, $row->agent, "testPreverveUserAgent(): UserAgent in checkuser tables matches moderator's UserAgent");
     $this->assertEquals($userUA, $row->agent, "testPreverveUserAgent(): UserAgent in checkuser tables doesn't match UserAgent of user who made the edit");
 }
 public function testEditSections()
 {
     $t = new ModerationTestsuite();
     # Note: we must do more than one edit here,
     # because sections-related code in ModerationEditHooks is only
     # used when user makes more than one edit to the same page.
     #
     # On the first edit, mod_text doesn't exist and therefore
     # doesn't need to be corrected.
     $sections = array("Text in zero section\n\n", "== First section ==\nText in first section\n\n", "== Second section ==\nText in second section\n\n", "== Third section ==\nText in third section\n\n");
     $title = 'Test page 1';
     $text = join('', $sections);
     $t->loginAs($t->automoderated);
     $t->doTestEdit($title, $text);
     $t->loginAs($t->unprivilegedUser);
     # Do several edits in the different sections of the text.
     $query = array('action' => 'edit', 'title' => $title, 'token' => null);
     $query['section'] = 0;
     $query['text'] = $sections[0] = "New text in zero section\n\n";
     $t->query($query);
     $query['section'] = 2;
     $query['text'] = $sections[2] = "== Second section (#2) ==\nText in second section\n\n";
     $t->query($query);
     $query['section'] = 'new';
     $query['text'] = $sections[] = "== New section ==\nText in the new section";
     $t->query($query);
     $t->fetchSpecial();
     $dbw = wfGetDB(DB_MASTER);
     $row = $dbw->selectRow('moderation', array('mod_text AS text'), array('mod_id' => $t->new_entries[0]->id), __METHOD__);
     $expected_text = join('', $sections);
     $this->assertEquals($expected_text, $row->text, "testEditSections(): Resulting text doesn't match expected");
     # Does PreSaveTransform work when editing sections?
     $t->loginAs($t->unprivilegedUser);
     $query['section'] = 2;
     $query['text'] = "== New section 2 ==\n~~~\n\n";
     $ret = $t->query($query);
     $row = $dbw->selectRow('moderation', array('mod_text AS text'), array('mod_id' => $t->new_entries[0]->id), __METHOD__);
     $this->assertNotRegExp('/~~~/', $row->text, "testEditSections(): Signature (~~~~) hasn't been properly substituted.");
 }
 public function testEditNoChange()
 {
     $t = new ModerationTestsuite();
     $page = 'Test page 1';
     $text = 'This is some ext';
     $t->loginAs($t->automoderated);
     $t->doTestEdit($page, $text);
     $t->loginAs($t->unprivilegedUser);
     $t->doTestEdit($page, $text);
     # Make zero edit
     $t->fetchSpecial();
     $entry = $t->new_entries[0];
     $error = $t->html->getModerationError($entry->approveLink);
     $this->assertEquals('(edit-no-change)', $error);
 }
 /**
 	@covers ModerationApproveHook
 */
 public function testReupload()
 {
     $t = new ModerationTestsuite();
     $title = "Test image 1.png";
     # Upload the image first
     $t->loginAs($t->automoderated);
     $t->doTestUpload($title, __DIR__ . "/../resources/image640x50.png", "Text 1");
     # Now queue reupload for moderation
     $t->loginAs($t->unprivilegedUser);
     $error = $t->doTestUpload($title, __DIR__ . "/../resources/image100x100.png", "Text 2");
     $t->fetchSpecial();
     # Was the reupload queued for moderation?
     $this->assertEquals('(moderation-image-queued)', $error);
     # Is the data on Special:Moderation correct?
     $entry = $t->new_entries[0];
     $this->assertCount(1, $t->new_entries, "testReupload(): One upload was queued for moderation, but number of added entries in Pending folder isn't 1");
     $this->assertCount(0, $t->deleted_entries, "testReupload(): Something was deleted from Pending folder during the queueing");
     $this->assertEquals($t->lastEdit['User'], $entry->user);
     $this->assertEquals($t->lastEdit['Title'], $entry->title);
     # Does modaction=show display (moderation-diff-reupload) message?
     $this->assertRegExp('/\\(moderation-diff-reupload\\)/', $t->html->getMainText($entry->showLink), "testReupload(): (moderation-diff-reupload) not found in the output of modaction=show");
     # Can we approve this reupload?
     $this->assertNotNull($entry->approveLink, "testReupload(): Approve link not found");
     $t->html->loadFromURL($entry->approveLink);
     $this->assertRegExp('/\\(moderation-approved-ok: 1\\)/', $t->html->getMainText(), "testReupload(): Result page doesn't contain (moderation-approved-ok: 1)");
     # Has the file been reuploaded after the approval?
     $ret = $t->query(array('action' => 'query', 'prop' => 'imageinfo', 'iilimit' => 1, 'iiprop' => 'user|timestamp|comment|size|url|sha1', 'titles' => $entry->title));
     $ret_page = array_shift($ret['query']['pages']);
     $ii = $ret_page['imageinfo'][0];
     $this->assertEquals($t->lastEdit['User'], $ii['user']);
     $this->assertEquals($t->lastEdit['Text'], $ii['comment']);
     $this->assertEquals($t->lastEdit['SHA1'], $ii['sha1']);
     # Check image page history: performUpload(... $user) mistakenly
     # tags image reuploads as made by moderator (and not $user).
     # Was that fixed? (via ModerationApproveHook class)
     $ret = $t->query(array('action' => 'query', 'prop' => 'revisions', 'rvlimit' => 2, 'rvprop' => 'user|timestamp|comment|content|ids', 'titles' => $entry->title));
     # Because API orders entries by timestamp (up to seconds), and
     # it's likely that two uploads we just made will have the same
     # timestamp, they may be ordered incorrectly ([0] not being the
     # most recent). So find the entry with 'parentid' referring to
     # the other entry.
     $ret_page = array_shift($ret['query']['pages']);
     $rev1 = $ret_page['revisions'][0];
     $rev2 = $ret_page['revisions'][1];
     # Make $rev1 the most recent edit
     if ($rev2['parentid'] == $rev1['revid']) {
         $tmp = $rev1;
         $rev1 = $rev2;
         $rev2 = $tmp;
     }
     $this->assertEquals($rev2['revid'], $rev1['parentid'], "testReupload(): parentid of new revision doesn't match revid of the previous revision");
     $this->assertNotEquals($t->moderator->getName(), $rev1['user'], "testReupload(): Image reupload was attributed to the moderator who approved it (instead of the user who made the reupload)");
     $this->assertEquals($t->lastEdit['User'], $rev1['user'], "testReupload(): Image reupload wasn't attributed to the user who made it");
 }
 public function testTokens()
 {
     $t = new ModerationTestsuite();
     $t->loginAs($t->unprivilegedUser);
     $t->doTestEdit();
     $t->fetchSpecial();
     $entry = $t->new_entries[0];
     $entry->fakeBlockLink();
     # Non-readonly actions require a correct token
     $links = array($entry->approveLink, $entry->approveAllLink, $entry->rejectLink, $entry->rejectAllLink, $entry->blockLink, $entry->unblockLink);
     foreach ($links as $url) {
         $this->assertRegExp('/\\(sessionfailure-title\\)/', $t->noTokenTitle($url));
         /* Double-check that nothing happened */
         $t->fetchSpecial();
         $this->assertCount(0, $t->new_entries);
         $this->assertCount(0, $t->deleted_entries);
         # Would the wrong token work?
         $this->assertRegExp('/\\(sessionfailure-title\\)/', $t->badTokenTitle($url));
         /* Double-check that nothing happened */
         $t->fetchSpecial();
         $this->assertCount(0, $t->new_entries);
         $this->assertCount(0, $t->deleted_entries);
     }
 }
 public function testBlock()
 {
     $t = new ModerationTestsuite();
     $entry = $t->getSampleEntry('Test page 1');
     $this->assertNotNull($entry->blockLink, "testBlock(): Block link not found for non-blocked user");
     $this->assertNull($entry->unblockLink, "testBlock(): Unblock link found for non-blocked user");
     $t->html->loadFromURL($entry->blockLink);
     $this->assertRegExp('/\\(moderation-block-ok: ' . preg_quote($entry->user) . '\\)/', $t->html->getMainText(), "testBlock(): Result page doesn't contain (moderation-block-ok)");
     # Now that the user is blocked, try to edit
     $t->loginAs($t->unprivilegedUser);
     $t->doTestEdit('Test page 2');
     $t->fetchSpecial();
     $this->assertCount(0, $t->new_entries, "testBlock(): Something was added into Pending folder when queueing an edit from spammer");
     $this->assertCount(0, $t->deleted_entries, "testBlock(): Something was deleted from Pending folder when queueing an edit from spammer");
     $t->fetchSpecial('spam');
     $this->assertCount(1, $t->new_entries, "testBlock(): One edit from spammer was queued for moderation, but number of added entries in Spam folder isn't 1");
     $this->assertCount(0, $t->deleted_entries, "testBlock(): Something was deleted from Spam folder during the queueing");
     $entry = $t->new_entries[0];
     $this->assertEquals($t->lastEdit['User'], $entry->user);
     $this->assertEquals($t->lastEdit['Title'], $entry->title);
     $this->assertFalse($entry->rejected_batch, "testBlock(): Edit rejected automatically has rejected_batch flag ON");
     $this->assertTrue($entry->rejected_auto, "testBlock(): Edit rejected automatically edit has rejected_auto flag OFF");
     $this->assertNull($entry->blockLink, "testBlock(): Block link found for blocked user");
     $this->assertNotNull($entry->unblockLink, "testBlock(): Unblock link not found for blocked user");
     $this->assertNull($entry->rejectLink, "testBlock(): Reject link found for already rejected edit");
     $this->assertNull($entry->rejectAllLink, "testBlock(): RejectAll link found for already rejected edit");
     $this->assertNull($entry->approveAllLink, "testBlock(): ApproveAll link found for already rejected edit");
     # Check 'block' log entry
     $events = $t->apiLogEntries();
     $this->assertCount(1, $events, "testBlock(): Wrong number of log entries after modaction=block.");
     $le = $events[0];
     $this->assertEquals('block', $le['action'], "testBlock(): Most recent log entry is not 'block'");
     $this->assertEquals($t->moderator->getName(), $le['user']);
     $this->assertEquals($t->unprivilegedUser->getUserPage(), $le['title']);
     $events = $t->nonApiLogEntries(1);
     $this->assertEquals('block', $events[0]['type']);
     $this->assertEquals($t->moderator->getName(), $events[0]['params'][1]);
     $this->assertEquals($t->unprivilegedUser->getUserPage()->getText(), $events[0]['params'][2]);
     # Unblock the user
     $t->html->loadFromURL($entry->unblockLink);
     $this->assertRegExp('/\\(moderation-unblock-ok: ' . preg_quote($entry->user) . '\\)/', $t->html->getMainText(), "testBlock(): Result page doesn't contain (moderation-unblock-ok)");
     # Check that the user is no longer considered a spammer...
     $t->loginAs($t->unprivilegedUser);
     $t->doTestEdit('Test page 3');
     $t->fetchSpecial('spam');
     $this->assertCount(0, $t->new_entries, "testBlock(): Something was added into Spam folder when queueing an edit from non-spammer");
     $this->assertCount(0, $t->deleted_entries, "testBlock(): Something was deleted from Spam folder when queueing an edit from non-spammer");
     $t->fetchSpecial();
     $this->assertCount(1, $t->new_entries, "testBlock(): One edit from non-spammer was queued for moderation, but number of added entries in Pending folder isn't 1");
     $this->assertCount(0, $t->deleted_entries, "testBlock(): Something was deleted from Pending folder when queueing an edit from non-spammer");
     $entry = $t->new_entries[0];
     $this->assertEquals($t->lastEdit['User'], $entry->user);
     $this->assertEquals($t->lastEdit['Title'], $entry->title);
     $this->assertNotNull($entry->blockLink, "testBlock(): Block link not found for no-longer-blocked user");
     $this->assertNull($entry->unblockLink, "testBlock(): Unblock link found for no-longer-blocked user");
     # Check 'unblock' log entry
     $events = $t->apiLogEntries();
     $this->assertCount(2, $events, "testBlock(): Wrong number of log entries after modaction=unblock.");
     $le = $events[0];
     $this->assertEquals('unblock', $le['action'], "testBlock(): Most recent log entry is not 'unblock'");
     $this->assertEquals($t->moderator->getName(), $le['user']);
     $this->assertEquals($t->unprivilegedUser->getUserPage(), $le['title']);
     $events = $t->nonApiLogEntries(1);
     $this->assertEquals('unblock', $events[0]['type']);
     $this->assertEquals($t->moderator->getName(), $events[0]['params'][1]);
     $this->assertEquals($t->unprivilegedUser->getUserPage()->getText(), $events[0]['params'][2]);
 }
 /**
 	@covers ModerationActionShowImage
 	@requires extension curl
 	@note Only cURL version of MWHttpRequest supports uploads.
 */
 public function testShowUpload()
 {
     $t = new ModerationTestsuite();
     /*
     	When testing thumbnails, we check two images -
     	one smaller than thumbnail's width, one larger,
     	because they are handled differently.
     
     	First test is on image640x50.png (large image),
     	second on image100x100.png (smaller image).
     */
     $t->loginAs($t->unprivilegedUser);
     $error = $t->doTestUpload("Test image 1.png", __DIR__ . "/../resources/image640x50.png", "");
     $t->fetchSpecial();
     $entry = $t->new_entries[0];
     $url = $entry->showLink;
     $this->assertNotNull($url, "testShowUpload(): Show link not found");
     $title = $t->html->getTitle($url);
     $this->assertRegExp('/\\(difference-title: ' . $t->lastEdit['Title'] . '\\)/', $title, "testShowUpload(): Difference page has a wrong HTML title");
     $this->assertRegExp('/\\(moderation-diff-upload-notext\\)/', $t->html->getMainText(), "testShowUpload(): File was uploaded without description, but (moderation-diff-upload-notext) is not shown");
     # Is the image thumbnail displayed on the difference page?
     $images = $t->html->getElementsByTagName('img');
     $thumb = null;
     $src = null;
     foreach ($images as $img) {
         $src = $img->getAttribute('src');
         if (strpos($src, 'modaction=showimg') !== false) {
             $thumb = $img;
             break;
         }
     }
     $this->assertNotNull($thumb, "testShowUpload(): Thumbnail image not found");
     $this->assertRegExp('/thumb=1/', $src, "testShowUpload(): Thumbnail image URL doesn't contain thumb=1");
     # Is the image thumbnail inside the link to the full image?
     $link = $thumb->parentNode;
     $this->assertEquals('a', $link->nodeName, "testShowUpload(): Thumbnail image isn't encased in <a> tag");
     $href = $link->getAttribute('href');
     $this->assertEquals($entry->expectedShowImgLink(), $href, "testShowUpload(): Full image URL doesn't match expected URL");
     $nonthumb_src = str_replace('&thumb=1', '', $src);
     $this->assertEquals($nonthumb_src, $href, "testShowUpload(): Full image URL doesn't match thumbnail image URL without '&thumb=1'");
     $this->assertNotRegExp('/token=/', $href, "testShowUpload(): Token was found in the read-only ShowImage link");
     # Check the full image
     $req = $t->httpGet($href);
     $this->assertEquals('image/png', $req->getResponseHeader('Content-Type'), "testShowUpload(): Wrong Content-Type header from modaction=showimg");
     $this->assertEquals($t->lastEdit['SHA1'], sha1($req->getContent()), "testShowUpload(): Checksum of image downloaded via modaction=showimg doesn't match the checksum of original image");
     $this->assertEquals("inline;filename*=UTF-8''Test%20image%201.png", $req->getResponseHeader('Content-Disposition'), "testShowUpload(640x50): Wrong Content-Disposition header from modaction=showimg");
     # Check the thumbnail
     $req = $t->httpGet($src);
     # Content-type check will catch HTML errors from StreamFile
     $this->assertRegExp('/^image\\//', $req->getResponseHeader('Content-Type'), "testShowUpload(640x50): Wrong Content-Type header from modaction=showimg&thumb=1");
     $this->assertEquals("inline;filename*=UTF-8''" . ModerationActionShowImage::THUMB_WIDTH . "px-Test%20image%201.png", $req->getResponseHeader('Content-Disposition'), "testShowUpload(640x50): Wrong Content-Disposition header from modaction=showimg&thumb=1");
     list($original_width, $original_height) = getimagesize($t->lastEdit['Source']);
     list($width, $height) = getImageSizeFromString($req->getContent());
     $orig_ratio = round($original_width / $original_height, 2);
     $ratio = round($width / $height, 2);
     # As this image is larger than THUMB_WIDTH,
     # its thumbnail must be exactly THUMB_WIDTH wide.
     $this->assertEquals(ModerationActionShowImage::THUMB_WIDTH, $width, "testShowUpload(): Thumbnail's width doesn't match expected");
     $this->assertEquals($orig_ratio, $ratio, "testShowUpload(): Thumbnail's ratio doesn't match original");
     # Check the thumbnail of image smaller than THUMB_WIDTH.
     # Its thumbnail must be exactly the same size as original image.
     $t->loginAs($t->unprivilegedUser);
     $t->doTestUpload("Test image 2.png", __DIR__ . "/../resources/image100x100.png", "Non-empty image description");
     $t->fetchSpecial();
     $req = $t->httpGet($t->new_entries[0]->expectedShowImgLink());
     list($original_width, $original_height) = getimagesize($t->lastEdit['Source']);
     list($width, $height) = getImageSizeFromString($req->getContent());
     $this->assertRegExp('/^image\\//', $req->getResponseHeader('Content-Type'), "testShowUpload(100x100): Wrong Content-Type header from modaction=showimg&thumb=1");
     # No "px-" in the filename, because this thumbnail isn't different from the original file
     $this->assertEquals("inline;filename*=UTF-8''Test%20image%202.png", $req->getResponseHeader('Content-Disposition'), "testShowUpload(100x100): Wrong Content-Disposition header from modaction=showimg&thumb=1");
     $this->assertEquals($original_width, $width, "testShowUpload(): Original image is smaller than THUMB_WIDTH, but thumbnail width doesn't match the original width");
     $this->assertEquals($original_height, $height, "testShowUpload(): Original image is smaller than THUMB_WIDTH, but thumbnail height doesn't match the original height");
     # Ensure absence of (moderation-diff-upload-notext)
     $this->assertNotRegExp('/\\(moderation-diff-upload-notext\\)/', $t->html->getMainText($t->new_entries[0]->showLink), "testShowUpload(): File was uploaded with description, but (moderation-diff-upload-notext) is shown");
 }
 public function testApproveAllConflicts()
 {
     $t = new ModerationTestsuite();
     $t->doNTestEditsWith($t->unprivilegedUser, null, 'Page A');
     $this->makeEditConflict($t);
     $t->doNTestEditsWith($t->unprivilegedUser, null, 'Page B');
     # Will attempt to ApproveAll the edit by $t->unprivilegedUser
     # cause an edit conflict?
     $t->fetchSpecial();
     $t->html->loadFromURL($t->new_entries[0]->approveAllLink);
     $text = $t->html->getMainText();
     $this->assertRegExp('/\\(moderation-approved-ok: ' . $t->TEST_EDITS_COUNT * 2 . '\\)/', $text, "testApproveAllConflicts(): Result page doesn't contain (moderation-approved-ok: N)");
     $this->assertRegExp('/\\(moderation-approved-errors: 1\\)/', $text, "testApproveAllConflicts(): Result page doesn't contain (moderation-approved-errors: 1)");
     $t->assumeFolderIsEmpty();
     $t->fetchSpecial();
     $this->assertCount(1, $t->new_entries, "testApproveAllConflicts(): Nothing left in Pending folder after modaction=approveall, even though there was an edit conflict");
     $this->assertTrue($t->new_entries[0]->conflict, "testApproveAllConflicts(): Edit with detected conflict is not marked with class='modconflict'");
 }
 public function testApproveTimestamp()
 {
     $t = new ModerationTestsuite();
     $entry = $t->getSampleEntry();
     $TEST_TIME_CHANGE = '6 hours';
     $ACCEPTABLE_DIFFERENCE = 300;
     # in seconds
     $ts = new MWTimestamp(time());
     $ts->timestamp->modify('-' . $TEST_TIME_CHANGE);
     $dbw = wfGetDB(DB_MASTER);
     $dbw->update('moderation', array('mod_timestamp' => $ts->getTimestamp(TS_MW)), array('mod_id' => $entry->id), __METHOD__);
     $rev = $this->tryToApprove($t, $entry);
     # Page history should mention the time when edit was made,
     # not when it was approved.
     $expected = $ts->getTimestamp(TS_ISO_8601);
     $this->assertEquals($expected, $rev['timestamp'], "testApproveTimestamp(): approved edit has incorrect timestamp in the page history");
     # RecentChanges should mention the time when the edit was
     # approved, so that it won't "appear in the past", confusing
     # those who read RecentChanges.
     $ret = $t->query(array('action' => 'query', 'list' => 'recentchanges', 'rcprop' => 'timestamp', 'rclimit' => 1, 'rcuser' => $t->lastEdit['User']));
     $rc_timestamp = $ret['query']['recentchanges'][0]['timestamp'];
     $this->assertNotEquals($expected, $rc_timestamp, "testApproveTimestamp(): approved edit has \"appeared in the past\" in the RecentChanges");
     # Does the time in RecentChanges match the time of approval?
     #
     # NOTE: we don't know the time of approval to the second, so
     # string comparison can't be used. Difference can be seconds
     # or even minutes (if system time is off).
     $ts->timestamp->modify('+' . $TEST_TIME_CHANGE);
     $expected = $ts->getTimestamp(TS_UNIX);
     $ts_actual = new MWTimestamp($rc_timestamp);
     $actual = $ts_actual->getTimestamp(TS_UNIX);
     $this->assertLessThan($ACCEPTABLE_DIFFERENCE, abs($expected - $actual), "testApproveTimestamp(): timestamp of approved edit in RecentChanges is too different from the time of approval");
 }