• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 19814540732

01 Dec 2025 07:18AM UTC coverage: 25.291% (+25.3%) from 0.0%
19814540732

push

github

soyuka
Merge 4.2

13 of 227 new or added lines in 14 files covered. (5.73%)

9 existing lines in 6 files now uncovered.

14636 of 57870 relevant lines covered (25.29%)

28.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Symfony/Tests/Validator/Metadata/Property/ValidatorPropertyMetadataFactoryTest.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
declare(strict_types=1);
13

14
namespace ApiPlatform\Symfony\Tests\Validator\Metadata\Property;
15

16
use ApiPlatform\Metadata\ApiProperty;
17
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
18
use ApiPlatform\Symfony\Tests\Fixtures\DummyAtLeastOneOfValidatedEntity;
19
use ApiPlatform\Symfony\Tests\Fixtures\DummyCollectionValidatedEntity;
20
use ApiPlatform\Symfony\Tests\Fixtures\DummyCompoundValidatedEntity;
21
use ApiPlatform\Symfony\Tests\Fixtures\DummyCountValidatedEntity;
22
use ApiPlatform\Symfony\Tests\Fixtures\DummyIriWithValidationEntity;
23
use ApiPlatform\Symfony\Tests\Fixtures\DummyNumericValidatedEntity;
24
use ApiPlatform\Symfony\Tests\Fixtures\DummyRangeValidatedEntity;
25
use ApiPlatform\Symfony\Tests\Fixtures\DummySequentiallyValidatedEntity;
26
use ApiPlatform\Symfony\Tests\Fixtures\DummyUniqueValidatedEntity;
27
use ApiPlatform\Symfony\Tests\Fixtures\DummyValidatedChoiceEntity;
28
use ApiPlatform\Symfony\Tests\Fixtures\DummyValidatedEntity;
29
use ApiPlatform\Symfony\Tests\Fixtures\DummyValidatedHostnameEntity;
30
use ApiPlatform\Symfony\Tests\Fixtures\DummyValidatedUlidEntity;
31
use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaChoiceRestriction;
32
use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaCollectionRestriction;
33
use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaCountRestriction;
34
use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaFormat;
35
use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaGreaterThanOrEqualRestriction;
36
use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaGreaterThanRestriction;
37
use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaLengthRestriction;
38
use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaLessThanOrEqualRestriction;
39
use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaLessThanRestriction;
40
use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaOneOfRestriction;
41
use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaRangeRestriction;
42
use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaRegexRestriction;
43
use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaUniqueRestriction;
44
use ApiPlatform\Symfony\Validator\Metadata\Property\ValidatorPropertyMetadataFactory;
45
use PHPUnit\Framework\Attributes\DataProvider;
46
use PHPUnit\Framework\Attributes\IgnoreDeprecations;
47
use PHPUnit\Framework\TestCase;
48
use Prophecy\PhpUnit\ProphecyTrait;
49
use Symfony\Component\PropertyInfo\Type as LegacyType;
50
use Symfony\Component\TypeInfo\Type;
51
use Symfony\Component\Validator\Constraints\Hostname;
52
use Symfony\Component\Validator\Constraints\Ulid;
53
use Symfony\Component\Validator\Mapping\ClassMetadata;
54
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
55
use Symfony\Component\Validator\Mapping\Loader\AttributeLoader;
56

57
/**
58
 * @author Baptiste Meyer <baptiste.meyer@gmail.com>
59
 */
60
class ValidatorPropertyMetadataFactoryTest extends TestCase
61
{
62
    use ProphecyTrait;
63

64
    private ClassMetadata $validatorClassMetadata;
65

66
    protected function setUp(): void
67
    {
68
        $this->validatorClassMetadata = new ClassMetadata(DummyValidatedEntity::class);
×
69
        (new AttributeLoader())->loadClassMetadata($this->validatorClassMetadata);
×
70
    }
71

72
    public function testCreateWithPropertyWithRequiredConstraints(): void
73
    {
74
        $dummyPropertyMetadata = (new ApiProperty())->withDescription('A dummy')->withReadable(true)->withWritable(true);
×
75
        $emailPropertyMetadata = (new ApiProperty())->withTypes(['https://schema.org/email'])->withReadable(true)->withWritable(true);
×
76

77
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
78
        $decoratedPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummy', [])->willReturn($dummyPropertyMetadata)->shouldBeCalled();
×
79
        $decoratedPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummyEmail', [])->willReturn($emailPropertyMetadata)->shouldBeCalled();
×
80

81
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
82
        $validatorMetadataFactory->getMetadataFor(DummyValidatedEntity::class)->willReturn($this->validatorClassMetadata)->shouldBeCalled();
×
83

84
        $validatorPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
85
            $validatorMetadataFactory->reveal(),
×
86
            $decoratedPropertyMetadataFactory->reveal(),
×
87
            []
×
88
        );
×
89

90
        $this->assertEquals(
×
91
            $dummyPropertyMetadata->withRequired(true),
×
92
            $validatorPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummy'),
×
93
        );
×
94

95
        $this->assertEquals(
×
96
            $emailPropertyMetadata->withRequired(false),
×
97
            $validatorPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummyEmail'),
×
98
        );
×
99
    }
100

101
    public function testCreateWithPropertyWithNotRequiredConstraints(): void
102
    {
103
        $propertyMetadata = (new ApiProperty())->withDescription('A dummy')->withReadable(true)->withWritable(true);
×
104
        $expectedPropertyMetadata = $propertyMetadata->withRequired(false);
×
105
        $expectedPropertyMetadata = $expectedPropertyMetadata->withTypes(['https://schema.org/Date']);
×
106

107
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
108
        $decoratedPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummyDate', [])->willReturn($propertyMetadata)->shouldBeCalled();
×
109

110
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
111
        $validatorMetadataFactory->getMetadataFor(DummyValidatedEntity::class)->willReturn($this->validatorClassMetadata)->shouldBeCalled();
×
112

113
        $validatorPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
114
            $validatorMetadataFactory->reveal(),
×
115
            $decoratedPropertyMetadataFactory->reveal(),
×
116
            []
×
117
        );
×
118
        $resultedPropertyMetadata = $validatorPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummyDate');
×
119

120
        $this->assertEquals($expectedPropertyMetadata, $resultedPropertyMetadata);
×
121
    }
122

123
    public function testCreateWithPropertyWithoutConstraints(): void
