Add PHP unit tests and page tests.
Run "composer install", then "composer test".
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
<?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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user