Add PHP unit tests and page tests.

Run "composer install", then "composer test".
This commit is contained in:
tim
2026-09-26 03:48:09 +02:00
parent 848c447c5b
commit ed959888b4
42 changed files with 5522 additions and 0 deletions
+151
View File
@@ -0,0 +1,151 @@
<?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']);
}
}