124
    {
125
        $propertyMetadata = (new ApiProperty())->withDescription('A dummy')->withReadable(true)->withWritable(true)->withIdentifier(true);
×
126
        $expectedPropertyMetadata = $propertyMetadata->withRequired(false);
×
127

128
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
129
        $decoratedPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummyId', [])->willReturn($propertyMetadata)->shouldBeCalled();
×
130

131
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
132
        $validatorMetadataFactory->getMetadataFor(DummyValidatedEntity::class)->willReturn($this->validatorClassMetadata)->shouldBeCalled();
×
133

134
        $validatorPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
135
            $validatorMetadataFactory->reveal(),
×
136
            $decoratedPropertyMetadataFactory->reveal(),
×
137
            []
×
138
        );
×
139
        $resultedPropertyMetadata = $validatorPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummyId');
×
140

141
        $this->assertEquals($expectedPropertyMetadata, $resultedPropertyMetadata);
×
142
    }
143

144
    public function testCreateWithPropertyWithRightValidationGroupsAndRequiredConstraints(): void
145
    {
146
        $propertyMetadata = (new ApiProperty())->withDescription('A dummy group')->withReadable(true)->withWritable(true);
×
147
        $expectedPropertyMetadata = $propertyMetadata->withRequired(true);
×
148

149
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
150
        $decoratedPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummyGroup', ['validation_groups' => ['dummy']])->willReturn($propertyMetadata)->shouldBeCalled();
×
151

152
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
153
        $validatorMetadataFactory->getMetadataFor(DummyValidatedEntity::class)->willReturn($this->validatorClassMetadata)->shouldBeCalled();
×
154

155
        $validatorPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
156
            $validatorMetadataFactory->reveal(),
×
157
            $decoratedPropertyMetadataFactory->reveal(),
×
158
            []
×
159
        );
×
160
        $resultedPropertyMetadata = $validatorPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummyGroup', ['validation_groups' => ['dummy']]);
×
161

162
        $this->assertEquals($expectedPropertyMetadata, $resultedPropertyMetadata);
×
163
    }
164

165
    public function testCreateWithPropertyWithBadValidationGroupsAndRequiredConstraints(): void
166
    {
167
        $propertyMetadata = (new ApiProperty())->withDescription('A dummy group')->withReadable(true)->withWritable(true);
×
168
        $expectedPropertyMetadata = $propertyMetadata->withRequired(false);
×
169

170
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
171
        $decoratedPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummyGroup', ['validation_groups' => ['ymmud']])->willReturn($propertyMetadata)->shouldBeCalled();
×
172

173
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
174
        $validatorMetadataFactory->getMetadataFor(DummyValidatedEntity::class)->willReturn($this->validatorClassMetadata)->shouldBeCalled();
×
175

176
        $validatorPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
177
            $validatorMetadataFactory->reveal(),
×
178
            $decoratedPropertyMetadataFactory->reveal(),
×
179
            []
×
180
        );
×
181
        $resultedPropertyMetadata = $validatorPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummyGroup', ['validation_groups' => ['ymmud']]);
×
182

183
        $this->assertEquals($expectedPropertyMetadata, $resultedPropertyMetadata);
×
184
    }
185

186
    public function testCreateWithPropertyWithNonStringValidationGroupsAndRequiredConstraints(): void
187
    {
188
        $propertyMetadata = (new ApiProperty())->withDescription('A dummy group')->withReadable(true)->withWritable(true);
×
189
        $expectedPropertyMetadata = $propertyMetadata->withRequired(false);
×
190

191
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
192
        $decoratedPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummyGroup', ['validation_groups' => [1312]])->willReturn($propertyMetadata)->shouldBeCalled();
×
193

194
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
195
        $validatorMetadataFactory->getMetadataFor(DummyValidatedEntity::class)->willReturn($this->validatorClassMetadata)->shouldBeCalled();
×
196

197
        $validatorPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
198
            $validatorMetadataFactory->reveal(),
×
199
            $decoratedPropertyMetadataFactory->reveal(),
×
200
            []
×
201
        );
×
202
        $resultedPropertyMetadata = $validatorPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummyGroup', ['validation_groups' => [1312]]);
×
203

204
        $this->assertEquals($expectedPropertyMetadata, $resultedPropertyMetadata);
×
205
    }
206

207
    public function testCreateWithRequiredByDecorated(): void
208
    {
209
        $propertyMetadata = (new ApiProperty())->withDescription('A dummy group')->withReadable(true)->withRequired(true)->withTypes(['foo:bar']);
×
210
        $expectedPropertyMetadata = (clone $propertyMetadata)->withTypes(['foo:bar', 'https://schema.org/Date']);
×
211

212
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
213
        $decoratedPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummyDate', [])->willReturn($propertyMetadata)->shouldBeCalled();
×
214

215
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
216
        $validatorMetadataFactory->getMetadataFor(DummyValidatedEntity::class)->willReturn($this->validatorClassMetadata)->shouldBeCalled();
×
217

218
        $validatorPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
219
            $validatorMetadataFactory->reveal(),
×
220
            $decoratedPropertyMetadataFactory->reveal(),
×
221
            []
×
222
        );
×
223
        $resultedPropertyMetadata = $validatorPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummyDate');
×
224

225
        $this->assertEquals($expectedPropertyMetadata, $resultedPropertyMetadata);
×
226
    }
227

228
    public function testCreateWithPropertyWithValidationConstraints(): void
