Browse Source

Add (incomplete) tests for static.php and installer.php

allow-textarea-resize
Aleksander Machniak 4 months ago
parent
commit
5fab389625
  1. 3
      composer.json
  2. 4
      public_html/static.php
  3. 24
      tests/Public/InstallerTest.php
  4. 90
      tests/Public/StaticTest.php
  5. 49
      tests/ServerTestCase.php

3
composer.json

@ -60,7 +60,8 @@
"roundcube/vcard_attachments": "*",
"roundcube/virtuser_file": "*",
"roundcube/virtuser_query": "*",
"roundcube/zipdownload": "*"
"roundcube/zipdownload": "*",
"symfony/process": "^7.0"
},
"suggest": {
"bjeavons/zxcvbn-php": "^1.0 required for Zxcvbn password strength driver",

4
public_html/static.php

@ -98,7 +98,7 @@ function validateStaticFile(string $path): ?string
$found = false;
foreach (ALLOWED_PATHS as $prefix) {
if (strpos($path, $prefix) === 0 && !preg_match('~skins/.+/templates/~', $path)) {
if (str_starts_with($path, $prefix) && !preg_match('~skins/.+/templates/~', $path)) {
$found = true;
break;
}
@ -138,7 +138,7 @@ function serveStaticFile($path): void
if (isset($_SERVER['HTTP_RANGE'])) {
// $valid = preg_match('^bytes=\d*-\d*(,\d*-\d*)*$', $_SERVER['HTTP_RANGE']);
if (substr($_SERVER['HTTP_RANGE'], 0, 6) != 'bytes=') {
if (!str_starts_with($_SERVER['HTTP_RANGE'], 'bytes=')) {
http_response_code(416); // "Range Not Satisfiable"
header('Content-Range: bytes */' . $size); // Required in 416.
return;

24
tests/Public/InstallerTest.php

@ -0,0 +1,24 @@
<?php
namespace Roundcube\Tests\Public;
use Roundcube\Tests\ServerTestCase;
/**
* Test class to test installer.php
*/
class InstallerTest extends ServerTestCase
{
/**
* Test installer.php
*/
public function testInstaller(): void
{
$response = $this->request('GET', 'installer.php/');
$body = (string) $response->getBody();
$this->assertSame(200, $response->getStatusCode());
$this->assertTrue(str_starts_with($body, '<!DOCTYPE html>'));
}
}

90
tests/Public/StaticTest.php

@ -0,0 +1,90 @@
<?php
namespace Roundcube\Tests\Public;
use PHPUnit\Framework\Attributes\DataProvider;
use Roundcube\Tests\ServerTestCase;
/**
* Test class to test static resources server
*/
class StaticTest extends ServerTestCase
{
/**
* Dataset for testExistingResources()
*/
public static function provideExistingResources(): iterable
{
return [
['program/resources/blank.gif', 'image/gif'],
['skins/elastic/images/logo.svg?s=1234567', 'image/svg+xml'],
['plugins/acl/acl.js', 'text/javascript'],
];
}
/**
* Test valid resources
*/
#[DataProvider('provideExistingResources')]
public function testExistingResources($path, $ctype): void
{
$response = $this->request('GET', 'static.php/' . $path);
$file = file_get_contents(INSTALL_PATH . preg_replace('/\?.*$/', '', $path));
$this->assertSame(200, $response->getStatusCode());
$this->assertSame($file, (string) $response->getBody());
$this->assertSame(['public, max-age=604800'], $response->getHeader('Cache-Control'));
$this->assertSame(['bytes'], $response->getHeader('Accept-Ranges'));
$this->assertSame([(string) strlen($file)], $response->getHeader('Content-Length'));
$this->assertStringContainsString($ctype, $response->getHeader('Content-Type')[0]);
// TODO: Expires header
}
/**
* Dataset for testForbiddenResources()
*/
public static function provideForbiddenResources(): iterable
{
return [
[''],
['CHANGELOG.md'],
['LICENSE'],
['skins/../passwd'],
['skins/elastic/templates/about.html'],
['plugins/acl/composer.json'],
['program/include/iniset.php'],
['program/localization/index.inc'],
['public_html/.htaccess'],
['vendor/friendsofphp/php-cs-fixer/logo.png'],
];
}
/**
* Test forbidden resources
*/
#[DataProvider('provideForbiddenResources')]
public function testForbiddenResources($path): void
{
$response = $this->request('GET', 'static.php/' . $path);
$this->assertSame(404, $response->getStatusCode());
$this->assertSame('', (string) $response->getBody());
}
/**
* Test handling of Modified-Since header
*/
public function testModifiedSinceHeader(): void
{
$this->markTestIncomplete();
}
/**
* Test handling of Range header
*/
public function testRangeHeader(): void
{
$this->markTestIncomplete();
}
}

49
tests/ServerTestCase.php

@ -0,0 +1,49 @@
<?php
namespace Roundcube\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Process\Process;
/**
* Test class to test HTTP requests to Roundcube
*/
class ServerTestCase extends TestCase
{
protected static $phpProcess;
#[\Override]
public static function setUpBeforeClass(): void
{
$path = realpath(__DIR__ . '/../public_html');
$cmd = ['php', '-S', 'localhost:8000', '-t', $path];
$env = [];
static::$phpProcess = new Process($cmd, null, $env);
static::$phpProcess->setWorkingDirectory($path);
static::$phpProcess->start();
usleep(50 * 1000); // give the server some time before we start testing
}
#[\Override]
public static function tearDownAfterClass(): void
{
static::$phpProcess->stop();
}
/**
* HTTP client request
*/
protected function request($method, $path, $options = [])
{
$config = [
'base_uri' => 'http://localhost:8000',
'http_errors' => false, // no exceptions for HTTP error codes
'handler' => null, // reset Mock state from other tests
];
$client = \rcmail::get_instance()->get_http_client($config);
return $client->request($method, $path, $options);
}
}
Loading…
Cancel
Save