152 lines
7.0 KiB
PHP
152 lines
7.0 KiB
PHP
<?php
|
|
namespace Xxx\Tests\Unit;
|
|
|
|
use Xxx\Tests\Support\FakeDb;
|
|
use Xxx\Tests\Support\Mail;
|
|
use Xxx\Tests\Support\SiteData;
|
|
use Xxx\Tests\Support\TestCase;
|
|
|
|
// save_data() in publish/post.lib.php
|
|
class SaveDataTest extends TestCase {
|
|
private const TABLES = array(
|
|
'xxx' => array(array('id' => 1, 'title' => 'Live', 'date' => '2020-01-01 10:00:00', 'story' => '<p>Old.</p>')),
|
|
'queue' => array(array('id' => 5, 'title' => 'Waiting', 'date' => '2020-03-01 10:00:00', 'story' => '',
|
|
'img' => 'QUEUED IMAGE', 'mime' => 'image/gif')),
|
|
);
|
|
|
|
protected function setUp(): void {
|
|
parent::setUp();
|
|
SiteData::install(self::TABLES);
|
|
open_db('open');
|
|
}
|
|
|
|
// Save as an editor, with $action and the POST fields given
|
|
private function save(string $action, array $post, array $get = array()): string {
|
|
$this->asEditor();
|
|
$GLOBALS['action'] = $action;
|
|
$this->request($get, $post + array('title' => 'Title', 'author' => 'Ann', 'story' => '<p>Text.</p>'));
|
|
return (string)save_data();
|
|
}
|
|
|
|
private function pendingImage(): string {
|
|
$name = bin2hex(random_bytes(16));
|
|
file_put_contents(upload_dir() . "/$name", self::png());
|
|
$_SESSION['pending_upload'] = $name;
|
|
return upload_dir() . "/$name";
|
|
}
|
|
|
|
public function testEditorSavesANewStoryToTheQueue(): void {
|
|
$id = $this->save('save', array('email' => 'ann@example.org', 'comments' => 'Note', 'editor' => 'ed',
|
|
'hidden' => '1', 'date' => '2021-05-06 07:08:09'));
|
|
$this->assertSame('6', $id);
|
|
$row = SiteData::find('queue', 6);
|
|
$this->assertSame(array('Title', 'Ann', 'ann@example.org', '<p>Text.</p>', 'Note', 'ed', 1, '2021-05-06 07:08:09'),
|
|
array($row['title'], $row['author'], $row['email'], $row['story'], $row['comments'], $row['editor'], $row['hidden'], $row['date']));
|
|
$this->assertCount(1, SiteData::$tables['xxx']);
|
|
}
|
|
|
|
public function testANewStoryGetsTheDateNow(): void {
|
|
$this->save('save', array());
|
|
$date = SiteData::find('queue', 6)['date'];
|
|
$this->assertEqualsWithDelta(time(), strtotime($date . ' UTC'), 5);
|
|
}
|
|
|
|
public function testEditorUpdatesAQueuedStoryInPlace(): void {
|
|
$id = $this->save('save', array('id' => '5', 'title' => 'Changed', 'date' => '2030-01-01 00:00:00'));
|
|
$this->assertSame('5', $id);
|
|
$this->assertSame('Changed', SiteData::find('queue', 5)['title']);
|
|
// An update keeps the date
|
|
$this->assertSame('2020-03-01 10:00:00', SiteData::find('queue', 5)['date']);
|
|
$update = FakeDb::matching('/^UPDATE queue/')[0];
|
|
$this->assertStringEndsWith('WHERE `id` = ? LIMIT 1', $update['sql']);
|
|
$this->assertSame(5, end($update['params']));
|
|
}
|
|
|
|
public function testEditorUpdatesALiveStory(): void {
|
|
$this->assertSame('1', $this->save('post', array('id' => '1', 'title' => 'Edited')));
|
|
$this->assertSame('Edited', SiteData::find('xxx', 1)['title']);
|
|
$this->assertCount(1, SiteData::$tables['xxx']);
|
|
}
|
|
|
|
public function testPostingFromTheQueueMovesTheStory(): void {
|
|
$id = $this->save('post', array('id' => '5', 'title' => 'Now live', 'date' => '2021-01-01 00:00:00'), array('queue' => '1'));
|
|
$this->assertSame('6', $id);
|
|
$row = SiteData::find('xxx', 6);
|
|
$this->assertSame(array('Now live', '2021-01-01 00:00:00', '<p>Text.</p>', NULL), array($row['title'], $row['date'], $row['story'], $row['img']));
|
|
$this->assertNull(SiteData::find('queue', 5));
|
|
$this->assertFalse($GLOBALS['queue']);
|
|
}
|
|
|
|
public function testPostingFromTheQueueCanKeepTheQueuedImage(): void {
|
|
$this->save('post', array('id' => '5', 'uploaded' => '5'), array('queue' => '1'));
|
|
$row = SiteData::find('xxx', 6);
|
|
$this->assertSame(array('QUEUED IMAGE', 'image/gif', ''), array($row['img'], $row['mime'], $row['story']));
|
|
}
|
|
|
|
public function testAPendingUploadBecomesTheImage(): void {
|
|
$file = $this->pendingImage();
|
|
$this->save('save', array('uploaded' => 'pending'));
|
|
$row = SiteData::find('queue', 6);
|
|
$this->assertSame(array(self::png(), 'image/png', ''), array($row['img'], $row['mime'], $row['story']));
|
|
$this->assertFileDoesNotExist($file);
|
|
$this->assertArrayNotHasKey('pending_upload', $_SESSION);
|
|
}
|
|
|
|
public function testRemovingTheImageKeepsTheText(): void {
|
|
$this->save('save', array('id' => '5', 'remove_img' => '1', 'story' => '<p>Words.</p>'));
|
|
$row = SiteData::find('queue', 5);
|
|
$this->assertSame(array(NULL, NULL, '<p>Words.</p>'), array($row['img'], $row['mime'], $row['story']));
|
|
}
|
|
|
|
public function testTheStoryIsSanitised(): void {
|
|
$this->save('save', array('story' => '<p onclick="x()">a -- b<script>alert(1)</script></p>'));
|
|
$this->assertSame('<p>a — b</p>', SiteData::find('queue', 6)['story']);
|
|
}
|
|
|
|
public function testValuesAreBoundAsParameters(): void {
|
|
$this->save('save', array('title' => "O'Brien\"; DROP TABLE xxx; --"));
|
|
$insert = FakeDb::matching('/^INSERT INTO queue/')[0];
|
|
$this->assertStringNotContainsString('DROP', $insert['sql']);
|
|
$this->assertContains("O'Brien\"; DROP TABLE xxx; --", $insert['params']);
|
|
}
|
|
|
|
// Visitors always add to the queue, and each story sends mail
|
|
public function testVisitorsCanOnlyAddToTheQueue(): void {
|
|
$state = $this->tmp . '/state';
|
|
file_put_contents($state, serialize(array(
|
|
'tables' => self::TABLES,
|
|
'post' => array('id' => '1', 'title' => 'Mine', 'author' => 'Vi', 'story' => '<p>Hi.</p>', 'comments' => 'sneaky',
|
|
'editor' => 'ed', 'hidden' => '1', 'date' => '1999-01-01 00:00:00'),
|
|
'session' => array('captcha_ok' => time()),
|
|
)));
|
|
$file = var_export($state, TRUE);
|
|
$code = <<<PHP
|
|
\$state = unserialize(file_get_contents($file));
|
|
Xxx\Tests\Support\SiteData::install(\$state['tables']);
|
|
open_db('open');
|
|
\$action = 'post';
|
|
\$_POST = \$_REQUEST = \$state['post'];
|
|
\$_SESSION = \$state['session'];
|
|
\$id = save_data();
|
|
file_put_contents($file, serialize(array('id' => \$id, 'tables' => Xxx\Tests\Support\SiteData::\$tables, 'session' => \$_SESSION, 'action' => \$action)));
|
|
PHP;
|
|
$result = Mail::run($code, array('UPLOAD_DIR' => $this->tmp . '/uploads'));
|
|
$this->assertSame(0, $result['status'], $result['stderr']);
|
|
$this->assertSame('', $result['log']);
|
|
$saved = unserialize(file_get_contents($state));
|
|
|
|
$this->assertSame('6', (string)$saved['id']);
|
|
$this->assertSame('submit', $saved['action']);
|
|
$this->assertSame(self::TABLES['xxx'][0]['title'], $saved['tables']['xxx'][0]['title']);
|
|
$row = end($saved['tables']['queue']);
|
|
$this->assertSame(array('6', 'Mine', 'original author', 0, '', '<p>Hi.</p>'),
|
|
array($row['id'], $row['title'], $row['editor'], $row['hidden'], $row['comments'], $row['story']));
|
|
$this->assertNotSame('1999-01-01 00:00:00', $row['date']);
|
|
$this->assertSame(array('allow_queue_image' => '6'), $saved['session']);
|
|
|
|
$this->assertCount(1, $result['mails']);
|
|
$this->assertSame('New submission: Mine', $result['mails'][0]['subject']);
|
|
$this->assertStringContainsString('Vi has sent the following new submission', $result['mails'][0]['body']);
|
|
}
|
|
}
|