229
    {
230
        $validatorClassMetadata = new ClassMetadata(DummyIriWithValidationEntity::class);
×
231
        (new AttributeLoader())->loadClassMetadata($validatorClassMetadata);
×
232

233
        $types = [
×
234
            'dummyUrl' => 'https://schema.org/url',
×
235
            'dummyEmail' => 'https://schema.org/email',
×
236
            'dummyUuid' => 'https://schema.org/identifier',
×
237
            'dummyCardScheme' => 'https://schema.org/identifier',
×
238
            'dummyBic' => 'https://schema.org/identifier',
×
239
            'dummyIban' => 'https://schema.org/identifier',
×
240
            'dummyDate' => 'https://schema.org/Date',
×
241
            'dummyDateTime' => 'https://schema.org/DateTime',
×
242
            'dummyTime' => 'https://schema.org/Time',
×
243
            'dummyImage' => 'https://schema.org/image',
×
244
            'dummyFile' => 'https://schema.org/MediaObject',
×
245
            'dummyCurrency' => 'https://schema.org/priceCurrency',
×
246
            'dummyIsbn' => 'https://schema.org/isbn',
×
247
            'dummyIssn' => 'https://schema.org/issn',
×
248
        ];
×
249

250
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
251
        foreach ($types as $property => $iri) {
×
252
            $decoratedPropertyMetadataFactory->create(DummyIriWithValidationEntity::class, $property, [])->willReturn(new ApiProperty())->shouldBeCalled();
×
253
        }
254

255
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
256
        $validatorMetadataFactory->getMetadataFor(DummyIriWithValidationEntity::class)->willReturn($validatorClassMetadata)->shouldBeCalled();
×
257

258
        $validatorPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
259
            $validatorMetadataFactory->reveal(),
×
260
            $decoratedPropertyMetadataFactory->reveal(),
×
261
            []
×
262
        );
×
263

264
        foreach ($types as $property => $iri) {
×
265
            $resultedPropertyMetadata = $validatorPropertyMetadataFactory->create(DummyIriWithValidationEntity::class, $property);
×
266
            $this->assertEquals($iri, $resultedPropertyMetadata->getTypes()[0]);
×
267
        }
268
    }
269

270
    public function testCreateWithPropertyLengthRestriction(): void
271
    {
272
        $validatorClassMetadata = new ClassMetadata(DummyValidatedEntity::class);
×
273
        (new AttributeLoader())->loadClassMetadata($validatorClassMetadata);
×
274

275
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
276
        $validatorMetadataFactory->getMetadataFor(DummyValidatedEntity::class)
×
277
            ->willReturn($validatorClassMetadata)
×
278
            ->shouldBeCalled();
×
279

280
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
281
        $property = 'dummy';
×
282
        $decoratedPropertyMetadataFactory->create(DummyValidatedEntity::class, $property, [])->willReturn(
×
283
            (new ApiProperty())->withNativeType(Type::string())
×
284
        )->shouldBeCalled();
×
285

286
        $lengthRestrictions = new PropertySchemaLengthRestriction();
×
287
        $validatorPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
288
            $validatorMetadataFactory->reveal(),
×
289
            $decoratedPropertyMetadataFactory->reveal(),
×
290
            [$lengthRestrictions]
×
291
        );
×
292

293
        $schema = $validatorPropertyMetadataFactory->create(DummyValidatedEntity::class, $property)->getSchema();
×
294
        $this->assertNotNull($schema);
×
295
        $this->assertArrayHasKey('minLength', $schema);
×
296
        $this->assertArrayHasKey('maxLength', $schema);
×
297
    }
298

299
    public function testCreateWithPropertyRegexRestriction(): void
300
    {
301
        $validatorClassMetadata = new ClassMetadata(DummyValidatedEntity::class);
×
302
        (new AttributeLoader())->loadClassMetadata($validatorClassMetadata);
×
303

304
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
305
        $validatorMetadataFactory->getMetadataFor(DummyValidatedEntity::class)
×
306
            ->willReturn($validatorClassMetadata)
×
307
            ->shouldBeCalled();
×
308

309
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
310
        $decoratedPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummy', [])->willReturn(
×
311
            new ApiProperty()
×
312
        )->shouldBeCalled();
×
313

314
        $validationPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
315
            $validatorMetadataFactory->reveal(),
×
316
            $decoratedPropertyMetadataFactory->reveal(),
×
317
            [new PropertySchemaRegexRestriction()]
×
318
        );
×
319

320
        $schema = $validationPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummy')->getSchema();
×
321
        $this->assertNotNull($schema);
×
322
        $this->assertArrayHasKey('pattern', $schema);
×
323
        $this->assertEquals('^(dummy)$', $schema['pattern']);
×
324
    }
325

326
    #[DataProvider('providePropertySchemaFormatCases')]
327
    public function testCreateWithPropertyFormatRestriction(string $property, string $class, array $expectedSchema): void
328
    {
329
        $validatorClassMetadata = new ClassMetadata($class);
×
330
        (new AttributeLoader())->loadClassMetadata($validatorClassMetadata);
×
331

332
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
333
        $validatorMetadataFactory->getMetadataFor($class)
×
334
            ->willReturn($validatorClassMetadata)
×
335
            ->shouldBeCalled();
×
336

337
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
338
        $decoratedPropertyMetadataFactory->create($class, $property, [])->willReturn(
×
339
            new ApiProperty()
×
340
        )->shouldBeCalled();
×
341
        $validationPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
342
            $validatorMetadataFactory->reveal(),
×
343
            $decoratedPropertyMetadataFactory->reveal(),
×
344
            [new PropertySchemaFormat()]
×
345
        );
×
346
        $schema = $validationPropertyMetadataFactory->create($class, $property)->getSchema();
×
347

348
        $this->assertEquals($expectedSchema, $schema);
×
349
    }
350

351
    public static function providePropertySchemaFormatCases(): \Generator
352
    {
353
        yield ['dummyEmail', DummyValidatedEntity::class, ['format' => 'email']];
×
354
        yield ['dummyUuid', DummyValidatedEntity::class, ['format' => 'uuid']];
×
355
        yield ['dummyIpv4', DummyValidatedEntity::class, ['format' => 'ipv4']];
×
356
        yield ['dummyIpv6', DummyValidatedEntity::class, ['format' => 'ipv6']];
×
357
        yield ['dummyUrl', DummyValidatedEntity::class, ['format' => 'uri']];
×
358
        if (class_exists(Ulid::class)) {
×
359
            yield ['dummyUlid', DummyValidatedUlidEntity::class, ['format' => 'ulid']];
×
360
        }
361
        if (class_exists(Hostname::class)) {
×
362
            yield ['dummyHostname', DummyValidatedHostnameEntity::class, ['format' => 'hostname']];
×
363
        }
364
    }
365

366
    public function testCreateWithSequentiallyConstraint(): void
