build: M1 Dockerized development stack with PHP skeleton, config/logging, Tailwind assets, and wrappers
Implements MeshVault M1 (issues #6–11): - Four-service Docker Compose stack (nginx, PHP-FPM 8.3, PostgreSQL 17, Node assets) - Slim 4 + PHP-DI routing with escaped PHP views and production/dev error handling - Immutable validated AppConfig with redacted diagnostics - JSON stdout Monolog logging with job-scoped context - Tailwind CSS v4 deterministic locked builds with template scanning - POSIX and PowerShell dev wrappers (up/down/test/lint/assets/watch) - Startup config validation fails before FPM serves as unprivileged www-data - migrate/seed explicitly unavailable until M2 (#12); #10 remains open Verified via clean-start acceptance on remote host (keith@192.168.2.23).
This commit is contained in:
1 parent
c2747a5762
commit
d3e8cf8fae
39 files changed
+5291
-38
No files matched your search
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MeshVault\Tests\Http;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class BootstrapTest extends TestCase
|
||||
{
|
||||
private function runPhp(string $file, string $mode, bool $valid): array
|
||||
{
|
||||
$env = array_merge(getenv(), [
|
||||
'MESHVAULT_ENV' => $mode,
|
||||
'MESHVAULT_LIBRARY_DIR' => sys_get_temp_dir(),
|
||||
'MESHVAULT_DB_HOST' => $valid ? 'db' : '',
|
||||
'MESHVAULT_DB_NAME' => 'meshvault',
|
||||
'MESHVAULT_DB_USER' => 'meshvault',
|
||||
'MESHVAULT_DB_PASSWORD' => 'bootstrap-secret',
|
||||
]);
|
||||
$code = 'register_shutdown_function(function () { fwrite(STDERR, "STATUS=" . http_response_code()); });'
|
||||
. 'require ' . var_export($file, true) . ';';
|
||||
$process = proc_open([PHP_BINARY, '-r', $code], [1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes, null, $env);
|
||||
$out = stream_get_contents($pipes[1]);
|
||||
$err = stream_get_contents($pipes[2]);
|
||||
fclose($pipes[1]);
|
||||
fclose($pipes[2]);
|
||||
return [proc_close($process), $out, $err];
|
||||
}
|
||||
|
||||
public function testProductionBootstrapFailureIsGeneric500(): void
|
||||
{
|
||||
[$exit, $out, $err] = $this->runPhp('public/index.php', 'production', false);
|
||||
self::assertStringContainsString('Internal Server Error', $out);
|
||||
self::assertStringContainsString('STATUS=500', $err);
|
||||
self::assertStringNotContainsString('MESHVAULT_DB_HOST', $out);
|
||||
self::assertStringNotContainsString('bootstrap-secret', $out . $err);
|
||||
self::assertStringContainsString('MESHVAULT_DB_HOST', $err);
|
||||
}
|
||||
|
||||
public function testDevelopmentBootstrapFailureIsReadable(): void
|
||||
{
|
||||
[$exit, $out, $err] = $this->runPhp('public/index.php', 'development', false);
|
||||
self::assertStringContainsString('MESHVAULT_DB_HOST', $out);
|
||||
self::assertStringContainsString('STATUS=500', $err);
|
||||
self::assertStringNotContainsString('bootstrap-secret', $out . $err);
|
||||
}
|
||||
|
||||
public function testConfigCliSucceedsOnlyForValidConfig(): void
|
||||
{
|
||||
self::assertFileExists('bin/check-config.php');
|
||||
[$exit, $out, $err] = $this->runPhp('bin/check-config.php', 'production', true);
|
||||
self::assertSame(0, $exit, $err);
|
||||
[$exit, $out, $err] = $this->runPhp('bin/check-config.php', 'production', false);
|
||||
self::assertSame(1, $exit);
|
||||
self::assertStringContainsString('MESHVAULT_DB_HOST', $err);
|
||||
self::assertStringNotContainsString('bootstrap-secret', $out . $err);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user