Skip to content

Commit cd1aeb4

Browse files
refactor: additional test case
1 parent 30d35bd commit cd1aeb4

File tree

4 files changed

+78
-48
lines changed

4 files changed

+78
-48
lines changed

packages/validation/src/Rules/Closure.php renamed to packages/validation/src/Rules/Predicate.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Tempest\Validation\Rules;
66

77
use Attribute;
8+
use Closure;
89
use InvalidArgumentException;
910
use ReflectionFunction;
1011
use Tempest\Validation\Rule;
@@ -15,12 +16,12 @@
1516
* The closure receives the value and must return true if it is valid, false otherwise.
1617
*/
1718
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
18-
final readonly class Closure implements Rule
19+
final readonly class Predicate implements Rule
1920
{
20-
private \Closure $callback;
21+
private Closure $callback;
2122

2223
public function __construct(
23-
\Closure $callback,
24+
Closure $callback,
2425
) {
2526
$this->callback = $callback;
2627

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Validation\Tests\Fixtures;
6+
7+
use Tempest\Validation\Rules\Predicate;
8+
9+
final class ObjectWithPredicateValidation
10+
{
11+
#[Predicate(static function (string $value): bool {
12+
return str_contains((string) $value, '@');
13+
})]
14+
public string $prop;
15+
}

packages/validation/tests/Rules/ClosureTest.php

Lines changed: 0 additions & 45 deletions
This file was deleted.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Validation\Tests\Rules;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use ReflectionProperty;
9+
use Tempest\Validation\Rules\Predicate;
10+
use Tempest\Validation\Tests\Fixtures\ObjectWithPredicateValidation;
11+
12+
/**
13+
* @internal
14+
*/
15+
final class PredicateTest extends TestCase
16+
{
17+
public function test_predicate_attribute_on_property_is_applied()
18+
{
19+
$reflection = new ReflectionProperty(ObjectWithPredicateValidation::class, 'prop');
20+
$attributes = $reflection->getAttributes(Predicate::class);
21+
22+
$this->assertCount(1, $attributes);
23+
24+
$rule = $attributes[0]->newInstance();
25+
$this->assertTrue($rule->isValid('user@example'));
26+
$this->assertFalse($rule->isValid('invalid-prop'));
27+
}
28+
29+
public function test_closure_validation_passes(): void
30+
{
31+
$rule = new Predicate(static fn (mixed $value): bool => str_contains((string) $value, '@'));
32+
$this->assertTrue($rule->isValid('user@example.com'));
33+
$this->assertTrue($rule->isValid('test@domain.org'));
34+
}
35+
36+
public function test_closure_validation_fails(): void
37+
{
38+
$rule = new Predicate(static fn (mixed $value): bool => str_contains((string) $value, '@'));
39+
40+
$this->assertFalse($rule->isValid('username'));
41+
$this->assertFalse($rule->isValid('example.com'));
42+
}
43+
44+
public function test_non_string_value_fails(): void
45+
{
46+
$rule = new Predicate(static fn (mixed $value): bool => str_contains((string) $value, '@'));
47+
48+
$this->assertFalse($rule->isValid(12345));
49+
$this->assertFalse($rule->isValid(null));
50+
$this->assertFalse($rule->isValid(false));
51+
}
52+
53+
public function test_static_closure_required(): void
54+
{
55+
$this->expectException(\InvalidArgumentException::class);
56+
57+
new Predicate(fn (mixed $value): bool => str_contains((string) $value, '@'));
58+
}
59+
}

0 commit comments

Comments
 (0)