367
    {
368
        $validatorClassMetadata = new ClassMetadata(DummySequentiallyValidatedEntity::class);
×
369
        (new AttributeLoader())->loadClassMetadata($validatorClassMetadata);
×
370

371
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
372
        $validatorMetadataFactory->getMetadataFor(DummySequentiallyValidatedEntity::class)
×
373
            ->willReturn($validatorClassMetadata)
×
374
            ->shouldBeCalled();
×
375

376
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
377
        $decoratedPropertyMetadataFactory->create(DummySequentiallyValidatedEntity::class, 'dummy', [])->willReturn(
×
378
            (new ApiProperty())->withNativeType(Type::string())
×
379
        )->shouldBeCalled();
×
380
        $validationPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
381
            $validatorMetadataFactory->reveal(),
×
382
            $decoratedPropertyMetadataFactory->reveal(),
×
383
            [new PropertySchemaLengthRestriction(), new PropertySchemaRegexRestriction()]
×
384
        );
×
385
        $schema = $validationPropertyMetadataFactory->create(DummySequentiallyValidatedEntity::class, 'dummy')->getSchema();
×
386

387
        $this->assertNotNull($schema);
×
388
        $this->assertArrayHasKey('minLength', $schema);
×
389
        $this->assertArrayHasKey('maxLength', $schema);
×
390
        $this->assertArrayHasKey('pattern', $schema);
×
391
    }
392

393
    public function testCreateWithCompoundConstraint(): void
394
    {
395
        $validatorClassMetadata = new ClassMetadata(DummyCompoundValidatedEntity::class);
×
396
        (new AttributeLoader())->loadClassMetadata($validatorClassMetadata);
×
397

398
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
399
        $validatorMetadataFactory->getMetadataFor(DummyCompoundValidatedEntity::class)
×
400
            ->willReturn($validatorClassMetadata)
×
401
            ->shouldBeCalled();
×
402

403
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
404
        $decoratedPropertyMetadataFactory->create(DummyCompoundValidatedEntity::class, 'dummy', [])->willReturn(
×
405
            (new ApiProperty())->withNativeType(Type::string())
×
406
        )->shouldBeCalled();
×
407
        $validationPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
408
            $validatorMetadataFactory->reveal(),
×
409
            $decoratedPropertyMetadataFactory->reveal(),
×
410
            [new PropertySchemaLengthRestriction(), new PropertySchemaRegexRestriction()]
×
411
        );
×
412
        $schema = $validationPropertyMetadataFactory->create(DummyCompoundValidatedEntity::class, 'dummy')->getSchema();
×
413

414
        $this->assertNotNull($schema);
×
415
        $this->assertArrayHasKey('minLength', $schema);
×
416
        $this->assertArrayHasKey('maxLength', $schema);
×
417
        $this->assertArrayHasKey('pattern', $schema);
×
418
    }
419

420
    public function testCreateWithAtLeastOneOfConstraint(): void
421
    {
422
        $validatorClassMetadata = new ClassMetadata(DummyAtLeastOneOfValidatedEntity::class);
×
423
        (new AttributeLoader())->loadClassMetadata($validatorClassMetadata);
×
424

425
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
426
        $validatorMetadataFactory->getMetadataFor(DummyAtLeastOneOfValidatedEntity::class)
×
427
            ->willReturn($validatorClassMetadata)
×
428
            ->shouldBeCalled();
×
429

430
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
431
        $decoratedPropertyMetadataFactory->create(DummyAtLeastOneOfValidatedEntity::class, 'dummy', [])->willReturn(
×
432
            (new ApiProperty())->withNativeType(Type::string())
×
433
        )->shouldBeCalled();
×
434
        $restrictionsMetadata = [new PropertySchemaLengthRestriction(), new PropertySchemaRegexRestriction()];
×
435
        $restrictionsMetadata = [new PropertySchemaOneOfRestriction($restrictionsMetadata), new PropertySchemaLengthRestriction(), new PropertySchemaRegexRestriction()];
×
436
        $validationPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
437
            $validatorMetadataFactory->reveal(),
×
438
            $decoratedPropertyMetadataFactory->reveal(),
×
439
            $restrictionsMetadata
×
440
        );
×
441
        $schema = $validationPropertyMetadataFactory->create(DummyAtLeastOneOfValidatedEntity::class, 'dummy')->getSchema();
×
442

443
        $this->assertNotNull($schema);
×
444
        $this->assertArrayHasKey('oneOf', $schema);
×
445
        $this->assertEquals([
×
446
            ['pattern' => '^(.*#.*)$'],
×
447
            ['minLength' => 10],
×
448
        ], $schema['oneOf']);
×
449
    }
450

451
    public function testCreateWithPropertyUniqueRestriction(): void
452
    {
453
        $validatorClassMetadata = new ClassMetadata(DummyUniqueValidatedEntity::class);
×
454
        (new AttributeLoader())->loadClassMetadata($validatorClassMetadata);
×
455

456
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
457
        $validatorMetadataFactory->getMetadataFor(DummyUniqueValidatedEntity::class)
×
458
            ->willReturn($validatorClassMetadata)
×
459
            ->shouldBeCalled();
×
460

461
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
462
        $decoratedPropertyMetadataFactory->create(DummyUniqueValidatedEntity::class, 'dummyItems', [])->willReturn(
×
463
            new ApiProperty()
×
464
        )->shouldBeCalled();
×
465

466
        $validationPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
467
            $validatorMetadataFactory->reveal(),
×
468
            $decoratedPropertyMetadataFactory->reveal(),
×
469
            [new PropertySchemaUniqueRestriction()]
×
470
        );
×
471

472
        $schema = $validationPropertyMetadataFactory->create(DummyUniqueValidatedEntity::class, 'dummyItems')->getSchema();
×
473

474
        $this->assertEquals(['uniqueItems' => true], $schema);
×
475
    }
476

477
    #[IgnoreDeprecations]
478
    public function testLegacyCreateWithRangeConstraint(): void
479
    {
NEW
480
        if (!class_exists(LegacyType::class)) {
×
NEW
481
            $this->markTestSkipped('symfony/property-info is not installed.');
×
482
        }
483

NEW
484
        $cases = [
×
NEW
485
            'min int' => ['type' => new LegacyType(LegacyType::BUILTIN_TYPE_INT), 'property' => 'dummyIntMin', 'expectedSchema' => ['minimum' => 1]],
×
NEW
486
            'max int' => ['type' => new LegacyType(LegacyType::BUILTIN_TYPE_INT), 'property' => 'dummyIntMax', 'expectedSchema' => ['maximum' => 10]],
×
NEW
487
            'min/max int' => ['type' => new LegacyType(LegacyType::BUILTIN_TYPE_INT), 'property' => 'dummyIntMinMax', 'expectedSchema' => ['minimum' => 1, 'maximum' => 10]],
×
NEW
488
            'min float' => ['type' => new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT), 'property' => 'dummyFloatMin', 'expectedSchema' => ['minimum' => 1.5]],
×
NEW
489
            'max float' => ['type' => new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT), 'property' => 'dummyFloatMax', 'expectedSchema' => ['maximum' => 10.5]],
