request(array(), $post + array('title' => 'Title', 'author' => 'Ann', 'story' => '
x
')); $this->assertTrue(validate_data()); return $GLOBALS['validate']; } public function testACompleteStoryPasses(): void { $this->assertSame(array(), $this->validate(array())); } public function testTitleAndAuthorAreNeeded(): void { $this->assertSame(array('author' => 'You have not submitted an author.', 'title' => 'You have not submitted a title.'), $this->validate(array('title' => '', 'author' => ''))); } public function testArraysCountAsEmpty(): void { $this->assertArrayHasKey('title', $this->validate(array('title' => array('x')))); } public function testRemovingTheImageNeedsText(): void { $this->assertSame(array('story' => "You've tried to remove an image without replacing it with text."), $this->validate(array('remove_img' => '1', 'story' => ''))); $GLOBALS['validate'] = array(); $this->assertSame(array(), $this->validate(array('remove_img' => '1'))); } public function testVisitorsNeedTheCaptcha(): void { FakeRecaptcha::install('{"tokenProperties":{"valid":false}}'); $this->assertSame(array('captcha' => 'Please complete the captcha.'), $this->validate(array('g-recaptcha-response' => 'r'), FALSE)); } public function testASolvedCaptchaIsRemembered(): void { FakeRecaptcha::install(); $this->assertSame(array(), $this->validate(array('g-recaptcha-response' => 'r'), FALSE)); $this->assertGreaterThan(time() - 5, $_SESSION['captcha_ok']); } public function testAnOldCaptchaPassDoesNotCount(): void { FakeRecaptcha::install('{"tokenProperties":{"valid":false}}'); $_SESSION['captcha_ok'] = time() - CAPTCHA_PASS_SECONDS - 1; $this->assertArrayHasKey('captcha', $this->validate(array(), FALSE)); } public function testEditorsNeedNoCaptcha(): void { FakeRecaptcha::install('{"tokenProperties":{"valid":false}}'); $this->asEditor(); $this->assertSame(array(), $this->validate(array(), FALSE)); $this->assertSame(array(), FakeRecaptcha::$requests); } public function testUploadErrorsAreReported(): void { $_FILES['upload'] = array('name' => 'a.png', 'tmp_name' => '', 'error' => UPLOAD_ERR_PARTIAL); $this->assertSame(array('upload' => 'There was an error uploading the file (3)'), $this->validate(array())); } public function testAFileThatPhpRefusedAsTooLargeIsReported(): void { foreach (array(UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE) as $error) { $_FILES['upload'] = array('name' => 'big.png', 'tmp_name' => '', 'error' => $error); $this->assertSame(array('upload' => 'big.png is larger than 1 MB. Please choose a smaller image.'), $this->validate(array())); } } // An image of $bytes bytes: a PNG with padding after its end private function imageOf(int $bytes): void { $file = $this->tmp . '/upload'; file_put_contents($file, str_pad(self::png(), $bytes, "\0")); $_FILES['upload'] = array('name' => 'big.png', 'tmp_name' => $file, 'error' => UPLOAD_ERR_OK); } public function testImagesCanBeOneMegabyte(): void { $this->imageOf(1024 * 1024); $this->assertSame(array(), $this->validate(array())); $this->imageOf(1024 * 1024 + 1); $this->assertSame(array('upload' => '<b>big</b>.png is larger than 1 MB. Please choose a smaller image.'), $this->validate(array())); } public function testTheImageLimitComesFromTheConfiguration(): void { $this->setEnv('UPLOAD_MAX_MB', '2.5'); $this->imageOf(2 * 1024 * 1024); $this->assertSame(array(), $this->validate(array())); $this->imageOf((int)(2.5 * 1024 * 1024) + 1); $this->assertSame(array('upload' => '<b>big</b>.png is larger than 2.5 MB. Please choose a smaller image.'), $this->validate(array())); } public function testNoneMeansNoImageLimit(): void { $this->setEnv('UPLOAD_MAX_MB', 'None'); $this->assertNull(upload_max_bytes()); $this->imageOf(3 * 1024 * 1024); $this->assertSame(array(), $this->validate(array())); $_FILES['upload'] = array('name' => 'big.png', 'tmp_name' => '', 'error' => UPLOAD_ERR_INI_SIZE); $this->assertSame(array('upload' => 'big.png is larger than the server accepts. Please choose a smaller image.'), $this->validate(array())); } public function testABadImageLimitMeansTheDefault(): void { foreach (array('', 'lots', '0', '-2') as $value) { $this->setEnv('UPLOAD_MAX_MB', $value); $this->assertSame(1024 * 1024, upload_max_bytes(), $value); } } public function testNoFileIsNotAnError(): void { $_FILES['upload'] = array('name' => '', 'tmp_name' => '', 'error' => UPLOAD_ERR_NO_FILE); $this->assertSame(array(), $this->validate(array())); } public function testTheUploadMustBeAnImage(): void { $file = $this->tmp . '/upload'; file_put_contents($file, 'This is not an image, but it is long enough to read.'); $_FILES['upload'] = array('name' => 'notes.txt', 'tmp_name' => $file, 'error' => UPLOAD_ERR_OK); $this->assertSame(array('upload' => "<b>notes</b>.txt wasn't an image file. Please go back."), $this->validate(array())); } public static function invalidUploadFields(): array { return array( 'array filename' => array('name', array('a.png')), 'array temporary file' => array('tmp_name', array('/tmp/not-an-upload')), 'array error code' => array('error', array(UPLOAD_ERR_OK)), 'numeric filename' => array('name', 123), 'boolean temporary file' => array('tmp_name', FALSE), 'string error code' => array('error', '0'), ); } #[DataProvider('invalidUploadFields')] public function testAnInvalidUploadFieldIsRejected(string $field, mixed $value): void { $this->imageOf(strlen(self::png())); $_FILES['upload'][$field] = $value; $this->assertArrayHasKey('upload', $this->validate(array())); $this->assertArrayNotHasKey('pending_upload', $_SESSION); } public function testANestedUploadIsRejected(): void { $_FILES['upload'] = array('name' => array('a.png'), 'tmp_name' => array('/tmp/not-an-upload'), 'error' => array(UPLOAD_ERR_OK), 'size' => array(1), 'type' => array('image/png')); $this->assertArrayHasKey('upload', $this->validate(array())); $this->assertArrayNotHasKey('pending_upload', $_SESSION); } public static function absentUploads(): array { return array( 'no upload entry' => array(array()), 'empty entry' => array(array('upload' => array())), 'no file chosen' => array(array('upload' => array('error' => UPLOAD_ERR_NO_FILE))), ); } #[DataProvider('absentUploads')] public function testAnAbsentUploadDoesNotBlockATextStory(array $files): void { $_FILES = $files; $this->assertSame(array(), $this->validate(array())); } public function testASuccessfulUploadNeedsATemporaryFilename(): void { $_FILES['upload'] = array('name' => 'a.png', 'error' => UPLOAD_ERR_OK); $this->assertArrayHasKey('upload', $this->validate(array())); } public function testAMissingTemporaryFileIsRejected(): void { $_FILES['upload'] = array('name' => 'a.png', 'tmp_name' => $this->tmp . '/missing', 'error' => UPLOAD_ERR_OK); $this->assertArrayHasKey('upload', $this->validate(array())); $this->assertArrayNotHasKey('pending_upload', $_SESSION); } public function testAScalarUploadEntryIsRejected(): void { $_FILES['upload'] = 'not a file entry'; $this->assertArrayHasKey('upload', $this->validate(array())); } public function testAnEmptyTemporaryFilenameIsRejected(): void { $_FILES['upload'] = array('name' => 'a.png', 'tmp_name' => '', 'error' => UPLOAD_ERR_OK); $this->assertArrayHasKey('upload', $this->validate(array())); } public function testADirectoryIsNotAnUpload(): void { $_FILES['upload'] = array('name' => 'a.png', 'tmp_name' => $this->tmp, 'error' => UPLOAD_ERR_OK); $this->assertArrayHasKey('upload', $this->validate(array())); } public function testAnEmptyImageFileIsRejectedWithoutWarnings(): void { $file = $this->tmp . '/empty'; file_put_contents($file, ''); $_FILES['upload'] = array('name' => 'a.png', 'tmp_name' => $file, 'error' => UPLOAD_ERR_OK); $this->assertArrayHasKey('upload', $this->validate(array())); } public function testAnImageUploadPasses(): void { $file = $this->tmp . '/upload'; file_put_contents($file, self::png()); $_FILES['upload'] = array('name' => 'a.png', 'tmp_name' => $file, 'error' => UPLOAD_ERR_OK); $this->assertSame(array(), $this->validate(array())); } }