- Move 12 markdown files from root to docs/ subdirectories - Organize documentation by category: • docs/troubleshooting/ (1 file) - Technical troubleshooting guides • docs/deployment/ (4 files) - Deployment and security documentation • docs/guides/ (3 files) - Feature-specific guides • docs/planning/ (4 files) - Planning and improvement proposals Root directory cleanup: - Reduced from 16 to 4 markdown files in root - Only essential project files remain: • CLAUDE.md (AI instructions) • README.md (Main project readme) • CLEANUP_PLAN.md (Current cleanup plan) • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements) This improves: ✅ Documentation discoverability ✅ Logical organization by purpose ✅ Clean root directory ✅ Better maintainability
163 lines
6.8 KiB
PHP
163 lines
6.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Framework\Database\ValueObjects;
|
|
|
|
use App\Framework\Database\ValueObjects\QueryParameters;
|
|
use InvalidArgumentException;
|
|
|
|
describe('QueryParameters Value Object', function () {
|
|
it('can be created empty', function () {
|
|
$params = QueryParameters::empty();
|
|
expect($params->isEmpty())->toBeTrue();
|
|
expect($params->count())->toBe(0);
|
|
expect($params->toArray())->toBe([]);
|
|
});
|
|
|
|
it('can be created from array', function () {
|
|
$data = ['id' => 123, 'name' => 'John'];
|
|
$params = QueryParameters::fromArray($data);
|
|
|
|
expect($params->isEmpty())->toBeFalse();
|
|
expect($params->count())->toBe(2);
|
|
expect($params->get('id'))->toBe(123);
|
|
expect($params->get('name'))->toBe('John');
|
|
});
|
|
|
|
it('can add parameters immutably', function () {
|
|
$params = QueryParameters::empty();
|
|
$newParams = $params->with('id', 123);
|
|
|
|
expect($params->has('id'))->toBeFalse(); // Original unchanged
|
|
expect($newParams->has('id'))->toBeTrue();
|
|
expect($newParams->get('id'))->toBe(123);
|
|
});
|
|
|
|
it('can merge parameters', function () {
|
|
$params1 = QueryParameters::fromArray(['id' => 123]);
|
|
$params2 = $params1->merge(['name' => 'John', 'active' => true]);
|
|
|
|
expect($params1->count())->toBe(1); // Original unchanged
|
|
expect($params2->count())->toBe(3);
|
|
expect($params2->get('id'))->toBe(123);
|
|
expect($params2->get('name'))->toBe('John');
|
|
expect($params2->get('active'))->toBeTrue();
|
|
});
|
|
|
|
it('can remove parameters immutably', function () {
|
|
$params = QueryParameters::fromArray(['id' => 123, 'name' => 'John']);
|
|
$newParams = $params->without('name');
|
|
|
|
expect($params->has('name'))->toBeTrue(); // Original unchanged
|
|
expect($newParams->has('name'))->toBeFalse();
|
|
expect($newParams->has('id'))->toBeTrue();
|
|
});
|
|
|
|
it('gets parameters with defaults', function () {
|
|
$params = QueryParameters::fromArray(['id' => 123]);
|
|
|
|
expect($params->get('id'))->toBe(123);
|
|
expect($params->get('missing'))->toBeNull();
|
|
expect($params->get('missing', 'default'))->toBe('default');
|
|
});
|
|
|
|
it('normalizes parameter names', function () {
|
|
$params = QueryParameters::fromArray([':id' => 123, 'name' => 'John']);
|
|
|
|
expect($params->has('id'))->toBeTrue();
|
|
expect($params->has(':id'))->toBeTrue(); // Both work
|
|
expect($params->get('id'))->toBe(123);
|
|
expect($params->get(':id'))->toBe(123);
|
|
});
|
|
|
|
it('converts to PDO array with colon prefixes', function () {
|
|
$params = QueryParameters::fromArray(['id' => 123, ':name' => 'John']);
|
|
$pdoArray = $params->toPdoArray();
|
|
|
|
expect($pdoArray)->toBe([':id' => 123, ':name' => 'John']);
|
|
});
|
|
|
|
it('finds used parameters in SQL', function () {
|
|
$params = QueryParameters::fromArray(['id' => 123, 'name' => 'John', 'unused' => 'test']);
|
|
$sql = 'SELECT * FROM users WHERE id = :id AND name = :name';
|
|
|
|
$used = $params->getUsedParameters($sql);
|
|
expect($used)->toBe(['id', 'name']);
|
|
});
|
|
|
|
it('finds unused parameters', function () {
|
|
$params = QueryParameters::fromArray(['id' => 123, 'name' => 'John', 'unused' => 'test']);
|
|
$sql = 'SELECT * FROM users WHERE id = :id';
|
|
|
|
$unused = $params->getUnusedParameters($sql);
|
|
expect($unused)->toBe(['name', 'unused']);
|
|
});
|
|
|
|
it('validates SQL parameter requirements', function () {
|
|
$params = QueryParameters::fromArray(['id' => 123]);
|
|
$sql = 'SELECT * FROM users WHERE id = :id AND name = :name';
|
|
|
|
expect(fn () => $params->validateForSql($sql))->toThrow(InvalidArgumentException::class);
|
|
});
|
|
|
|
it('validates SQL parameter requirements successfully', function () {
|
|
$params = QueryParameters::fromArray(['id' => 123, 'name' => 'John']);
|
|
$sql = 'SELECT * FROM users WHERE id = :id AND name = :name';
|
|
|
|
expect(fn () => $params->validateForSql($sql))->not->toThrow(InvalidArgumentException::class);
|
|
});
|
|
|
|
it('determines PDO parameter types', function () {
|
|
$params = QueryParameters::fromArray([
|
|
'null_val' => null,
|
|
'bool_val' => true,
|
|
'int_val' => 123,
|
|
'string_val' => 'text',
|
|
]);
|
|
|
|
expect($params->getPdoType('null_val'))->toBe(\PDO::PARAM_NULL);
|
|
expect($params->getPdoType('bool_val'))->toBe(\PDO::PARAM_BOOL);
|
|
expect($params->getPdoType('int_val'))->toBe(\PDO::PARAM_INT);
|
|
expect($params->getPdoType('string_val'))->toBe(\PDO::PARAM_STR);
|
|
});
|
|
|
|
it('throws exception for non-string parameter names', function () {
|
|
expect(fn () => QueryParameters::fromArray([123 => 'value']))->toThrow(InvalidArgumentException::class);
|
|
});
|
|
|
|
it('throws exception for empty parameter names', function () {
|
|
expect(fn () => QueryParameters::fromArray(['' => 'value']))->toThrow(InvalidArgumentException::class);
|
|
expect(fn () => QueryParameters::fromArray([':' => 'value']))->toThrow(InvalidArgumentException::class);
|
|
});
|
|
|
|
it('throws exception for invalid parameter names', function () {
|
|
expect(fn () => QueryParameters::fromArray(['1invalid' => 'value']))->toThrow(InvalidArgumentException::class);
|
|
expect(fn () => QueryParameters::fromArray(['invalid-name' => 'value']))->toThrow(InvalidArgumentException::class);
|
|
expect(fn () => QueryParameters::fromArray(['invalid name' => 'value']))->toThrow(InvalidArgumentException::class);
|
|
});
|
|
|
|
it('allows valid parameter names', function () {
|
|
expect(fn () => QueryParameters::fromArray(['valid' => 'value']))->not->toThrow(InvalidArgumentException::class);
|
|
expect(fn () => QueryParameters::fromArray(['valid_name' => 'value']))->not->toThrow(InvalidArgumentException::class);
|
|
expect(fn () => QueryParameters::fromArray(['valid123' => 'value']))->not->toThrow(InvalidArgumentException::class);
|
|
expect(fn () => QueryParameters::fromArray(['_valid' => 'value']))->not->toThrow(InvalidArgumentException::class);
|
|
expect(fn () => QueryParameters::fromArray([':valid' => 'value']))->not->toThrow(InvalidArgumentException::class);
|
|
});
|
|
|
|
it('throws exception for non-scalar parameter values', function () {
|
|
expect(fn () => QueryParameters::fromArray(['array' => [1, 2, 3]]))->toThrow(InvalidArgumentException::class);
|
|
expect(fn () => QueryParameters::fromArray(['object' => new \stdClass()]))->toThrow(InvalidArgumentException::class);
|
|
});
|
|
|
|
it('allows scalar and null parameter values', function () {
|
|
expect(fn () => QueryParameters::fromArray([
|
|
'string' => 'text',
|
|
'int' => 123,
|
|
'float' => 12.34,
|
|
'bool' => true,
|
|
'null' => null,
|
|
]))->not->toThrow(InvalidArgumentException::class);
|
|
});
|
|
});
|