×
NEW
490
            'min/max float' => ['type' => new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT), 'property' => 'dummyFloatMinMax', 'expectedSchema' => ['minimum' => 1.5, 'maximum' => 10.5]],
×
NEW
491
        ];
×
492

NEW
493
        foreach ($cases as ['type' => $type, 'property' => $property, 'expectedSchema' => $expectedSchema]) {
×
494
            $validatorClassMetadata = new ClassMetadata(DummyRangeValidatedEntity::class);
×
495
            (new AttributeLoader())->loadClassMetadata($validatorClassMetadata);
×
496

497
            $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
498
            $validatorMetadataFactory->getMetadataFor(DummyRangeValidatedEntity::class)
×
499
                ->willReturn($validatorClassMetadata)
×
500
                ->shouldBeCalled();
×
501

502
            $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
503
            $decoratedPropertyMetadataFactory->create(DummyRangeValidatedEntity::class, $property, [])->willReturn(
×
504
                (new ApiProperty())->withBuiltinTypes([$type])
×
505
            )->shouldBeCalled();
×
506
            $validationPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
507
                $validatorMetadataFactory->reveal(),
×
508
                $decoratedPropertyMetadataFactory->reveal(),
×
509
                [new PropertySchemaRangeRestriction()]
×
510
            );
×
511
            $schema = $validationPropertyMetadataFactory->create(DummyRangeValidatedEntity::class, $property)->getSchema();
×
512

513
            $this->assertEquals($expectedSchema, $schema);
×
514
        }
515
    }
516

517
    #[DataProvider('provideRangeConstraintCasesWithNativeType')]
518
    public function testCreateWithRangeConstraintWithNativeType(Type $type, string $property, array $expectedSchema): void // Use new Type
519
    {
520
        $validatorClassMetadata = new ClassMetadata(DummyRangeValidatedEntity::class);
×
521
        (new AttributeLoader())->loadClassMetadata($validatorClassMetadata);
×
522

523
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
524
        $validatorMetadataFactory->getMetadataFor(DummyRangeValidatedEntity::class)
×
525
            ->willReturn($validatorClassMetadata)
×
526
            ->shouldBeCalled();
×
527

528
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
529
        $decoratedPropertyMetadataFactory->create(DummyRangeValidatedEntity::class, $property, [])->willReturn(
×
530
            (new ApiProperty())->withNativeType($type) // Use withNativeType and new Type
×
531
        )->shouldBeCalled();
×
532
        $validationPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
533
            $validatorMetadataFactory->reveal(),
×
534
            $decoratedPropertyMetadataFactory->reveal(),
×
535
            [new PropertySchemaRangeRestriction()]
×
536
        );
×
537
        $schema = $validationPropertyMetadataFactory->create(DummyRangeValidatedEntity::class, $property)->getSchema();
×
538

539
        $this->assertEquals($expectedSchema, $schema);
×
540
    }
541

542
    public static function provideRangeConstraintCasesWithNativeType(): \Generator
543
    {
544
        yield 'native type: min int' => ['type' => Type::int(), 'property' => 'dummyIntMin', 'expectedSchema' => ['minimum' => 1]];
×
545
        yield 'native type: max int' => ['type' => Type::int(), 'property' => 'dummyIntMax', 'expectedSchema' => ['maximum' => 10]];
×
546
        yield 'native type: min/max int' => ['type' => Type::int(), 'property' => 'dummyIntMinMax', 'expectedSchema' => ['minimum' => 1, 'maximum' => 10]];
×
547
        yield 'native type: min float' => ['type' => Type::float(), 'property' => 'dummyFloatMin', 'expectedSchema' => ['minimum' => 1.5]];
×
548
        yield 'native type: max float' => ['type' => Type::float(), 'property' => 'dummyFloatMax', 'expectedSchema' => ['maximum' => 10.5]];
×
549
        yield 'native type: min/max float' => ['type' => Type::float(), 'property' => 'dummyFloatMinMax', 'expectedSchema' => ['minimum' => 1.5, 'maximum' => 10.5]];
×
550
    }
551

552
    #[IgnoreDeprecations]
553
    public function testCreateWithPropertyChoiceRestriction(): void
554
    {
NEW
555
        if (!class_exists(LegacyType::class)) {
×
NEW
556
            $this->markTestSkipped('symfony/property-info is not installed.');
×
557
        }
558

NEW
559
        $cases = [
×
NEW
560
            'single choice' => ['propertyMetadata' => (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)]), 'property' => 'dummySingleChoice', 'expectedSchema' => ['enum' => ['a', 'b']]],
×
NEW
561
            'single choice callback' => ['propertyMetadata' => (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)]), 'property' => 'dummySingleChoiceCallback', 'expectedSchema' => ['enum' => ['a', 'b', 'c', 'd']]],
×
NEW
562
            'multi choice' => ['propertyMetadata' => (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)]), 'property' => 'dummyMultiChoice', 'expectedSchema' => ['type' => 'array', 'items' => ['type' => 'string', 'enum' => ['a', 'b']]]],
×
NEW
563
            'multi choice callback' => ['propertyMetadata' => (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)]), 'property' => 'dummyMultiChoiceCallback', 'expectedSchema' => ['type' => 'array', 'items' => ['type' => 'string', 'enum' => ['a', 'b', 'c', 'd']]]],
×
NEW
564
            'multi choice min' => ['propertyMetadata' => (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)]), 'property' => 'dummyMultiChoiceMin', 'expectedSchema' => ['type' => 'array', 'items' => ['type' => 'string', 'enum' => ['a', 'b', 'c', 'd']], 'minItems' => 2]],
×
NEW
565
            'multi choice max' => ['propertyMetadata' => (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)]), 'property' => 'dummyMultiChoiceMax', 'expectedSchema' => ['type' => 'array', 'items' => ['type' => 'string', 'enum' => ['a', 'b', 'c', 'd']], 'maxItems' => 4]],
