Issues (138)

tests/Rule/CallbackTest.php (20 issues)

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 $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 ignore-unused  annotation

76
                    static fn (mixed $value, object $rule, /** @scrutinizer ignore-unused */ ValidationContext $context): Result => new Result(),

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 ignore-unused  annotation

76
                    static fn (mixed $value, /** @scrutinizer ignore-unused */ object $rule, ValidationContext $context): Result => new Result(),

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 ignore-unused  annotation

76
                    static fn (/** @scrutinizer ignore-unused */ mixed $value, object $rule, ValidationContext $context): Result => new Result(),

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 ignore-unused  annotation

86
                    static fn (/** @scrutinizer ignore-unused */ mixed $value, object $rule, ValidationContext $context): Result => new Result(),

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 ignore-unused  annotation

86
                    static fn (mixed $value, object $rule, /** @scrutinizer ignore-unused */ ValidationContext $context): Result => new Result(),

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 ignore-unused  annotation

86
                    static fn (mixed $value, /** @scrutinizer ignore-unused */ object $rule, ValidationContext $context): Result => new Result(),

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 ignore-unused  annotation

112
                    new Callback(static function (mixed $value, object $rule, /** @scrutinizer ignore-unused */ ValidationContext $context): Result {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 ignore-unused  annotation

112
                    new Callback(static function (mixed $value, /** @scrutinizer ignore-unused */ object $rule, ValidationContext $context): Result {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 ignore-unused  annotation

131
                    new Callback(static function (mixed $value, /** @scrutinizer ignore-unused */ object $rule, ValidationContext $context): Result {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 ignore-unused  annotation

131
                    new Callback(static function (mixed $value, object $rule, /** @scrutinizer ignore-unused */ ValidationContext $context): Result {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 ignore-unused  annotation

145
                    new Callback(static function (mixed $value, /** @scrutinizer ignore-unused */ object $rule, ValidationContext $context): Result {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 ignore-unused  annotation

145
                    new Callback(static function (mixed $value, object $rule, /** @scrutinizer ignore-unused */ ValidationContext $context): Result {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 ignore-unused  annotation

165
                    private function validateName(mixed $value, RuleInterface $rule, /** @scrutinizer ignore-unused */ ValidationContext $context): Result

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 ignore-unused  annotation

165
                    private function validateName(mixed $value, /** @scrutinizer ignore-unused */ RuleInterface $rule, ValidationContext $context): Result

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The method validateName() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
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
The method staticValidateName() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
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 ignore-unused  annotation

179
                        /** @scrutinizer ignore-unused */ RuleInterface $rule,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 ignore-unused  annotation

205
        $callback = static fn (mixed $value, object $rule, /** @scrutinizer ignore-unused */ ValidationContext $context): string => 'invalid return';

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 ignore-unused  annotation

205
        $callback = static fn (/** @scrutinizer ignore-unused */ mixed $value, object $rule, ValidationContext $context): string => 'invalid return';

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 ignore-unused  annotation

205
        $callback = static fn (mixed $value, /** @scrutinizer ignore-unused */ object $rule, ValidationContext $context): string => 'invalid return';

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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