setTables(array( 'xxx' => array( array('id' => 1, 'title' => 'Tom & "Jerry"', 'author' => 'Ann ', 'date' => '2021-01-03 10:00:00', 'story' => "

Wait… a bold\x01 move & more.

"), array('id' => 2, 'title' => 'Picture', 'date' => '2021-01-02 10:00:00', 'story' => '', 'img' => TestCase::png(), 'mime' => 'text/html'), array('id' => 3, 'title' => 'Secret', 'date' => '2021-01-01 10:00:00', 'img' => TestCase::png(), 'mime' => 'image/png', 'hidden' => '1'), array('id' => 4, 'title' => 'Not an image', 'img' => '', 'mime' => 'image/png'), ), 'queue' => array( array('id' => 9, 'title' => 'Waiting', 'img' => TestCase::png(), 'mime' => 'image/png'), ), )); } public function testTheFeedIsValidRss(): void { $feed = $this->get('/feed'); $this->assertSame('application/rss+xml', $this->header($feed, 'Content-Type')); $xml = simplexml_load_string($feed['body']); $this->assertNotFalse($xml, $feed['body']); $items = $xml->channel->item; // The front page, then the visible stories, newest first $this->assertSame(array('Front Page', 'Tom & "Jerry"', 'Picture', 'Not an image'), array_map('strval', iterator_to_array($xml->xpath('/rss/channel/item/title'), FALSE))); // The addresses come from SITE_URL $this->assertSame('https://site.test/', (string)$xml->channel->link); $this->assertSame('https://site.test/article?id=1', (string)$items[1]->link); $this->assertSame('editor@site.test (Editors)', (string)$xml->channel->managingEditor); $this->assertStringContainsString('edi', $feed['body']); $this->assertSame('Wait… a bold move & more.', (string)$items[1]->description); $this->assertSame('Ann ', (string)$items[1]->children('http://purl.org/dc/elements/1.1/')->creator); $this->assertSame('Image: Picture', (string)$items[2]->description); $this->assertSame(date('D, d M Y H:i:s O', strtotime('2021-01-03 10:00:00')), (string)$xml->channel->lastBuildDate); } // Without stories, the feed dates are the time of the request public function testTheFeedOfAnEmptySite(): void { $this->setTables(array()); $xml = simplexml_load_string($this->get('/feed')['body']); $this->assertNotFalse($xml); $now = (string)$xml->channel->pubDate; $this->assertSame($now, (string)$xml->channel->lastBuildDate); $this->assertSame($now, (string)$xml->channel->item[0]->pubDate); $this->assertCount(1, $xml->channel->item); } public function testAnImageIsServedWithItsRealType(): void { $image = $this->get('/display?id=2'); $this->assertSame(200, $image['status']); $this->assertSame(TestCase::png(), $image['body']); $this->assertSame('image/png', $this->header($image, 'Content-Type')); $this->assertSame('nosniff', $this->header($image, 'X-Content-Type-Options')); $this->assertSame((string)strlen(TestCase::png()), $this->header($image, 'Content-Length')); } public function testDataThatIsNotAnImageIsNotServed(): void { $image = $this->get('/display?id=4'); $this->assertSame(404, $image['status']); $this->assertStringNotContainsString('script', $image['body']); } public function testAMissingImageIsNotFound(): void { $this->assertSame(404, $this->get('/display?id=99')['status']); $this->assertSame(404, $this->get('/display?id=1')['status']); } public function testHiddenImagesAreForEditorsOnly(): void { $this->assertSame(404, $this->get('/display?id=3')['status']); $this->loginAs(); $this->assertSame(200, $this->get('/display?id=3')['status']); } public function testARemovedEditorSeesNoHiddenImages(): void { $this->loginAs(); $this->setTables(array('xxx' => $this->tables()['xxx'])); $this->assertSame(404, $this->get('/display?id=3')['status']); } public function testQueuedImagesNeedPermission(): void { $this->assertSame(401, $this->get('/display?id=9&queue=1')['status']); $this->startSession(array('allow_queue_image' => '8')); $this->assertSame(401, $this->get('/display?id=9&queue=1')['status']); $this->startSession(array('allow_queue_image' => '9')); $this->assertSame(200, $this->get('/display?id=9&queue=1')['status']); $this->loginAs(); $this->assertSame(200, $this->get('/display?id=9&queue=1')['status']); } public function testRestrictedImagesAreNotCached(): void { $this->assertNull($this->header($this->get('/display?id=2'), 'Cache-Control')); $this->loginAs(); $this->assertSame('private, no-store', $this->header($this->get('/display?id=3'), 'Cache-Control')); $this->assertSame('private, no-store', $this->header($this->get('/display?id=9&queue=1'), 'Cache-Control')); } public function testThePendingUploadIsShownToItsSession(): void { $name = bin2hex(random_bytes(16)); mkdir(self::$state . '/tmp/uploads', 0700); file_put_contents(self::$state . "/tmp/uploads/$name", TestCase::png()); $this->assertSame(404, $this->get('/display?pending=x')['status']); $this->startSession(array('pending_upload' => $name)); $image = $this->get('/display?pending=' . substr($name, 0, 8)); $this->assertSame(200, $image['status']); $this->assertSame(TestCase::png(), $image['body']); $this->assertSame('private, no-store', $this->header($image, 'Cache-Control')); } public function testAPendingUploadMustBeAnImage(): void { $name = bin2hex(random_bytes(16)); mkdir(self::$state . '/tmp/uploads', 0700); file_put_contents(self::$state . "/tmp/uploads/$name", 'This is not an image at all.'); $this->startSession(array('pending_upload' => $name)); $this->assertSame(404, $this->get('/display?pending=x')['status']); } }