×
NEW
566
            'multi choice min/max' => ['propertyMetadata' => (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)]), 'property' => 'dummyMultiChoiceMinMax', 'expectedSchema' => ['type' => 'array', 'items' => ['type' => 'string', 'enum' => ['a', 'b', 'c', 'd']], 'minItems' => 2, 'maxItems' => 4]],
×
NEW
567
        ];
×
568

NEW
569
        foreach ($cases as ['propertyMetadata' => $propertyMetadata, 'property' => $property, 'expectedSchema' => $expectedSchema]) {
×
570
            $validatorClassMetadata = new ClassMetadata(DummyValidatedChoiceEntity::class);
×
571
            (new AttributeLoader())->loadClassMetadata($validatorClassMetadata);
×
572

573
            $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
574
            $validatorMetadataFactory->getMetadataFor(DummyValidatedChoiceEntity::class)
×
575
                ->willReturn($validatorClassMetadata)
×
576
                ->shouldBeCalled();
×
577

578
            $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
579
            $decoratedPropertyMetadataFactory->create(DummyValidatedChoiceEntity::class, $property, [])->willReturn(
×
580
                $propertyMetadata
×
581
            )->shouldBeCalled();
×
582

583
            $validationPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
584
                $validatorMetadataFactory->reveal(),
×
585
                $decoratedPropertyMetadataFactory->reveal(),
×
586
                [new PropertySchemaChoiceRestriction()]
×
587
            );
×
588

589
            $schema = $validationPropertyMetadataFactory->create(DummyValidatedChoiceEntity::class, $property)->getSchema();
×
590

591
            $this->assertEquals($expectedSchema, $schema);
×
592
        }
593
    }
594

595
    #[DataProvider('provideChoiceConstraintCasesWithNativeType')]
596
    public function testCreateWithPropertyChoiceRestrictionWithNativeType(ApiProperty $propertyMetadata, string $property, array $expectedSchema): void
597
    {
598
        $validatorClassMetadata = new ClassMetadata(DummyValidatedChoiceEntity::class);
×
599
        (new AttributeLoader())->loadClassMetadata($validatorClassMetadata);
×
600

601
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
602
        $validatorMetadataFactory->getMetadataFor(DummyValidatedChoiceEntity::class)
×
603
            ->willReturn($validatorClassMetadata)
×
604
            ->shouldBeCalled();
×
605

606
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
607
        $decoratedPropertyMetadataFactory->create(DummyValidatedChoiceEntity::class, $property, [])->willReturn(
×
608
            $propertyMetadata // Provider now sends ApiProperty configured with withNativeType
×
609
        )->shouldBeCalled();
×
610

611
        $validationPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
612
            $validatorMetadataFactory->reveal(),
×
613
            $decoratedPropertyMetadataFactory->reveal(),
×
614
            [new PropertySchemaChoiceRestriction()]
×
615
        );
×
616

617
        $schema = $validationPropertyMetadataFactory->create(DummyValidatedChoiceEntity::class, $property)->getSchema();
×
618

619
        $this->assertEquals($expectedSchema, $schema);
×
620
    }
621

622
    public static function provideChoiceConstraintCasesWithNativeType(): \Generator
623
    {
624
        yield 'native type: single choice' => ['propertyMetadata' => (new ApiProperty())->withNativeType(Type::string()), 'property' => 'dummySingleChoice', 'expectedSchema' => ['enum' => ['a', 'b']]];
×
625
        yield 'native type: single choice callback' => ['propertyMetadata' => (new ApiProperty())->withNativeType(Type::string()), 'property' => 'dummySingleChoiceCallback', 'expectedSchema' => ['enum' => ['a', 'b', 'c', 'd']]];
×
626
        yield 'native type: multi choice' => ['propertyMetadata' => (new ApiProperty())->withNativeType(Type::string()), 'property' => 'dummyMultiChoice', 'expectedSchema' => ['type' => 'array', 'items' => ['type' => 'string', 'enum' => ['a', 'b']]]];
×
627
        yield 'native type: multi choice callback' => ['propertyMetadata' => (new ApiProperty())->withNativeType(Type::string()), 'property' => 'dummyMultiChoiceCallback', 'expectedSchema' => ['type' => 'array', 'items' => ['type' => 'string', 'enum' => ['a', 'b', 'c', 'd']]]];
×
628
        yield 'native type: multi choice min' => ['propertyMetadata' => (new ApiProperty())->withNativeType(Type::string()), 'property' => 'dummyMultiChoiceMin', 'expectedSchema' => ['type' => 'array', 'items' => ['type' => 'string', 'enum' => ['a', 'b', 'c', 'd']], 'minItems' => 2]];
×
629
        yield 'native type: multi choice max' => ['propertyMetadata' => (new ApiProperty())->withNativeType(Type::string()), 'property' => 'dummyMultiChoiceMax', 'expectedSchema' => ['type' => 'array', 'items' => ['type' => 'string', 'enum' => ['a', 'b', 'c', 'd']], 'maxItems' => 4]];
×
630
        yield 'native type: multi choice min/max' => ['propertyMetadata' => (new ApiProperty())->withNativeType(Type::string()), 'property' => 'dummyMultiChoiceMinMax', 'expectedSchema' => ['type' => 'array', 'items' => ['type' => 'string', 'enum' => ['a', 'b', 'c', 'd']], 'minItems' => 2, 'maxItems' => 4]];
×
631
    }
632

633
    #[DataProvider('provideCountConstraintCases')]
634
    public function testCreateWithPropertyCountRestriction(string $property, array $expectedSchema): void
635
    {
636
        $validatorClassMetadata = new ClassMetadata(DummyCountValidatedEntity::class);
×
637
        (new AttributeLoader())->loadClassMetadata($validatorClassMetadata);
×
638

639
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
640
        $validatorMetadataFactory->getMetadataFor(DummyCountValidatedEntity::class)
×
641
            ->willReturn($validatorClassMetadata)
×
642
            ->shouldBeCalled();
×
643

644
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
645
        $decoratedPropertyMetadataFactory->create(DummyCountValidatedEntity::class, $property, [])->willReturn(
×
646
            new ApiProperty()
×
647
        )->shouldBeCalled();
×
648

649
        $validationPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
650
            $validatorMetadataFactory->reveal(),
×
651
            $decoratedPropertyMetadataFactory->reveal(),
×
652
            [new PropertySchemaCountRestriction()]
×
653
        );
×
654

