Files
tim ed959888b4 Add PHP unit tests and page tests.
Run "composer install", then "composer test".
2026-09-26 03:48:09 +02:00

110 lines
5.0 KiB
PHP

<?php
namespace Xxx\Tests\Unit;
use Xxx\Tests\Support\PhpProcess;
use Xxx\Tests\Support\TestCase;
/*
* config() in config.lib.php. It reads the .env file once per request,
* and config.lib.php calls it as soon as it loads, so each test runs
* config.lib.php in a new PHP process.
*/
class ConfigTest extends TestCase {
/*
* Load config.lib.php with $content as the .env file and return
* config($name, $default) for each array($name, $default) in $calls.
*/
private function config(string $content, array $calls, array $env = array(), string $between = ''): array {
file_put_contents($this->tmp . '/env', $content);
$code = 'require ' . var_export(XXX_ROOT . '/config.lib.php', TRUE) . ";\n\$out = array();\n";
foreach ($calls as $i => $call) {
if ($i === 1)
$code .= $between;
$code .= '$out[] = config(' . var_export($call[0], TRUE) . ', ' . var_export($call[1] ?? NULL, TRUE) . ");\n";
}
$code .= 'echo json_encode($out);';
$result = PhpProcess::runPlain($code, $env + array('XXX_ENV_FILE' => $this->tmp . '/env'));
$this->assertSame('', $result['stderr']);
return json_decode($result['stdout'], TRUE);
}
public function testReadsNameValueLines(): void {
$this->assertSame(array('example.org', 'stories'),
$this->config("DB_HOST=example.org\nDB_NAME=stories\n", array(array('DB_HOST'), array('DB_NAME'))));
}
public function testSkipsCommentsBlankLinesAndLinesWithoutEquals(): void {
$this->assertSame(array(NULL, NULL, 'real'),
$this->config("# DB_HOST=commented\n\n \nnot a setting\nDB_USER=real\n",
array(array('DB_HOST'), array('not a setting'), array('DB_USER'))));
}
public function testRemovesMatchingQuotesAndSpaces(): void {
$this->assertSame(array('double quoted', 'single quoted', "\"unbalanced'", '', '"x'),
$this->config("A = \"double quoted\" \nB='single quoted'\nC=\"unbalanced'\nD=\"\"\nE=\"x\n",
array(array('A'), array('B'), array('C'), array('D'), array('E'))));
}
public function testKeepsEqualsSignsInValues(): void {
$this->assertSame(array('a=b==c'), $this->config("DB_PASS=a=b==c\n", array(array('DB_PASS'))));
}
public function testAcceptsExportPrefix(): void {
$this->assertSame(array('exported'), $this->config("export DB_HOST=exported\n", array(array('DB_HOST'))));
}
public function testTheLastOfTwoLinesWins(): void {
$this->assertSame(array('second'), $this->config("KEY=first\nKEY=second\n", array(array('KEY'))));
}
public function testEnvironmentBeatsTheFile(): void {
$this->assertSame(array('from-environment'),
$this->config("DB_HOST=from-file\n", array(array('DB_HOST')), array('DB_HOST' => 'from-environment')));
}
public function testDefaultWhenMissing(): void {
$this->assertSame(array(NULL, 'fallback'),
$this->config("OTHER=1\n", array(array('MISSING'), array('MISSING', 'fallback'))));
}
public function testEmptyValueIsNotTheDefault(): void {
$this->assertSame(array(''), $this->config("EMPTY=\n", array(array('EMPTY', 'fallback'))));
}
public function testMissingFileGivesDefaults(): void {
$result = PhpProcess::runPlain('require ' . var_export(XXX_ROOT . '/config.lib.php', TRUE) . '; echo config("DB_HOST", "fallback");',
array('XXX_ENV_FILE' => $this->tmp . '/does-not-exist'));
$this->assertSame('fallback', $result['stdout']);
}
public function testFileIsReadOnlyOnce(): void {
$this->assertSame(array('before', 'before'),
$this->config("KEY=before\n", array(array('KEY'), array('KEY')), array(),
'file_put_contents(getenv("XXX_ENV_FILE"), "KEY=after\n");'));
}
public function testDisplayErrorsIsOffUnlessConfigured(): void {
$code = 'require ' . var_export(XXX_ROOT . '/config.lib.php', TRUE) . '; echo ini_get("display_errors"), "|", ini_get("log_errors");';
file_put_contents($this->tmp . '/env', "DISPLAY_ERRORS=\n");
$this->assertSame('|1', PhpProcess::runPlain($code, array('XXX_ENV_FILE' => $this->tmp . '/env'))['stdout']);
file_put_contents($this->tmp . '/env', "DISPLAY_ERRORS=stderr\n");
$this->assertSame('stderr|1', PhpProcess::runPlain($code, array('XXX_ENV_FILE' => $this->tmp . '/env'))['stdout']);
file_put_contents($this->tmp . '/env', "OTHER=1\n");
$this->assertSame('0|1', PhpProcess::runPlain($code, array('XXX_ENV_FILE' => $this->tmp . '/env'))['stdout']);
}
public function testTheTestsUseTheTestFile(): void {
$this->assertSame('test-site-key', config('RECAPTCHA_SITE_KEY'));
}
public function testExampleFileListsEverySettingTheCodeReads(): void {
$code = implode('', array_map('file_get_contents', array_merge(
glob(XXX_ROOT . '/*.php'), glob(XXX_ROOT . '/lib/*.php'), glob(XXX_ROOT . '/publish/*.php'))));
preg_match_all("/config\\('([A-Z_]+)'/", $code, $used);
$example = file_get_contents(XXX_ROOT . '/.env.example');
$this->assertNotEmpty($used[1]);
foreach (array_unique($used[1]) as $name)
$this->assertMatchesRegularExpression("/^$name=/m", $example, "$name is missing from .env.example");
}
}