0, 1, NULL) AS img'; protected function setUp(): void { parent::setUp(); SiteData::install(array( 'xxx' => array( array('id' => 1, 'title' => 'Apple', 'author' => 'Zed', 'date' => '2020-01-01 10:00:00'), array('id' => 2, 'title' => 'Banana', 'author' => 'Yan', 'date' => '2020-02-01 10:00:00', 'story' => '
A cherry story.
'), array('id' => 3, 'title' => 'Cherry', 'author' => 'Xi', 'date' => '2020-03-01 10:00:00', 'hidden' => '1'), ), 'queue' => array( array('id' => 10, 'title' => 'Queued', 'date' => '2020-04-01 10:00:00'), ), )); open_db('open'); } private function lastQuery(): array { return end(FakeDb::$log); } public function testVisitorsDoNotCountHiddenStories(): void { $this->assertSame('2', get_count()); $this->assertSame('SELECT COUNT(*) FROM xxx WHERE 1 AND hidden IS NOT TRUE', $this->lastQuery()['sql']); } public function testEditorsCountHiddenStories(): void { $this->asEditor(); $this->assertSame('3', get_count()); $this->assertSame('SELECT COUNT(*) FROM xxx WHERE 1', $this->lastQuery()['sql']); } public function testEditorsCountTheQueue(): void { $this->asEditor(); $this->request(array('queue' => '1')); $this->assertSame('1', get_count()); } public function testVisitorsCannotCountTheQueue(): void { $this->request(array('queue' => '1')); $this->assertSame('2', get_count()); $this->assertStringContainsString('FROM xxx', $this->lastQuery()['sql']); } public function testSearchCountBindsTheTermFourTimes(): void { $this->request(array('search' => "cherry' OR '1'='1")); get_count(); $query = $this->lastQuery(); $this->assertSame("SELECT COUNT(*) FROM xxx WHERE (title LIKE ? OR author LIKE ? OR date LIKE ? OR story LIKE ?) AND hidden IS NOT TRUE", $query['sql']); $this->assertSame(array_fill(0, 4, "%cherry' OR '1'='1%"), $query['params']); } public function testSearchCountFindsMatches(): void { $this->request(array('search' => 'cherry')); $this->assertSame('1', get_count()); // Banana's story; Cherry is hidden } public function testDefaultListIsNewestFirstWithoutHiddenStories(): void { $GLOBALS['num_stories'] = 5; $rows = get_query(); $this->assertSame(array('2', '1'), array_column($rows, 'id')); $this->assertSame('SELECT ' . self::COLUMNS . ' FROM xxx WHERE 1 AND hidden IS NOT TRUE ORDER BY date desc LIMIT 0 , 5', $this->lastQuery()['sql']); $this->assertSame(array(0, 0), array_column($rows, 'queue_url')); } public function testEditorsSeeHiddenStories(): void { $this->asEditor(); $GLOBALS['num_stories'] = 5; $this->assertSame(array('3', '2', '1'), array_column(get_query(), 'id')); } public function testSortOrderStartAndShow(): void { $this->request(array('sort' => 'title', 'order' => 'asc', 'start' => '1', 'show' => '1')); $rows = get_query(); $this->assertSame('SELECT ' . self::COLUMNS . ' FROM xxx WHERE 1 AND hidden IS NOT TRUE ORDER BY title asc LIMIT 1 , 1', $this->lastQuery()['sql']); $this->assertSame(array('Banana'), array_column($rows, 'title')); } public function testBadSortAndOrderValuesFallBack(): void { $this->request(array('sort' => 'id; DROP TABLE xxx', 'order' => 'desc; --', 'start' => 'x', 'show' => 'y')); get_query(); $this->assertSame('SELECT ' . self::COLUMNS . ' FROM xxx WHERE 1 AND hidden IS NOT TRUE ORDER BY date desc LIMIT 0 , 1', $this->lastQuery()['sql']); } public function testOneStoryById(): void { $rows = get_query('2'); $this->assertSame('SELECT ' . self::COLUMNS . ' FROM xxx WHERE 1 AND hidden IS NOT TRUE AND id = 2 LIMIT 0, 1', $this->lastQuery()['sql']); $this->assertSame(array('Banana'), array_column($rows, 'title')); } public function testVisitorsCannotOpenAHiddenStory(): void { $this->assertSame(array(), get_query('3')); } public function testEditorsCanOpenAHiddenStory(): void { $this->asEditor(); $this->assertSame(array('Cherry'), array_column(get_query('3'), 'title')); $this->assertSame('SELECT ' . self::COLUMNS . ' FROM xxx WHERE 1 AND id = 3 LIMIT 0, 1', $this->lastQuery()['sql']); } public function testIdIsCastToAnInteger(): void { get_query('2.9'); $this->assertStringContainsString('AND id = 2 LIMIT', $this->lastQuery()['sql']); } public function testNonNumericIdListsStories(): void { get_query('2 OR 1=1'); $this->assertStringNotContainsString('OR 1=1', $this->lastQuery()['sql']); $this->assertStringContainsString('ORDER BY', $this->lastQuery()['sql']); } public function testSearchListBindsTheTerm(): void { $this->request(array('search' => '%_')); get_query(); $query = $this->lastQuery(); $this->assertStringStartsWith('SELECT ' . self::COLUMNS . ' FROM xxx WHERE (title LIKE ? OR author LIKE ? OR date LIKE ? OR story LIKE ?) AND hidden IS NOT TRUE ORDER BY', $query['sql']); $this->assertSame(array_fill(0, 4, '%%_%'), $query['params']); } // A page of 50 stories would otherwise read 50 images, only to see which stories have one public function testImageDataStaysInTheDatabase(): void { SiteData::install(array('xxx' => array( array('id' => 1, 'story' => '', 'img' => str_repeat('P', 1000), 'mime' => 'image/png'), array('id' => 2), ))); $GLOBALS['num_stories'] = 5; $rows = array_column(get_query(), NULL, 'id'); $this->assertSame(1, $rows['1']['img']); $this->assertNull($rows['2']['img']); $this->assertSame(1, get_query('1')[0]['img']); } public function testQueueRowsAreMarked(): void { $this->asEditor(); $this->request(array('queue' => '1')); $GLOBALS['num_stories'] = 5; $rows = get_query(); $this->assertSame(array('10'), array_column($rows, 'id')); $this->assertSame(array(1), array_column($rows, 'queue_url')); } }