Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
163
tests/Framework/Validation/ValidatorTest.php
Normal file
163
tests/Framework/Validation/ValidatorTest.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user