- 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.
271 lines
9.8 KiB
PHP
271 lines
9.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Framework\Database\ValueObjects\DatabaseName;
|
|
|
|
describe('DatabaseName', function () {
|
|
it('creates database name from string', function () {
|
|
$dbName = DatabaseName::fromString('myapp_production');
|
|
|
|
expect($dbName->value)->toBe('myapp_production');
|
|
expect($dbName->toString())->toBe('myapp_production');
|
|
});
|
|
|
|
it('validates database name format', function () {
|
|
// Valid names - should not throw
|
|
DatabaseName::fromString('myapp');
|
|
DatabaseName::fromString('my_app_db');
|
|
DatabaseName::fromString('_temp_db');
|
|
DatabaseName::fromString('db123');
|
|
|
|
expect(true)->toBeTrue(); // Validation passed
|
|
|
|
// Invalid names - should throw
|
|
try {
|
|
DatabaseName::fromString('');
|
|
expect(false)->toBeTrue('Should have thrown for empty name');
|
|
} catch (\InvalidArgumentException $e) {
|
|
expect($e->getMessage())->toContain('cannot be empty');
|
|
}
|
|
|
|
try {
|
|
DatabaseName::fromString('123invalid');
|
|
expect(false)->toBeTrue('Should have thrown for name starting with number');
|
|
} catch (\InvalidArgumentException $e) {
|
|
expect($e->getMessage())->toContain('must start with a letter or underscore');
|
|
}
|
|
|
|
try {
|
|
DatabaseName::fromString('invalid-name');
|
|
expect(false)->toBeTrue('Should have thrown for name with hyphen');
|
|
} catch (\InvalidArgumentException $e) {
|
|
expect($e->getMessage())->toContain('can only contain letters, numbers, and underscores');
|
|
}
|
|
|
|
try {
|
|
DatabaseName::fromString('invalid name');
|
|
expect(false)->toBeTrue('Should have thrown for name with space');
|
|
} catch (\InvalidArgumentException $e) {
|
|
expect($e->getMessage())->toContain('can only contain letters, numbers, and underscores');
|
|
}
|
|
});
|
|
|
|
it('validates maximum length', function () {
|
|
// Valid length
|
|
$validName = str_repeat('a', 64);
|
|
DatabaseName::fromString($validName); // Should not throw
|
|
|
|
// Too long
|
|
$tooLong = str_repeat('a', 65);
|
|
try {
|
|
DatabaseName::fromString($tooLong);
|
|
expect(false)->toBeTrue('Should have thrown for name exceeding max length');
|
|
} catch (\InvalidArgumentException $e) {
|
|
expect($e->getMessage())->toContain('exceeds maximum length');
|
|
}
|
|
});
|
|
|
|
it('detects SQL injection attempts', function () {
|
|
// Note: SQL injection attempts are caught by format validation
|
|
// since they contain invalid characters (quotes, hyphens, spaces, etc.)
|
|
|
|
try {
|
|
DatabaseName::fromString("myapp'; DROP DATABASE--");
|
|
expect(false)->toBeTrue('Should have thrown for SQL injection attempt');
|
|
} catch (\InvalidArgumentException $e) {
|
|
expect($e->getMessage())->toBeString();
|
|
}
|
|
|
|
try {
|
|
DatabaseName::fromString('myapp UNION SELECT');
|
|
expect(false)->toBeTrue('Should have thrown for SQL injection attempt');
|
|
} catch (\InvalidArgumentException $e) {
|
|
expect($e->getMessage())->toBeString();
|
|
}
|
|
|
|
try {
|
|
DatabaseName::fromString('myapp/*comment*/');
|
|
expect(false)->toBeTrue('Should have thrown for SQL injection attempt');
|
|
} catch (\InvalidArgumentException $e) {
|
|
expect($e->getMessage())->toBeString();
|
|
}
|
|
});
|
|
|
|
it('quotes database names for different platforms', function () {
|
|
$dbName = DatabaseName::fromString('myapp');
|
|
|
|
expect($dbName->quoted('mysql'))->toBe('`myapp`');
|
|
expect($dbName->quoted('postgresql'))->toBe('"myapp"');
|
|
expect($dbName->quoted('postgres'))->toBe('"myapp"');
|
|
expect($dbName->quoted('pgsql'))->toBe('"myapp"');
|
|
expect($dbName->quoted('sqlite'))->toBe('"myapp"');
|
|
expect($dbName->quoted())->toBe('`myapp`'); // Default MySQL
|
|
expect($dbName->quoted('unknown'))->toBe('`myapp`'); // Fallback to MySQL
|
|
});
|
|
|
|
it('compares database names for equality', function () {
|
|
$db1 = DatabaseName::fromString('myapp');
|
|
$db2 = DatabaseName::fromString('myapp');
|
|
$db3 = DatabaseName::fromString('MYAPP'); // Different case
|
|
$db4 = DatabaseName::fromString('other_db');
|
|
|
|
expect($db1->equals($db2))->toBeTrue();
|
|
expect($db1->equals($db3))->toBeTrue(); // Case-insensitive
|
|
expect($db1->equals($db4))->toBeFalse();
|
|
});
|
|
|
|
it('matches database name patterns', function () {
|
|
$dbName = DatabaseName::fromString('myapp_production');
|
|
|
|
expect($dbName->matches('myapp_*'))->toBeTrue();
|
|
expect($dbName->matches('*_production'))->toBeTrue();
|
|
expect($dbName->matches('myapp_production'))->toBeTrue();
|
|
expect($dbName->matches('other_*'))->toBeFalse();
|
|
});
|
|
|
|
it('detects reserved SQL keywords', function () {
|
|
$dbName = DatabaseName::fromString('myapp');
|
|
expect($dbName->isReservedKeyword())->toBeFalse();
|
|
});
|
|
|
|
it('converts to lowercase', function () {
|
|
$dbName = DatabaseName::fromString('MyApp_Production');
|
|
|
|
expect($dbName->toLower())->toBe('myapp_production');
|
|
});
|
|
|
|
it('converts to uppercase', function () {
|
|
$dbName = DatabaseName::fromString('myapp_production');
|
|
|
|
expect($dbName->toUpper())->toBe('MYAPP_PRODUCTION');
|
|
});
|
|
|
|
it('checks for database name prefix', function () {
|
|
$dbName = DatabaseName::fromString('myapp_production');
|
|
|
|
expect($dbName->hasPrefix('myapp_'))->toBeTrue();
|
|
expect($dbName->hasPrefix('other_'))->toBeFalse();
|
|
});
|
|
|
|
it('checks for database name suffix', function () {
|
|
$dbName = DatabaseName::fromString('myapp_production');
|
|
|
|
expect($dbName->hasSuffix('_production'))->toBeTrue();
|
|
expect($dbName->hasSuffix('_staging'))->toBeFalse();
|
|
});
|
|
|
|
it('adds prefix to database name', function () {
|
|
$dbName = DatabaseName::fromString('myapp');
|
|
$prefixed = $dbName->withPrefix('dev_');
|
|
|
|
expect($prefixed->value)->toBe('dev_myapp');
|
|
expect($prefixed->toString())->toBe('dev_myapp');
|
|
|
|
// Original unchanged (immutable)
|
|
expect($dbName->value)->toBe('myapp');
|
|
});
|
|
|
|
it('removes prefix from database name', function () {
|
|
$dbName = DatabaseName::fromString('dev_myapp');
|
|
$unprefixed = $dbName->withoutPrefix('dev_');
|
|
|
|
expect($unprefixed->value)->toBe('myapp');
|
|
|
|
// Removing non-existent prefix returns same instance
|
|
$same = $unprefixed->withoutPrefix('prod_');
|
|
expect($same->value)->toBe('myapp');
|
|
});
|
|
|
|
it('adds suffix to database name', function () {
|
|
$dbName = DatabaseName::fromString('myapp');
|
|
$suffixed = $dbName->withSuffix('_production');
|
|
|
|
expect($suffixed->value)->toBe('myapp_production');
|
|
|
|
// Original unchanged (immutable)
|
|
expect($dbName->value)->toBe('myapp');
|
|
});
|
|
|
|
it('removes suffix from database name', function () {
|
|
$dbName = DatabaseName::fromString('myapp_production');
|
|
$unsuffixed = $dbName->withoutSuffix('_production');
|
|
|
|
expect($unsuffixed->value)->toBe('myapp');
|
|
|
|
// Removing non-existent suffix returns same instance
|
|
$same = $unsuffixed->withoutSuffix('_staging');
|
|
expect($same->value)->toBe('myapp');
|
|
});
|
|
|
|
it('detects environment suffixes', function () {
|
|
$prodDb = DatabaseName::fromString('myapp_production');
|
|
expect($prodDb->getEnvironmentSuffix())->toBe('production');
|
|
|
|
$stagingDb = DatabaseName::fromString('myapp_staging');
|
|
expect($stagingDb->getEnvironmentSuffix())->toBe('staging');
|
|
|
|
$testDb = DatabaseName::fromString('myapp_test');
|
|
expect($testDb->getEnvironmentSuffix())->toBe('test');
|
|
|
|
$devDb = DatabaseName::fromString('myapp_development');
|
|
expect($devDb->getEnvironmentSuffix())->toBe('development');
|
|
|
|
$localDb = DatabaseName::fromString('myapp_local');
|
|
expect($localDb->getEnvironmentSuffix())->toBe('local');
|
|
|
|
$noEnvDb = DatabaseName::fromString('myapp');
|
|
expect($noEnvDb->getEnvironmentSuffix())->toBeNull();
|
|
});
|
|
|
|
it('detects test databases', function () {
|
|
$testDb1 = DatabaseName::fromString('myapp_test');
|
|
expect($testDb1->isTestDatabase())->toBeTrue();
|
|
|
|
$testDb2 = DatabaseName::fromString('test_myapp');
|
|
expect($testDb2->isTestDatabase())->toBeTrue();
|
|
|
|
$prodDb = DatabaseName::fromString('myapp_production');
|
|
expect($prodDb->isTestDatabase())->toBeFalse();
|
|
});
|
|
|
|
it('converts to string via magic method', function () {
|
|
$dbName = DatabaseName::fromString('myapp');
|
|
|
|
expect((string) $dbName)->toBe('myapp');
|
|
});
|
|
|
|
it('is immutable', function () {
|
|
$original = DatabaseName::fromString('myapp');
|
|
$prefixed = $original->withPrefix('dev_');
|
|
$suffixed = $original->withSuffix('_prod');
|
|
|
|
// Original remains unchanged
|
|
expect($original->value)->toBe('myapp');
|
|
|
|
// New instances created - values should differ
|
|
expect($prefixed->value)->toBe('dev_myapp');
|
|
expect($suffixed->value)->toBe('myapp_prod');
|
|
|
|
// Verify immutability by checking original hasn't changed
|
|
expect($original->value)->toBe('myapp');
|
|
});
|
|
|
|
it('handles edge cases correctly', function () {
|
|
// Single character (valid if letter or underscore)
|
|
$a = DatabaseName::fromString('a');
|
|
expect($a->value)->toBe('a');
|
|
|
|
$underscore = DatabaseName::fromString('_');
|
|
expect($underscore->value)->toBe('_');
|
|
|
|
// Underscore-only prefix
|
|
$dbName = DatabaseName::fromString('_temp_myapp');
|
|
expect($dbName->value)->toBe('_temp_myapp');
|
|
|
|
// Numbers in name (but not at start)
|
|
$dbName = DatabaseName::fromString('myapp_123_db');
|
|
expect($dbName->value)->toBe('myapp_123_db');
|
|
});
|
|
});
|