- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
216 lines
6.5 KiB
PHP
216 lines
6.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Framework\Console\ExitCode;
|
|
use App\Framework\Filesystem\ValueObjects\FilePath;
|
|
use App\Framework\Logging\Logger;
|
|
use App\Framework\Process\SystemProcess;
|
|
use App\Framework\Process\ValueObjects\Command;
|
|
use App\Framework\Process\ValueObjects\EnvironmentVariables;
|
|
|
|
beforeEach(function () {
|
|
$this->logger = Mockery::mock(Logger::class);
|
|
$this->logger->shouldReceive('debug')->andReturn(null);
|
|
|
|
$this->process = new SystemProcess($this->logger);
|
|
});
|
|
|
|
afterEach(function () {
|
|
Mockery::close();
|
|
});
|
|
|
|
describe('Process Integration - Real World Scenarios', function () {
|
|
it('can execute multi-line script', function () {
|
|
$command = Command::fromString('
|
|
echo "line1"
|
|
echo "line2"
|
|
echo "line3"
|
|
');
|
|
|
|
$result = $this->process->run($command);
|
|
|
|
expect($result->isSuccess())->toBeTrue()
|
|
->and($result->stdout)->toContain('line1')
|
|
->and($result->stdout)->toContain('line2')
|
|
->and($result->stdout)->toContain('line3');
|
|
});
|
|
|
|
it('can create and read temporary file', function () {
|
|
$tempFile = FilePath::temp('process_test.txt');
|
|
$command = Command::fromString(
|
|
"echo 'test content' > {$tempFile->toString()} && cat {$tempFile->toString()}"
|
|
);
|
|
|
|
$result = $this->process->run($command);
|
|
|
|
expect($result->isSuccess())->toBeTrue()
|
|
->and($result->stdout)->toContain('test content');
|
|
|
|
// Cleanup
|
|
if ($tempFile->exists()) {
|
|
unlink($tempFile->toString());
|
|
}
|
|
});
|
|
|
|
it('can pipe commands', function () {
|
|
$command = Command::fromString('echo "hello world" | grep "world"');
|
|
|
|
$result = $this->process->run($command);
|
|
|
|
expect($result->isSuccess())->toBeTrue()
|
|
->and($result->stdout)->toContain('world');
|
|
});
|
|
|
|
it('handles complex environment variable substitution', function () {
|
|
$env = EnvironmentVariables::fromArray([
|
|
'PREFIX' => 'test',
|
|
'SUFFIX' => 'value',
|
|
]);
|
|
|
|
$command = Command::fromString('echo "$PREFIX-$SUFFIX"');
|
|
|
|
$result = $this->process->run(
|
|
command: $command,
|
|
env: $env
|
|
);
|
|
|
|
expect($result->stdout)->toContain('test-value');
|
|
});
|
|
|
|
it('can execute PHP script', function () {
|
|
$phpCode = 'echo json_encode(["status" => "ok", "pid" => getmypid()]);';
|
|
$command = Command::fromArray(['php', '-r', $phpCode]);
|
|
|
|
$result = $this->process->run($command);
|
|
|
|
expect($result->isSuccess())->toBeTrue();
|
|
|
|
$output = json_decode($result->stdout, true);
|
|
expect($output)->toBeArray()
|
|
->and($output['status'])->toBe('ok')
|
|
->and($output['pid'])->toBeGreaterThan(0);
|
|
});
|
|
|
|
it('handles large output', function () {
|
|
// Generate 1000 lines of output
|
|
$command = Command::fromString('for i in {1..1000}; do echo "Line $i"; done');
|
|
|
|
$result = $this->process->run($command);
|
|
|
|
expect($result->isSuccess())->toBeTrue();
|
|
|
|
$lines = explode("\n", trim($result->stdout));
|
|
expect(count($lines))->toBeGreaterThanOrEqual(1000);
|
|
});
|
|
|
|
it('preserves exit codes correctly', function () {
|
|
$testCases = [
|
|
['exit 0', ExitCode::SUCCESS],
|
|
['exit 1', ExitCode::GENERAL_ERROR],
|
|
['exit 2', ExitCode::USAGE_ERROR],
|
|
['exit 126', ExitCode::PERMISSION_DENIED],
|
|
];
|
|
|
|
foreach ($testCases as [$cmd, $expectedCode]) {
|
|
$result = $this->process->run(Command::fromString($cmd));
|
|
expect($result->exitCode)->toBe($expectedCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Process Integration - Error Handling', function () {
|
|
it('handles command not found', function () {
|
|
$result = $this->process->run(
|
|
Command::fromString('nonexistent_command_xyz_123')
|
|
);
|
|
|
|
expect($result->isFailed())->toBeTrue()
|
|
->and($result->hasErrors())->toBeTrue();
|
|
});
|
|
|
|
it('handles permission denied', function () {
|
|
// Try to write to root directory (should fail)
|
|
$command = Command::fromString('touch /root/test_file.txt');
|
|
|
|
$result = $this->process->run($command);
|
|
|
|
expect($result->isFailed())->toBeTrue();
|
|
});
|
|
|
|
it('handles syntax errors in shell commands', function () {
|
|
$command = Command::fromString('echo "unclosed string');
|
|
|
|
$result = $this->process->run($command);
|
|
|
|
expect($result->isFailed())->toBeTrue();
|
|
});
|
|
});
|
|
|
|
describe('Process Integration - Performance', function () {
|
|
it('can execute multiple processes sequentially', function () {
|
|
$startTime = microtime(true);
|
|
|
|
for ($i = 0; $i < 5; $i++) {
|
|
$result = $this->process->run(
|
|
Command::fromArray(['echo', "iteration {$i}"])
|
|
);
|
|
|
|
expect($result->isSuccess())->toBeTrue();
|
|
}
|
|
|
|
$totalTime = microtime(true) - $startTime;
|
|
|
|
// Should complete 5 simple commands in under 2 seconds
|
|
expect($totalTime)->toBeLessThan(2.0);
|
|
});
|
|
|
|
it('tracks runtime accurately', function () {
|
|
$sleepTime = 0.2; // 200ms
|
|
$result = $this->process->run(
|
|
Command::fromArray(['sleep', (string) $sleepTime])
|
|
);
|
|
|
|
$runtime = $result->runtime->toSeconds();
|
|
|
|
// Runtime should be approximately the sleep time (with some tolerance)
|
|
expect($runtime)->toBeGreaterThanOrEqual($sleepTime)
|
|
->and($runtime)->toBeLessThan($sleepTime + 0.1);
|
|
});
|
|
});
|
|
|
|
describe('Process Integration - Async Process Management', function () {
|
|
it('can manage multiple async processes', function () {
|
|
$processes = [];
|
|
|
|
// Start 3 processes
|
|
for ($i = 0; $i < 3; $i++) {
|
|
$processes[] = $this->process->start(
|
|
Command::fromArray(['sleep', '0.1'])
|
|
);
|
|
}
|
|
|
|
// All should be running
|
|
foreach ($processes as $proc) {
|
|
expect($proc->isRunning())->toBeTrue();
|
|
}
|
|
|
|
// Wait for all
|
|
foreach ($processes as $proc) {
|
|
$result = $proc->wait();
|
|
expect($result->isSuccess())->toBeTrue();
|
|
}
|
|
});
|
|
|
|
it('can capture output from async process', function () {
|
|
$running = $this->process->start(
|
|
Command::fromArray(['echo', 'async output'])
|
|
);
|
|
|
|
$result = $running->wait();
|
|
|
|
expect($result->stdout)->toContain('async output')
|
|
->and($result->isSuccess())->toBeTrue();
|
|
});
|
|
});
|