- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
164 lines
4.7 KiB
PHP
164 lines
4.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Framework\Reflection\CachedReflectionProvider;
|
|
use App\Framework\Validation\GroupAware;
|
|
use App\Framework\Validation\Rules\Email;
|
|
use App\Framework\Validation\Rules\Required;
|
|
use App\Framework\Validation\Rules\StringLength;
|
|
use App\Framework\Validation\ValidationResult;
|
|
use App\Framework\Validation\ValidationRule;
|
|
use App\Framework\Validation\Validator;
|
|
|
|
beforeEach(function () {
|
|
$reflectionProvider = new CachedReflectionProvider();
|
|
$this->validator = new Validator($reflectionProvider);
|
|
});
|
|
|
|
test('validation passes for valid object', function () {
|
|
$object = new ValidTestObject('test@example.com', 'John Doe');
|
|
$result = $this->validator->validate($object);
|
|
|
|
expect($result->hasErrors())->toBeFalse();
|
|
});
|
|
|
|
test('validation fails for invalid email', function () {
|
|
$object = new ValidTestObject('invalid-email', 'John Doe');
|
|
$result = $this->validator->validate($object);
|
|
|
|
expect($result->hasErrors())->toBeTrue()
|
|
->and($result->getFieldErrors('email'))->not->toBeEmpty();
|
|
});
|
|
|
|
test('validation fails for empty required field', function () {
|
|
$object = new ValidTestObject('test@example.com', '');
|
|
$result = $this->validator->validate($object);
|
|
|
|
expect($result->hasErrors())->toBeTrue()
|
|
->and($result->getFieldErrors('name'))->not->toBeEmpty();
|
|
});
|
|
|
|
test('validation fails for too short string', function () {
|
|
$object = new ValidTestObject('test@example.com', 'Jo'); // Too short
|
|
$result = $this->validator->validate($object);
|
|
|
|
expect($result->hasErrors())->toBeTrue()
|
|
->and($result->getFieldErrors('name'))->not->toBeEmpty();
|
|
});
|
|
|
|
test('validation with groups only validates specified group', function () {
|
|
$object = new GroupedTestObject('test@example.com', '');
|
|
|
|
// Validate only 'basic' group - should pass
|
|
$result = $this->validator->validate($object, 'basic');
|
|
expect($result->hasErrors())->toBeFalse();
|
|
|
|
// Validate 'extended' group - should fail due to empty name
|
|
$result = $this->validator->validate($object, 'extended');
|
|
expect($result->hasErrors())->toBeTrue()
|
|
->and($result->getFieldErrors('name'))->not->toBeEmpty();
|
|
});
|
|
|
|
test('validation handles uninitialized non-nullable properties', function () {
|
|
$object = new UninitializedTestObject();
|
|
$result = $this->validator->validate($object);
|
|
|
|
expect($result->hasErrors())->toBeTrue()
|
|
->and($result->getFieldErrors('requiredField'))->not->toBeEmpty();
|
|
});
|
|
|
|
test('validation handles nullable properties correctly', function () {
|
|
$object = new NullableTestObject();
|
|
$result = $this->validator->validate($object);
|
|
|
|
expect($result->hasErrors())->toBeFalse();
|
|
});
|
|
|
|
test('multiple validation errors are collected', function () {
|
|
$object = new ValidTestObject('invalid', ''); // Both email and name invalid
|
|
$result = $this->validator->validate($object);
|
|
|
|
expect($result->hasErrors())->toBeTrue()
|
|
->and($result->getFieldErrors('email'))->not->toBeEmpty()
|
|
->and($result->getFieldErrors('name'))->not->toBeEmpty()
|
|
->and($result->getAllErrorMessages())->toHaveCount(2);
|
|
});
|
|
|
|
test('validation result can be merged', function () {
|
|
$result1 = new ValidationResult();
|
|
$result1->addError('field1', 'Error 1');
|
|
|
|
$result2 = new ValidationResult();
|
|
$result2->addError('field2', 'Error 2');
|
|
|
|
$merged = $result1->merge($result2);
|
|
|
|
expect($merged->getFieldErrors('field1'))->toContain('Error 1')
|
|
->and($merged->getFieldErrors('field2'))->toContain('Error 2')
|
|
->and($merged->getAllErrorMessages())->toHaveCount(2);
|
|
});
|
|
|
|
// Test fixtures
|
|
class ValidTestObject
|
|
{
|
|
public function __construct(
|
|
#[Email]
|
|
public string $email,
|
|
#[Required]
|
|
#[StringLength(min: 3, max: 50)]
|
|
public string $name
|
|
) {
|
|
}
|
|
}
|
|
|
|
class GroupedTestObject
|
|
{
|
|
public function __construct(
|
|
#[Email]
|
|
#[TestValidationGroup('basic')]
|
|
public string $email,
|
|
#[Required]
|
|
#[TestValidationGroup('extended')]
|
|
public string $name
|
|
) {
|
|
}
|
|
}
|
|
|
|
class UninitializedTestObject
|
|
{
|
|
#[Required]
|
|
public string $requiredField;
|
|
}
|
|
|
|
class NullableTestObject
|
|
{
|
|
#[Email]
|
|
public ?string $optionalEmail = null;
|
|
}
|
|
|
|
// Custom validation rule for testing groups
|
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
|
class TestValidationGroup implements ValidationRule, GroupAware
|
|
{
|
|
public function __construct(
|
|
private string $group
|
|
) {
|
|
}
|
|
|
|
public function validate(mixed $value): bool
|
|
{
|
|
return true; // Always pass - we're just testing group functionality
|
|
}
|
|
|
|
public function getErrorMessages(): array
|
|
{
|
|
return [];
|
|
}
|
|
|
|
public function belongsToGroup(string $group): bool
|
|
{
|
|
return $this->group === $group;
|
|
}
|
|
}
|