1 | <?php |
||||||||
2 | |||||||||
3 | declare(strict_types=1); |
||||||||
4 | |||||||||
5 | namespace Yiisoft\Validator\Tests\Rule; |
||||||||
6 | |||||||||
7 | use InvalidArgumentException; |
||||||||
8 | use RuntimeException; |
||||||||
9 | use stdClass; |
||||||||
10 | use Yiisoft\Validator\DataSet\ObjectDataSet; |
||||||||
11 | use Yiisoft\Validator\Exception\InvalidCallbackReturnTypeException; |
||||||||
12 | use Yiisoft\Validator\Result; |
||||||||
13 | use Yiisoft\Validator\Rule\Callback; |
||||||||
14 | use Yiisoft\Validator\Rule\CallbackHandler; |
||||||||
15 | use Yiisoft\Validator\RuleInterface; |
||||||||
16 | use Yiisoft\Validator\Tests\Rule\Base\DifferentRuleInHandlerTestTrait; |
||||||||
17 | use Yiisoft\Validator\Tests\Rule\Base\RuleTestCase; |
||||||||
18 | use Yiisoft\Validator\Tests\Rule\Base\RuleWithOptionsTestTrait; |
||||||||
19 | use Yiisoft\Validator\Tests\Rule\Base\SkipOnErrorTestTrait; |
||||||||
20 | use Yiisoft\Validator\Tests\Rule\Base\WhenTestTrait; |
||||||||
21 | use Yiisoft\Validator\Tests\Support\Data\CallbackDto; |
||||||||
22 | use Yiisoft\Validator\ValidationContext; |
||||||||
23 | use Yiisoft\Validator\Validator; |
||||||||
24 | |||||||||
25 | final class CallbackTest extends RuleTestCase |
||||||||
26 | { |
||||||||
27 | use DifferentRuleInHandlerTestTrait; |
||||||||
28 | use RuleWithOptionsTestTrait; |
||||||||
29 | use SkipOnErrorTestTrait; |
||||||||
30 | use WhenTestTrait; |
||||||||
31 | |||||||||
32 | public function testInitWithNoCallbackAndMethodException(): void |
||||||||
33 | { |
||||||||
34 | $this->expectException(InvalidArgumentException::class); |
||||||||
35 | $this->expectExceptionMessage('Either "$callback" or "$method" must be specified.'); |
||||||||
36 | new Callback(); |
||||||||
37 | } |
||||||||
38 | |||||||||
39 | public function testInitWithBothCallbackAndMethodException(): void |
||||||||
40 | { |
||||||||
41 | $this->expectException(InvalidArgumentException::class); |
||||||||
42 | $this->expectExceptionMessage('"$callback" and "$method" are mutually exclusive.'); |
||||||||
43 | new Callback(callback: static fn (): Result => new Result(), method: 'test'); |
||||||||
44 | } |
||||||||
45 | |||||||||
46 | public function testGetName(): void |
||||||||
47 | { |
||||||||
48 | $rule = new Callback(callback: static fn (): Result => new Result()); |
||||||||
49 | $this->assertSame(Callback::class, $rule->getName()); |
||||||||
50 | } |
||||||||
51 | |||||||||
52 | public function testGetMethod(): void |
||||||||
53 | { |
||||||||
54 | $rule = new Callback(method: 'test'); |
||||||||
55 | $this->assertSame('test', $rule->getMethod()); |
||||||||
56 | } |
||||||||
57 | |||||||||
58 | public function testAfterInitAttributeWithNoMethod(): void |
||||||||
59 | { |
||||||||
60 | $rule = new Callback(callback: static fn (): Result => new Result()); |
||||||||
61 | $callback = $rule->getCallback(); |
||||||||
62 | $this->assertIsCallable($callback); |
||||||||
63 | $this->assertNull($rule->getMethod()); |
||||||||
64 | |||||||||
65 | $rule->afterInitAttribute(new ObjectDataSet(new stdClass())); |
||||||||
66 | $this->assertIsCallable($callback); |
||||||||
67 | $this->assertNull($rule->getMethod()); |
||||||||
68 | $this->assertSame($callback, $rule->getCallback()); |
||||||||
69 | } |
||||||||
70 | |||||||||
71 | public function dataOptions(): array |
||||||||
72 | { |
||||||||
73 | return [ |
||||||||
74 | [ |
||||||||
75 | new Callback( |
||||||||
76 | static fn (mixed $value, object $rule, ValidationContext $context): Result => new Result(), |
||||||||
0 ignored issues
–
show
The parameter
$rule is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() The parameter
$value is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||||
77 | ), |
||||||||
78 | [ |
||||||||
79 | 'method' => null, |
||||||||
80 | 'skipOnEmpty' => false, |
||||||||
81 | 'skipOnError' => false, |
||||||||
82 | ], |
||||||||
83 | ], |
||||||||
84 | [ |
||||||||
85 | new Callback( |
||||||||
86 | static fn (mixed $value, object $rule, ValidationContext $context): Result => new Result(), |
||||||||
0 ignored issues
–
show
The parameter
$value is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() The parameter
$context is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() The parameter
$rule is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||||
87 | skipOnEmpty: true, |
||||||||
88 | ), |
||||||||
89 | [ |
||||||||
90 | 'method' => null, |
||||||||
91 | 'skipOnEmpty' => true, |
||||||||
92 | 'skipOnError' => false, |
||||||||
93 | ], |
||||||||
94 | ], |
||||||||
95 | [ |
||||||||
96 | new Callback(method: 'test'), |
||||||||
97 | [ |
||||||||
98 | 'method' => 'test', |
||||||||
99 | 'skipOnEmpty' => false, |
||||||||
100 | 'skipOnError' => false, |
||||||||
101 | ], |
||||||||
102 | ], |
||||||||
103 | ]; |
||||||||
104 | } |
||||||||
105 | |||||||||
106 | public function dataValidationPassed(): array |
||||||||
107 | { |
||||||||
108 | return [ |
||||||||
109 | [ |
||||||||
110 | 42, |
||||||||
111 | [ |
||||||||
112 | new Callback(static function (mixed $value, object $rule, ValidationContext $context): Result { |
||||||||
0 ignored issues
–
show
The parameter
$context is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() The parameter
$rule is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||||
113 | $result = new Result(); |
||||||||
114 | if ($value !== 42) { |
||||||||
115 | $result->addError('Value should be 42!'); |
||||||||
116 | } |
||||||||
117 | |||||||||
118 | return $result; |
||||||||
119 | }), |
||||||||
120 | ], |
||||||||
121 | ], |
||||||||
122 | ]; |
||||||||
123 | } |
||||||||
124 | |||||||||
125 | public function dataValidationFailed(): array |
||||||||
126 | { |
||||||||
127 | return [ |
||||||||
128 | [ |
||||||||
129 | 41, |
||||||||
130 | [ |
||||||||
131 | new Callback(static function (mixed $value, object $rule, ValidationContext $context): Result { |
||||||||
0 ignored issues
–
show
The parameter
$rule is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() The parameter
$context is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||||
132 | $result = new Result(); |
||||||||
133 | if ($value !== 42) { |
||||||||
134 | $result->addError('Value should be 42!'); |
||||||||
135 | } |
||||||||
136 | |||||||||
137 | return $result; |
||||||||
138 | }), |
||||||||
139 | ], |
||||||||
140 | ['' => ['Value should be 42!']], |
||||||||
141 | ], |
||||||||
142 | 'custom error' => [ |
||||||||
143 | 41, |
||||||||
144 | [ |
||||||||
145 | new Callback(static function (mixed $value, object $rule, ValidationContext $context): Result { |
||||||||
0 ignored issues
–
show
The parameter
$rule is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() The parameter
$context is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||||
146 | $result = new Result(); |
||||||||
147 | if ($value !== 42) { |
||||||||
148 | $result->addError('Custom error'); |
||||||||
149 | } |
||||||||
150 | |||||||||
151 | return $result; |
||||||||
152 | }), |
||||||||
153 | ], |
||||||||
154 | ['' => ['Custom error']], |
||||||||
155 | ], |
||||||||
156 | 'non-static callable' => [ |
||||||||
157 | new class (1) { |
||||||||
158 | public function __construct( |
||||||||
159 | #[Callback(method: 'validateName')] |
||||||||
160 | #[Callback(method: 'staticValidateName')] |
||||||||
161 | private $age, |
||||||||
162 | ) { |
||||||||
163 | } |
||||||||
164 | |||||||||
165 | private function validateName(mixed $value, RuleInterface $rule, ValidationContext $context): Result |
||||||||
0 ignored issues
–
show
The parameter
$context is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() The parameter
$rule is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||||
166 | { |
||||||||
167 | if ($value !== $this->age) { |
||||||||
168 | throw new RuntimeException('Method scope was not bound to the object.'); |
||||||||
169 | } |
||||||||
170 | |||||||||
171 | $result = new Result(); |
||||||||
172 | $result->addError('Hello from non-static method.'); |
||||||||
173 | |||||||||
174 | return $result; |
||||||||
175 | } |
||||||||
176 | |||||||||
177 | private static function staticValidateName( |
||||||||
0 ignored issues
–
show
|
|||||||||
178 | mixed $value, |
||||||||
179 | RuleInterface $rule, |
||||||||
0 ignored issues
–
show
The parameter
$rule is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||||
180 | ValidationContext $context |
||||||||
181 | ): Result { |
||||||||
182 | if ($value !== $context->getDataSet()->getAttributeValue('age')) { |
||||||||
183 | throw new RuntimeException('Method scope was not bound to the object.'); |
||||||||
184 | } |
||||||||
185 | |||||||||
186 | $result = new Result(); |
||||||||
187 | $result->addError('Hello from static method.'); |
||||||||
188 | |||||||||
189 | return $result; |
||||||||
190 | } |
||||||||
191 | }, |
||||||||
192 | null, |
||||||||
193 | ['age' => ['Hello from non-static method.', 'Hello from static method.']], |
||||||||
194 | ], |
||||||||
195 | 'class attribute' => [ |
||||||||
196 | new CallbackDto(7, 42), |
||||||||
197 | null, |
||||||||
198 | ['' => ['7 / 42']], |
||||||||
199 | ], |
||||||||
200 | ]; |
||||||||
201 | } |
||||||||
202 | |||||||||
203 | public function testThrowExceptionWithInvalidReturn(): void |
||||||||
204 | { |
||||||||
205 | $callback = static fn (mixed $value, object $rule, ValidationContext $context): string => 'invalid return'; |
||||||||
0 ignored issues
–
show
The parameter
$context is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() The parameter
$value is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() The parameter
$rule is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||||
206 | $rule = new Callback($callback); |
||||||||
207 | $validator = new Validator(); |
||||||||
208 | |||||||||
209 | $this->expectException(InvalidCallbackReturnTypeException::class); |
||||||||
210 | $message = 'Return value of callback must be an instance of "Yiisoft\Validator\Result", "string" returned.'; |
||||||||
211 | $this->expectExceptionMessage($message); |
||||||||
212 | $validator->validate(null, [$rule]); |
||||||||
213 | } |
||||||||
214 | |||||||||
215 | public function testValidateUsingMethodOutsideAttributeScope(): void |
||||||||
216 | { |
||||||||
217 | $rule = new Callback(method: 'validateName'); |
||||||||
218 | $validator = new Validator(); |
||||||||
219 | |||||||||
220 | $this->expectException(InvalidArgumentException::class); |
||||||||
221 | $this->expectExceptionMessage('Using method outside of attribute scope is prohibited.'); |
||||||||
222 | $validator->validate(null, [$rule]); |
||||||||
223 | } |
||||||||
224 | |||||||||
225 | public function testSkipOnError(): void |
||||||||
226 | { |
||||||||
227 | $this->testSkipOnErrorInternal( |
||||||||
228 | new Callback(callback: static fn (): Result => new Result()), |
||||||||
229 | new Callback(callback: static fn (): Result => new Result(), skipOnError: true), |
||||||||
230 | ); |
||||||||
231 | } |
||||||||
232 | |||||||||
233 | public function testWhen(): void |
||||||||
234 | { |
||||||||
235 | $when = static fn (mixed $value): bool => $value !== null; |
||||||||
236 | $this->testWhenInternal( |
||||||||
237 | new Callback(callback: static fn (): Result => new Result()), |
||||||||
238 | new Callback(callback: static fn (): Result => new Result(), when: $when), |
||||||||
239 | ); |
||||||||
240 | } |
||||||||
241 | |||||||||
242 | protected function getDifferentRuleInHandlerItems(): array |
||||||||
243 | { |
||||||||
244 | return [Callback::class, CallbackHandler::class]; |
||||||||
245 | } |
||||||||
246 | } |
||||||||
247 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.