204 lines
8.8 KiB
PHP
204 lines
8.8 KiB
PHP
<?php
|
|
namespace Xxx\Tests\Unit;
|
|
|
|
use PHPUnit\Framework\Attributes\DataProvider;
|
|
use Xxx\Tests\Support\FakeRecaptcha;
|
|
use Xxx\Tests\Support\TestCase;
|
|
|
|
// validate_data() in publish/post.lib.php
|
|
class ValidateDataTest extends TestCase {
|
|
private function validate(array $post, bool $captcha = TRUE): array {
|
|
if ($captcha)
|
|
$_SESSION['captcha_ok'] = time();
|
|
$this->request(array(), $post + array('title' => 'Title', 'author' => 'Ann', 'story' => '<p>x</p>'));
|
|
$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' => '<b>big</b>.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' => '<b>notes</b>.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()));
|
|
}
|
|
}
|