Files
michaelschiemer/tests/Unit/Framework/Database/ValueObjects/ColumnNameTest.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- 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.
2025-10-25 19:18:37 +02:00

215 lines
8.8 KiB
PHP

<?php
declare(strict_types=1);
use App\Framework\Database\ValueObjects\ColumnName;
use App\Framework\Database\ValueObjects\TableName;
describe('ColumnName', function () {
it('creates column name from string', function () {
$column = ColumnName::fromString('email');
expect($column->value)->toBe('email');
expect($column->toString())->toBe('email');
});
it('validates column name format', function () {
// Valid names
expect(fn() => ColumnName::fromString('email'))->not->toThrow(\InvalidArgumentException::class);
expect(fn() => ColumnName::fromString('user_email'))->not->toThrow(\InvalidArgumentException::class);
expect(fn() => ColumnName::fromString('_temp_field'))->not->toThrow(\InvalidArgumentException::class);
expect(fn() => ColumnName::fromString('column123'))->not->toThrow(\InvalidArgumentException::class);
// Invalid names
expect(fn() => ColumnName::fromString(''))
->toThrow(\InvalidArgumentException::class, 'cannot be empty');
expect(fn() => ColumnName::fromString('123invalid'))
->toThrow(\InvalidArgumentException::class, 'must start with a letter or underscore');
expect(fn() => ColumnName::fromString('invalid-name'))
->toThrow(\InvalidArgumentException::class, 'can only contain letters, numbers, and underscores');
});
it('validates maximum length', function () {
$validName = str_repeat('a', 64);
expect(fn() => ColumnName::fromString($validName))->not->toThrow(\InvalidArgumentException::class);
$tooLong = str_repeat('a', 65);
expect(fn() => ColumnName::fromString($tooLong))
->toThrow(\InvalidArgumentException::class, '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.)
expect(fn() => ColumnName::fromString("email'; DROP TABLE--"))
->toThrow(\InvalidArgumentException::class);
expect(fn() => ColumnName::fromString('email UNION SELECT'))
->toThrow(\InvalidArgumentException::class);
});
it('quotes column names for different platforms', function () {
$column = ColumnName::fromString('email');
expect($column->quoted('mysql'))->toBe('`email`');
expect($column->quoted('postgresql'))->toBe('"email"');
expect($column->quoted('postgres'))->toBe('"email"');
expect($column->quoted('pgsql'))->toBe('"email"');
expect($column->quoted('sqlite'))->toBe('"email"');
expect($column->quoted())->toBe('`email`'); // Default MySQL
});
it('creates qualified column name', function () {
$table = TableName::fromString('users');
$column = ColumnName::fromString('email');
expect($column->qualified($table, 'mysql'))->toBe('`users`.`email`');
expect($column->qualified($table, 'postgresql'))->toBe('"users"."email"');
expect($column->qualified($table, 'sqlite'))->toBe('"users"."email"');
});
it('compares column names for equality', function () {
$col1 = ColumnName::fromString('email');
$col2 = ColumnName::fromString('email');
$col3 = ColumnName::fromString('EMAIL'); // Different case
$col4 = ColumnName::fromString('username');
expect($col1->equals($col2))->toBeTrue();
expect($col1->equals($col3))->toBeTrue(); // Case-insensitive
expect($col1->equals($col4))->toBeFalse();
});
it('matches column name patterns', function () {
$column = ColumnName::fromString('user_email');
expect($column->matches('user_*'))->toBeTrue();
expect($column->matches('*_email'))->toBeTrue();
expect($column->matches('user_email'))->toBeTrue();
expect($column->matches('order_*'))->toBeFalse();
});
it('detects reserved SQL keywords', function () {
$column = ColumnName::fromString('email');
expect($column->isReservedKeyword())->toBeFalse();
});
it('converts to lowercase', function () {
$column = ColumnName::fromString('UserEmail');
expect($column->toLower())->toBe('useremail');
});
it('checks for column name suffix', function () {
$column = ColumnName::fromString('user_id');
expect($column->hasSuffix('_id'))->toBeTrue();
expect($column->hasSuffix('_at'))->toBeFalse();
});
it('detects foreign key columns', function () {
$userId = ColumnName::fromString('user_id');
$orderId = ColumnName::fromString('order_id');
$email = ColumnName::fromString('email');
$id = ColumnName::fromString('id'); // Primary key, not foreign key
expect($userId->isForeignKey())->toBeTrue();
expect($orderId->isForeignKey())->toBeTrue();
expect($email->isForeignKey())->toBeFalse();
expect($id->isForeignKey())->toBeFalse(); // Special case: 'id' itself is not FK
});
it('detects timestamp columns', function () {
$createdAt = ColumnName::fromString('created_at');
$updatedAt = ColumnName::fromString('updated_at');
$deletedAt = ColumnName::fromString('deleted_at');
$publishedAt = ColumnName::fromString('published_at');
$email = ColumnName::fromString('email');
expect($createdAt->isTimestamp())->toBeTrue();
expect($updatedAt->isTimestamp())->toBeTrue();
expect($deletedAt->isTimestamp())->toBeTrue();
expect($publishedAt->isTimestamp())->toBeTrue(); // Any *_at
expect($email->isTimestamp())->toBeFalse();
});
it('detects standard timestamp column names', function () {
// Standard Laravel/framework timestamp columns
$createdAt = ColumnName::fromString('created_at');
$updatedAt = ColumnName::fromString('updated_at');
$deletedAt = ColumnName::fromString('deleted_at');
expect($createdAt->isTimestamp())->toBeTrue();
expect($updatedAt->isTimestamp())->toBeTrue();
expect($deletedAt->isTimestamp())->toBeTrue();
});
it('converts to string via magic method', function () {
$column = ColumnName::fromString('email');
expect((string) $column)->toBe('email');
});
it('is immutable', function () {
$column = ColumnName::fromString('email');
$value = $column->value;
// Value cannot be changed
expect($column->value)->toBe('email');
expect($value)->toBe('email');
});
it('handles edge cases correctly', function () {
// Single character (valid if letter or underscore)
expect(fn() => ColumnName::fromString('a'))->not->toThrow(\InvalidArgumentException::class);
expect(fn() => ColumnName::fromString('_'))->not->toThrow(\InvalidArgumentException::class);
// Underscore prefix
$column = ColumnName::fromString('_temp_field');
expect($column->value)->toBe('_temp_field');
// Numbers in name
$column = ColumnName::fromString('field_123');
expect($column->value)->toBe('field_123');
});
it('combines with TableName correctly', function () {
$table = TableName::fromString('users');
$column = ColumnName::fromString('email');
$qualified = $column->qualified($table);
expect($qualified)->toContain('users');
expect($qualified)->toContain('email');
expect($qualified)->toContain('.');
});
it('detects various foreign key naming patterns', function () {
// Standard pattern: table_id
expect(ColumnName::fromString('user_id')->isForeignKey())->toBeTrue();
expect(ColumnName::fromString('order_id')->isForeignKey())->toBeTrue();
expect(ColumnName::fromString('product_id')->isForeignKey())->toBeTrue();
// Non-FK patterns
expect(ColumnName::fromString('id')->isForeignKey())->toBeFalse();
expect(ColumnName::fromString('email')->isForeignKey())->toBeFalse();
expect(ColumnName::fromString('created_at')->isForeignKey())->toBeFalse();
});
it('detects various timestamp naming patterns', function () {
// Suffixed with _at
expect(ColumnName::fromString('created_at')->isTimestamp())->toBeTrue();
expect(ColumnName::fromString('updated_at')->isTimestamp())->toBeTrue();
expect(ColumnName::fromString('deleted_at')->isTimestamp())->toBeTrue();
expect(ColumnName::fromString('published_at')->isTimestamp())->toBeTrue();
expect(ColumnName::fromString('verified_at')->isTimestamp())->toBeTrue();
// Non-timestamp patterns
expect(ColumnName::fromString('email')->isTimestamp())->toBeFalse();
expect(ColumnName::fromString('user_id')->isTimestamp())->toBeFalse();
expect(ColumnName::fromString('status')->isTimestamp())->toBeFalse();
});
});