655
        $schema = $validationPropertyMetadataFactory->create(DummyCountValidatedEntity::class, $property)->getSchema();
×
656

657
        $this->assertEquals($expectedSchema, $schema);
×
658
    }
659

660
    public static function provideCountConstraintCases(): \Generator
661
    {
662
        yield 'min' => ['property' => 'dummyMin', 'expectedSchema' => ['minItems' => 1]];
×
663
        yield 'max' => ['property' => 'dummyMax', 'expectedSchema' => ['maxItems' => 10]];
×
664
        yield 'min/max' => ['property' => 'dummyMinMax', 'expectedSchema' => ['minItems' => 1, 'maxItems' => 10]];
×
665
    }
666

667
    public function testCreateWithPropertyCollectionRestriction(): void
668
    {
669
        $validatorClassMetadata = new ClassMetadata(DummyCollectionValidatedEntity::class);
×
670
        (new AttributeLoader())->loadClassMetadata($validatorClassMetadata);
×
671

672
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
673
        $validatorMetadataFactory->getMetadataFor(DummyCollectionValidatedEntity::class)
×
674
            ->willReturn($validatorClassMetadata)
×
675
            ->shouldBeCalled();
×
676

677
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
678
        $decoratedPropertyMetadataFactory->create(DummyCollectionValidatedEntity::class, 'dummyData', [])->willReturn(
×
679
            (new ApiProperty())->withNativeType(Type::list())
×
680
        )->shouldBeCalled();
×
681

682
        $greaterThanRestriction = new PropertySchemaGreaterThanRestriction();
×
683
        $lengthRestriction = new PropertySchemaLengthRestriction();
×
684
        $regexRestriction = new PropertySchemaRegexRestriction();
×
685
        $formatRestriction = new PropertySchemaFormat();
×
686
        $restrictionsMetadata = [
×
687
            $greaterThanRestriction,
×
688
            $lengthRestriction,
×
689
            $regexRestriction,
×
690
            $formatRestriction,
×
691
            new PropertySchemaCollectionRestriction([
×
692
                $greaterThanRestriction,
×
693
                $lengthRestriction,
×
694
                $regexRestriction,
×
695
                $formatRestriction,
×
696
                new PropertySchemaCollectionRestriction([
×
697
                    $greaterThanRestriction,
×
698
                    $lengthRestriction,
×
699
                    $regexRestriction,
×
700
                    $formatRestriction,
×
701
                ]),
×
702
            ]),
×
703
        ];
×
704

705
        $validationPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
706
            $validatorMetadataFactory->reveal(),
×
707
            $decoratedPropertyMetadataFactory->reveal(),
×
708
            $restrictionsMetadata
×
709
        );
×
710

711
        $schema = $validationPropertyMetadataFactory->create(DummyCollectionValidatedEntity::class, 'dummyData')->getSchema();
×
712

713
        $this->assertEquals([
×
714
            'type' => 'object',
×
715
            'properties' => new \ArrayObject([
×
716
                'name' => new \ArrayObject(),
×
717
                'email' => ['format' => 'email', 'minLength' => 2, 'maxLength' => 255],
×
718
                'phone' => ['pattern' => '^([+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\./0-9]*)$'],
×
719
                'age' => [
×
720
                    'exclusiveMinimum' => 0,
×
721
                    'minimum' => 0,
×
722
                ],
×
723
                'social' => [
×
724
                    'type' => 'object',
×
725
                    'properties' => new \ArrayObject([
×
726
                        'githubUsername' => new \ArrayObject(),
×
727
                    ]),
×
728
                    'additionalProperties' => false,
×
729
                    'required' => ['githubUsername'],
×
730
                ],
×
731
            ]),
×
732
            'additionalProperties' => true,
×
733
            'required' => ['name', 'email', 'social'],
×
734
        ], $schema);
×
735
    }
736

737
    #[IgnoreDeprecations]
738
    public function testCreateWithPropertyNumericRestriction(): void
739
    {
NEW
740
        if (!class_exists(LegacyType::class)) {
×
NEW
741
            $this->markTestSkipped('symfony/property-info is not installed.');
×
742
        }
743

NEW
744
        $cases = [
×
NEW
745
            [
×
NEW
746
                'propertyMetadata' => (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_INT)]),
×
NEW
747
                'property' => 'greaterThanMe',
×
NEW
748
                'expectedSchema' => ['exclusiveMinimum' => 10, 'minimum' => 10],
×
NEW
749
            ],
×
NEW
750
            [
×
NEW
751
                'propertyMetadata' => (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)]),
×
NEW
752
                'property' => 'greaterThanOrEqualToMe',
×
NEW
753
                'expectedSchema' => ['minimum' => 10.99],
×
NEW
754
            ],
×
NEW
755
            [
×
NEW
756
                'propertyMetadata' => (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_INT)]),
×
NEW
757
                'property' => 'lessThanMe',
×
NEW
758
                'expectedSchema' => ['exclusiveMaximum' => 99, 'maximum' => 99],
×
NEW
759
            ],
×
NEW
760
            [
×
NEW
761
                'propertyMetadata' => (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)]),
×
NEW
762
                'property' => 'lessThanOrEqualToMe',
×
NEW
763
                'expectedSchema' => ['maximum' => 99.33],
×
NEW
764
            ],
×
NEW
765
            [
×
NEW
766
                'propertyMetadata' => (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_INT)]),
×
NEW
767
                'property' => 'positive',
×
NEW
768
                'expectedSchema' => ['exclusiveMinimum' => 0, 'minimum' => 0],
×
NEW
769
            ],
×
NEW
770
            [
×
NEW
771
                'propertyMetadata' => (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_INT)]),
×
NEW
772
                'property' => 'positiveOrZero',
×
NEW
773
                'expectedSchema' => ['minimum' => 0],
×
NEW
774
            ],
×
NEW
775
            [
×
NEW
776
                'propertyMetadata' => (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_INT)]),
×
NEW
777
                'property' => 'negative',
×
NEW
778
                'expectedSchema' => ['exclusiveMaximum' => 0, 'maximum' => 0],
×
NEW
779
            ],
×
NEW
780
            [
×
NEW
781
                'propertyMetadata' => (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_INT)]),
×
NEW
782
                'property' => 'negativeOrZero',
×
NEW
783
                'expectedSchema' => ['maximum' => 0],
×
NEW
784
            ],
×
NEW
785
        ];
×
786

