feat: CI/CD pipeline setup complete - Ansible playbooks updated, secrets configured, workflow ready

This commit is contained in:
2025-10-31 01:39:24 +01:00
parent 55c04e4fd0
commit e26eb2aa12
601 changed files with 44184 additions and 32477 deletions

View File

@@ -0,0 +1,496 @@
<?php
declare(strict_types=1);
use App\Framework\Config\EnvFileParser;
use App\Framework\Filesystem\ValueObjects\FilePath;
describe('EnvFileParser', function () {
beforeEach(function () {
$this->parser = new EnvFileParser();
});
describe('parseString()', function () {
it('parses basic key=value pairs', function () {
$content = <<<ENV
KEY1=value1
KEY2=value2
KEY3=value3
ENV;
$result = $this->parser->parseString($content);
expect($result)->toBe([
'KEY1' => 'value1',
'KEY2' => 'value2',
'KEY3' => 'value3',
]);
});
it('handles empty lines', function () {
$content = <<<ENV
KEY1=value1
KEY2=value2
KEY3=value3
ENV;
$result = $this->parser->parseString($content);
expect($result)->toBe([
'KEY1' => 'value1',
'KEY2' => 'value2',
'KEY3' => 'value3',
]);
});
it('skips comment lines', function () {
$content = <<<ENV
# This is a comment
KEY1=value1
# Another comment
KEY2=value2
ENV;
$result = $this->parser->parseString($content);
expect($result)->toBe([
'KEY1' => 'value1',
'KEY2' => 'value2',
]);
});
it('removes double quotes from values', function () {
$content = <<<ENV
KEY1="quoted value"
KEY2="value with spaces"
KEY3="123"
ENV;
$result = $this->parser->parseString($content);
expect($result)->toBe([
'KEY1' => 'quoted value',
'KEY2' => 'value with spaces',
'KEY3' => 123, // Numeric string gets cast to int
]);
});
it('removes single quotes from values', function () {
$content = <<<ENV
KEY1='single quoted'
KEY2='value with spaces'
KEY3='true'
ENV;
$result = $this->parser->parseString($content);
expect($result)->toBe([
'KEY1' => 'single quoted',
'KEY2' => 'value with spaces',
'KEY3' => true, // 'true' gets cast to boolean
]);
});
it('casts "true" to boolean true', function () {
$content = <<<ENV
BOOL1=true
BOOL2=True
BOOL3=TRUE
ENV;
$result = $this->parser->parseString($content);
expect($result['BOOL1'])->toBeTrue();
expect($result['BOOL2'])->toBeTrue();
expect($result['BOOL3'])->toBeTrue();
});
it('casts "false" to boolean false', function () {
$content = <<<ENV
BOOL1=false
BOOL2=False
BOOL3=FALSE
ENV;
$result = $this->parser->parseString($content);
expect($result['BOOL1'])->toBeFalse();
expect($result['BOOL2'])->toBeFalse();
expect($result['BOOL3'])->toBeFalse();
});
it('casts "null" to null', function () {
$content = <<<ENV
NULL1=null
NULL2=Null
NULL3=NULL
ENV;
$result = $this->parser->parseString($content);
expect($result['NULL1'])->toBeNull();
expect($result['NULL2'])->toBeNull();
expect($result['NULL3'])->toBeNull();
});
it('casts numeric strings to integers', function () {
$content = <<<ENV
INT1=0
INT2=123
INT3=456
INT4=-789
ENV;
$result = $this->parser->parseString($content);
expect($result['INT1'])->toBe(0);
expect($result['INT2'])->toBe(123);
expect($result['INT3'])->toBe(456);
expect($result['INT4'])->toBe(-789);
});
it('casts numeric strings with decimals to floats', function () {
$content = <<<ENV
FLOAT1=0.0
FLOAT2=123.45
FLOAT3=-67.89
ENV;
$result = $this->parser->parseString($content);
expect($result['FLOAT1'])->toBe(0.0);
expect($result['FLOAT2'])->toBe(123.45);
expect($result['FLOAT3'])->toBe(-67.89);
});
it('keeps non-numeric strings as strings', function () {
$content = <<<ENV
STR1=hello
STR2=world123
STR3=abc-def
ENV;
$result = $this->parser->parseString($content);
expect($result['STR1'])->toBe('hello');
expect($result['STR2'])->toBe('world123');
expect($result['STR3'])->toBe('abc-def');
});
it('trims whitespace around keys and values', function () {
$content = <<<ENV
KEY1 = value1
KEY2= value2
KEY3=value3
ENV;
$result = $this->parser->parseString($content);
expect($result)->toBe([
'KEY1' => 'value1',
'KEY2' => 'value2',
'KEY3' => 'value3',
]);
});
it('handles empty values', function () {
$content = <<<ENV
EMPTY1=
EMPTY2=""
EMPTY3=''
ENV;
$result = $this->parser->parseString($content);
expect($result['EMPTY1'])->toBe('');
expect($result['EMPTY2'])->toBe('');
expect($result['EMPTY3'])->toBe('');
});
it('handles values with equals signs', function () {
$content = <<<ENV
URL=https://example.com?param=value
EQUATION=a=b+c
ENV;
$result = $this->parser->parseString($content);
expect($result['URL'])->toBe('https://example.com?param=value');
expect($result['EQUATION'])->toBe('a=b+c');
});
it('skips lines without equals sign', function () {
$content = <<<ENV
KEY1=value1
INVALID_LINE_WITHOUT_EQUALS
KEY2=value2
ENV;
$result = $this->parser->parseString($content);
expect($result)->toBe([
'KEY1' => 'value1',
'KEY2' => 'value2',
]);
});
it('handles mixed content with all features', function () {
$content = <<<ENV
# Database configuration
DB_HOST=localhost
DB_PORT=3306
DB_NAME="my_database"
DB_USER='root'
DB_PASS=
# Application settings
APP_ENV=production
APP_DEBUG=false
APP_URL=https://example.com
# Feature flags
FEATURE_ENABLED=true
CACHE_TTL=3600
API_TIMEOUT=30.5
ENV;
$result = $this->parser->parseString($content);
expect($result)->toBe([
'DB_HOST' => 'localhost',
'DB_PORT' => 3306,
'DB_NAME' => 'my_database',
'DB_USER' => 'root',
'DB_PASS' => '',
'APP_ENV' => 'production',
'APP_DEBUG' => false,
'APP_URL' => 'https://example.com',
'FEATURE_ENABLED' => true,
'CACHE_TTL' => 3600,
'API_TIMEOUT' => 30.5,
]);
});
});
describe('removeQuotes()', function () {
it('removes double quotes', function () {
$result = $this->parser->removeQuotes('"quoted value"');
expect($result)->toBe('quoted value');
});
it('removes single quotes', function () {
$result = $this->parser->removeQuotes("'quoted value'");
expect($result)->toBe('quoted value');
});
it('keeps unquoted values unchanged', function () {
$result = $this->parser->removeQuotes('unquoted');
expect($result)->toBe('unquoted');
});
it('keeps mismatched quotes unchanged', function () {
$result1 = $this->parser->removeQuotes('"mismatched\'');
$result2 = $this->parser->removeQuotes('\'mismatched"');
expect($result1)->toBe('"mismatched\'');
expect($result2)->toBe('\'mismatched"');
});
it('keeps empty quoted strings as empty', function () {
$result1 = $this->parser->removeQuotes('""');
$result2 = $this->parser->removeQuotes("''");
expect($result1)->toBe('');
expect($result2)->toBe('');
});
it('keeps single quote character unchanged', function () {
$result = $this->parser->removeQuotes('"');
expect($result)->toBe('"');
});
});
describe('parse() with FilePath', function () {
beforeEach(function () {
// Create temporary test directory
$this->testDir = '/home/michael/dev/michaelschiemer/tests/tmp';
if (!is_dir($this->testDir)) {
mkdir($this->testDir, 0777, true);
}
});
afterEach(function () {
// Clean up test files
if (isset($this->testFile) && file_exists($this->testFile)) {
unlink($this->testFile);
}
});
it('parses file with FilePath object', function () {
$this->testFile = $this->testDir . '/test.env';
file_put_contents($this->testFile, "KEY1=value1\nKEY2=value2");
$filePath = FilePath::create($this->testFile);
$result = $this->parser->parse($filePath);
expect($result)->toBe([
'KEY1' => 'value1',
'KEY2' => 'value2',
]);
});
it('parses file with string path', function () {
$this->testFile = $this->testDir . '/test.env';
file_put_contents($this->testFile, "KEY1=value1\nKEY2=value2");
$result = $this->parser->parse($this->testFile);
expect($result)->toBe([
'KEY1' => 'value1',
'KEY2' => 'value2',
]);
});
it('returns empty array for non-existent file', function () {
$result = $this->parser->parse($this->testDir . '/non-existent.env');
expect($result)->toBe([]);
});
it('returns empty array for unreadable file', function () {
$this->testFile = $this->testDir . '/unreadable.env';
file_put_contents($this->testFile, "KEY=value");
chmod($this->testFile, 0000); // Make unreadable
$result = $this->parser->parse($this->testFile);
expect($result)->toBe([]);
// Restore permissions for cleanup
chmod($this->testFile, 0644);
});
it('parses real .env file format', function () {
$this->testFile = $this->testDir . '/.env.test';
$content = <<<ENV
# Environment Configuration
APP_NAME="My Application"
APP_ENV=production
APP_DEBUG=false
# Database
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
# Redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
ENV;
file_put_contents($this->testFile, $content);
$result = $this->parser->parse($this->testFile);
expect($result)->toBe([
'APP_NAME' => 'My Application',
'APP_ENV' => 'production',
'APP_DEBUG' => false,
'DB_CONNECTION' => 'mysql',
'DB_HOST' => '127.0.0.1',
'DB_PORT' => 3306,
'DB_DATABASE' => 'homestead',
'DB_USERNAME' => 'homestead',
'DB_PASSWORD' => 'secret',
'REDIS_HOST' => '127.0.0.1',
'REDIS_PASSWORD' => null,
'REDIS_PORT' => 6379,
]);
});
});
describe('edge cases', function () {
it('handles Windows line endings (CRLF)', function () {
$content = "KEY1=value1\r\nKEY2=value2\r\n";
$result = $this->parser->parseString($content);
expect($result)->toBe([
'KEY1' => 'value1',
'KEY2' => 'value2',
]);
});
it('handles mixed line endings', function () {
$content = "KEY1=value1\nKEY2=value2\r\nKEY3=value3\r";
$result = $this->parser->parseString($content);
expect($result)->toBe([
'KEY1' => 'value1',
'KEY2' => 'value2',
'KEY3' => 'value3',
]);
});
it('handles keys with underscores and numbers', function () {
$content = <<<ENV
VAR_1=value1
VAR_2_TEST=value2
VAR123=value3
ENV;
$result = $this->parser->parseString($content);
expect($result)->toBe([
'VAR_1' => 'value1',
'VAR_2_TEST' => 'value2',
'VAR123' => 'value3',
]);
});
it('handles special characters in values', function () {
$content = <<<ENV
SPECIAL1=value!@#$%
SPECIAL2=value^&*()
SPECIAL3=value[]{}
ENV;
$result = $this->parser->parseString($content);
expect($result['SPECIAL1'])->toBe('value!@#$%');
expect($result['SPECIAL2'])->toBe('value^&*()');
expect($result['SPECIAL3'])->toBe('value[]{}');
});
it('handles URL values correctly', function () {
$content = <<<ENV
URL1=http://localhost:8080
URL2=https://example.com/path?param=value&other=123
URL3=mysql://user:pass@localhost:3306/database
ENV;
$result = $this->parser->parseString($content);
expect($result['URL1'])->toBe('http://localhost:8080');
expect($result['URL2'])->toBe('https://example.com/path?param=value&other=123');
expect($result['URL3'])->toBe('mysql://user:pass@localhost:3306/database');
});
it('overrides duplicate keys with last value', function () {
$content = <<<ENV
KEY=value1
KEY=value2
KEY=value3
ENV;
$result = $this->parser->parseString($content);
expect($result['KEY'])->toBe('value3');
});
});
});