NEW
787
        foreach ($cases as ['propertyMetadata' => $propertyMetadata, 'property' => $property, 'expectedSchema' => $expectedSchema]) {
×
788
            $validatorClassMetadata = new ClassMetadata(DummyNumericValidatedEntity::class);
×
789
            (new AttributeLoader())->loadClassMetadata($validatorClassMetadata);
×
790

791
            $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
792
            $validatorMetadataFactory->getMetadataFor(DummyNumericValidatedEntity::class)
×
793
                ->willReturn($validatorClassMetadata)
×
794
                ->shouldBeCalled();
×
795

796
            $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
797
            $decoratedPropertyMetadataFactory->create(DummyNumericValidatedEntity::class, $property, [])->willReturn(
×
798
                $propertyMetadata
×
799
            )->shouldBeCalled();
×
800

801
            $validationPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
802
                $validatorMetadataFactory->reveal(),
×
803
                $decoratedPropertyMetadataFactory->reveal(),
×
804
                [
×
805
                    new PropertySchemaGreaterThanOrEqualRestriction(),
×
806
                    new PropertySchemaGreaterThanRestriction(),
×
807
                    new PropertySchemaLessThanOrEqualRestriction(),
×
808
                    new PropertySchemaLessThanRestriction(),
×
809
                ]
×
810
            );
×
811

812
            $schema = $validationPropertyMetadataFactory->create(DummyNumericValidatedEntity::class, $property)->getSchema();
×
813

814
            $this->assertEquals($expectedSchema, $schema);
×
815
        }
816
    }
817

818
    #[DataProvider('provideNumericConstraintCasesWithNativeType')]
819
    public function testCreateWithPropertyNumericRestrictionWithNativeType(ApiProperty $propertyMetadata, string $property, array $expectedSchema): void
820
    {
821
        $validatorClassMetadata = new ClassMetadata(DummyNumericValidatedEntity::class);
×
822
        (new AttributeLoader())->loadClassMetadata($validatorClassMetadata);
×
823

824
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
825
        $validatorMetadataFactory->getMetadataFor(DummyNumericValidatedEntity::class)
×
826
            ->willReturn($validatorClassMetadata)
×
827
            ->shouldBeCalled();
×
828

829
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
830
        $decoratedPropertyMetadataFactory->create(DummyNumericValidatedEntity::class, $property, [])->willReturn(
×
831
            $propertyMetadata
×
832
        )->shouldBeCalled();
×
833

834
        $validationPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
835
            $validatorMetadataFactory->reveal(),
×
836
            $decoratedPropertyMetadataFactory->reveal(),
×
837
            [
×
838
                new PropertySchemaGreaterThanOrEqualRestriction(),
×
839
                new PropertySchemaGreaterThanRestriction(),
×
840
                new PropertySchemaLessThanOrEqualRestriction(),
×
841
                new PropertySchemaLessThanRestriction(),
×
842
            ]
×
843
        );
×
844

845
        $schema = $validationPropertyMetadataFactory->create(DummyNumericValidatedEntity::class, $property)->getSchema();
×
846

847
        $this->assertEquals($expectedSchema, $schema);
×
848
    }
849

850
    public static function provideNumericConstraintCasesWithNativeType(): \Generator
851
    {
852
        yield [
×
853
            'propertyMetadata' => (new ApiProperty())->withNativeType(Type::int()),
×
854
            'property' => 'greaterThanMe',
×
855
            'expectedSchema' => ['exclusiveMinimum' => 10, 'minimum' => 10],
×
856
        ];
×
857

858
        yield [
×
859
            'propertyMetadata' => (new ApiProperty())->withNativeType(Type::float()),
×
860
            'property' => 'greaterThanOrEqualToMe',
×
861
            'expectedSchema' => ['minimum' => 10.99],
×
862
        ];
×
863

864
        yield [
×
865
            'propertyMetadata' => (new ApiProperty())->withNativeType(Type::int()),
×
866
            'property' => 'lessThanMe',
×
867
            'expectedSchema' => ['exclusiveMaximum' => 99, 'maximum' => 99],
×
868
        ];
×
869

870
        yield [
×
871
            'propertyMetadata' => (new ApiProperty())->withNativeType(Type::float()),
×
872
            'property' => 'lessThanOrEqualToMe',
×
873
            'expectedSchema' => ['maximum' => 99.33],
×
874
        ];
×
875

876
        yield [
×
877
            'propertyMetadata' => (new ApiProperty())->withNativeType(Type::int()),
×
878
            'property' => 'positive',
×
879
            'expectedSchema' => ['exclusiveMinimum' => 0, 'minimum' => 0],
×
880
        ];
×
881

882
        yield [
×
883
            'propertyMetadata' => (new ApiProperty())->withNativeType(Type::int()),
×
884
            'property' => 'positiveOrZero',
×
885
            'expectedSchema' => ['minimum' => 0],
×
886
        ];
×
887

888
        yield [
×
889
            'propertyMetadata' => (new ApiProperty())->withNativeType(Type::int()),
×
890
            'property' => 'negative',
×
891
            'expectedSchema' => ['exclusiveMaximum' => 0, 'maximum' => 0],
×
892
        ];
×
893

894
        yield [
×
895
            'propertyMetadata' => (new ApiProperty())->withNativeType(Type::int()),
×
896
            'property' => 'negativeOrZero',
×
897
            'expectedSchema' => ['maximum' => 0],
×
898
        ];
×
899
    }
900

901
    public function testCallableGroup(): void
902
    {
903
        $propertyMetadata = (new ApiProperty())->withDescription('A dummy group')->withReadable(true)->withWritable(true);
×
904

905
        $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
906
        $decoratedPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummyGroup', ['validation_groups' => [DummyValidatedEntity::class, 'getValidationGroups']])->willReturn($propertyMetadata)->shouldBeCalled();
×
907

908
        $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
×
909
        $validatorMetadataFactory->getMetadataFor(DummyValidatedEntity::class)->willReturn($this->validatorClassMetadata)->shouldBeCalled();
×
910

911
        $validatorPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
×
912
            $validatorMetadataFactory->reveal(),
×
913
            $decoratedPropertyMetadataFactory->reveal(),
×
914
            []
×
915
        );
×
916
        $validatorPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummyGroup', ['validation_groups' => [DummyValidatedEntity::class, 'getValidationGroups']]);
×
917
    }
918
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc