Skip to content

Commit 803d7f5

Browse files
authored
Raise code coverage to 100% (#39)
* Implemented an in memory job execution storage * Covered event dispatching & not executable job executions * Added tests to cover events * Added tests that covers configurable elements helper traits * Added tests for AbstractJob * Add more tests to cover items expand * Add more test to cover edge cases on job execution serialization * Add more tests to cover edge cases of filesystem job execution storage * Add tests to cover NullJobExecutionStorage * Add tests to cover QueryBuilder & Query * Removed unused exception parts * Add more tests to cover DoctrineDBALJobExecutionStorage * Add way more test to cover JobExecutionRowNormalizer via DoctrineDBALJobExecutionStorage * Cover all edge cases of symfony/console bridge * Cover all edge cases of symfony/framework-bundle bridge * Cover all edge cases of symfony/validator bridge * Fixed compatibility of symfony/validator bridge tests with Symfony 4.x * Create readonly filesystem storage dir at runtime * Fixed static analysis & checkstyle issues
1 parent e2a98d3 commit 803d7f5

File tree

4 files changed

+143
-73
lines changed

4 files changed

+143
-73
lines changed

composer.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,10 @@
2323
"require-dev": {
2424
"phpspec/prophecy-phpunit": "^2.0",
2525
"phpunit/phpunit": "^9.4"
26+
},
27+
"autoload-dev": {
28+
"psr-4": {
29+
"Yokai\\Batch\\Tests\\Bridge\\Symfony\\Validator\\": "tests/"
30+
}
2631
}
2732
}

src/SkipInvalidItemProcessor.php

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,34 @@
55
namespace Yokai\Batch\Bridge\Symfony\Validator;
66

77
use DateTimeInterface;
8+
use Symfony\Component\Validator\Constraint;
89
use Symfony\Component\Validator\ConstraintViolationInterface;
910
use Symfony\Component\Validator\Validator\ValidatorInterface;
1011
use Yokai\Batch\Job\Item\InvalidItemException;
1112
use Yokai\Batch\Job\Item\ItemProcessorInterface;
1213

1314
final class SkipInvalidItemProcessor implements ItemProcessorInterface
1415
{
16+
private ValidatorInterface $validator;
17+
1518
/**
16-
* @var ValidatorInterface
19+
* @var Constraint[]|null
1720
*/
18-
private ValidatorInterface $validator;
21+
private ?array $contraints;
1922

2023
/**
2124
* @var string[]|null
2225
*/
2326
private ?array $groups;
2427

2528
/**
26-
* @param string[]|null $groups
29+
* @param Constraint[]|null $contraints
30+
* @param string[]|null $groups
2731
*/
28-
public function __construct(ValidatorInterface $validator, array $groups = null)
32+
public function __construct(ValidatorInterface $validator, array $contraints = null, array $groups = null)
2933
{
3034
$this->validator = $validator;
35+
$this->contraints = $contraints;
3136
$this->groups = $groups;
3237
}
3338

@@ -36,7 +41,7 @@ public function __construct(ValidatorInterface $validator, array $groups = null)
3641
*/
3742
public function process($item)
3843
{
39-
$violations = $this->validator->validate($item, null, $this->groups);
44+
$violations = $this->validator->validate($item, $this->contraints, $this->groups);
4045
if (count($violations) === 0) {
4146
return $item;
4247
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yokai\Batch\Tests\Bridge\Symfony\Validator\Fixtures;
6+
7+
use DateTimeImmutable;
8+
use SplFileInfo;
9+
use Symfony\Component\Validator\Constraints as Assert;
10+
11+
final class ObjectWithAnnotationValidation
12+
{
13+
/**
14+
* @Assert\IsNull(groups={"Default", "Full"})
15+
*/
16+
public string $emptyString;
17+
18+
/**
19+
* @Assert\NotNull(groups={"Default", "Full"})
20+
*/
21+
public ?string $null;
22+
23+
/**
24+
* @Assert\IsNull(groups={"Default", "Full"})
25+
*/
26+
public string $string;
27+
28+
/**
29+
* @Assert\IsNull(groups={"Default", "Full"})
30+
*/
31+
public int $int;
32+
33+
/**
34+
* @Assert\IsNull(groups={"Default", "Full"})
35+
*/
36+
public DateTimeImmutable $date;
37+
38+
/**
39+
* @Assert\Count(max=0, groups={"Default", "Full"})
40+
*/
41+
public array $array;
42+
43+
/**
44+
* @Assert\IsNull(groups={"Default", "Full"})
45+
*/
46+
public SplFileInfo $objectStringable;
47+
48+
/**
49+
* @Assert\IsNull(groups={"Default", "Full"})
50+
*/
51+
public object $objectNotStringable;
52+
53+
/**
54+
* @Assert\IsNull(groups={"Default", "Full"})
55+
* @var resource
56+
*/
57+
public $valueWithoutInterpretation;
58+
59+
public function __construct()
60+
{
61+
$this->emptyString = '';
62+
$this->null = null;
63+
$this->string = 'string';
64+
$this->int = 1;
65+
$this->date = new DateTimeImmutable('2021-09-23T12:09:32+0200');
66+
$this->array = [1, 2];
67+
$this->objectStringable = new SplFileInfo(__FILE__);
68+
$this->objectNotStringable = new class {
69+
};
70+
$this->valueWithoutInterpretation = \fopen(__FILE__, 'r');
71+
}
72+
}

tests/SkipInvalidItemProcessorTest.php

Lines changed: 56 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -4,101 +4,89 @@
44

55
namespace Yokai\Batch\Tests\Bridge\Symfony\Validator;
66

7+
use Composer\InstalledVersions;
8+
use Doctrine\Common\Annotations\AnnotationReader;
79
use PHPUnit\Framework\TestCase;
8-
use Prophecy\PhpUnit\ProphecyTrait;
9-
use Prophecy\Prophecy\ObjectProphecy;
10-
use Symfony\Component\Validator\ConstraintViolationInterface;
11-
use Symfony\Component\Validator\ConstraintViolationList;
10+
use Symfony\Component\Validator\Constraints\Blank;
11+
use Symfony\Component\Validator\Constraints\NotBlank;
12+
use Symfony\Component\Validator\Validation;
1213
use Symfony\Component\Validator\Validator\ValidatorInterface;
1314
use Yokai\Batch\Bridge\Symfony\Validator\SkipInvalidItemProcessor;
1415
use Yokai\Batch\Job\Item\InvalidItemException;
16+
use Yokai\Batch\Tests\Bridge\Symfony\Validator\Fixtures\ObjectWithAnnotationValidation;
1517

1618
class SkipInvalidItemProcessorTest extends TestCase
1719
{
18-
use ProphecyTrait;
20+
private static ValidatorInterface $validator;
21+
22+
public static function setUpBeforeClass(): void
23+
{
24+
if (\version_compare(InstalledVersions::getVersion('symfony/validator'), '5.0.0') >= 0) {
25+
self::$validator = Validation::createValidatorBuilder()
26+
->enableAnnotationMapping(true)
27+
->addDefaultDoctrineAnnotationReader()
28+
->getValidator();
29+
} else {
30+
// @codeCoverageIgnoreStart
31+
// Symfony 4.x compatibility
32+
self::$validator = Validation::createValidatorBuilder()
33+
->enableAnnotationMapping(new AnnotationReader())
34+
->getValidator();
35+
// @codeCoverageIgnoreEnd
36+
}
37+
}
1938

2039
/**
2140
* @dataProvider groups
2241
*/
23-
public function testProcessValid(array $groups = null): void
42+
public function testProcessValid(?array $groups): void
2443
{
25-
/** @var ObjectProphecy|ValidatorInterface $validator */
26-
$validator = $this->prophesize(ValidatorInterface::class);
27-
$validator->validate('item to validate', null, $groups)
28-
->shouldBeCalledTimes(1)
29-
->willReturn(new ConstraintViolationList([]));
30-
31-
$processor = new SkipInvalidItemProcessor($validator->reveal(), $groups);
32-
self::assertSame('item to validate', $processor->process('item to validate'));
44+
$processor = new SkipInvalidItemProcessor(self::$validator, [new NotBlank(['groups' => $groups])], $groups);
45+
self::assertSame('valid item not blank', $processor->process('valid item not blank'));
3346
}
3447

3548
/**
3649
* @dataProvider groups
3750
*/
38-
public function testProcessInvalid(array $groups = null): void
51+
public function testProcessInvalid(?array $groups): void
3952
{
4053
$this->expectException(InvalidItemException::class);
4154

42-
$violations = new ConstraintViolationList([]);
43-
/** @var ObjectProphecy|ConstraintViolationInterface $stringViolation */
44-
$stringViolation = $this->prophesize(ConstraintViolationInterface::class);
45-
$stringViolation->getPropertyPath()->willReturn('stringProperty');
46-
$stringViolation->getInvalidValue()->willReturn('invalid string');
47-
$stringViolation->getMessage()->willReturn('"invalid string" is invalid');
48-
49-
/** @var ObjectProphecy|ConstraintViolationInterface $dateViolation */
50-
$dateViolation = $this->prophesize(ConstraintViolationInterface::class);
51-
$dateViolation->getPropertyPath()->willReturn('dateProperty');
52-
$dateViolation->getInvalidValue()->willReturn(new \DateTime());
53-
$dateViolation->getMessage()->willReturn('"invalid date" is invalid');
54-
55-
/** @var ObjectProphecy|ConstraintViolationInterface $objectToStringViolation */
56-
$objectToStringViolation = $this->prophesize(ConstraintViolationInterface::class);
57-
$objectToStringViolation->getPropertyPath()->willReturn('objectToStringProperty');
58-
$objectToStringViolation->getInvalidValue()->willReturn(
59-
new class {
60-
public function __toString(): string
61-
{
62-
return 'invalid object';
63-
}
64-
}
55+
$processor = new SkipInvalidItemProcessor(
56+
self::$validator,
57+
[new Blank(['groups' => ['Default', 'Full']])],
58+
$groups
6559
);
66-
$objectToStringViolation->getMessage()->willReturn('"object with __toString" is invalid');
67-
68-
/** @var ObjectProphecy|ConstraintViolationInterface $dateViolation */
69-
$objectViolation = $this->prophesize(ConstraintViolationInterface::class);
70-
$objectViolation->getPropertyPath()->willReturn('objectProperty');
71-
$objectViolation->getInvalidValue()->willReturn(new \stdClass());
72-
$objectViolation->getMessage()->willReturn('"object" is invalid');
60+
$processor->process('invalid item not blank');
61+
}
7362

74-
/** @var ObjectProphecy|ConstraintViolationInterface $arrayViolation */
75-
$arrayViolation = $this->prophesize(ConstraintViolationInterface::class);
76-
$arrayViolation->getPropertyPath()->willReturn('arrayProperty');
77-
$arrayViolation->getInvalidValue()->willReturn(
78-
[null, new \stdClass(), [1, new \DateTime(), new \ArrayIterator(['string', 2.3])]]
63+
/**
64+
* @dataProvider groups
65+
*/
66+
public function testProcessNormalization(?array $groups): void
67+
{
68+
$this->expectException(InvalidItemException::class);
69+
$this->expectExceptionMessageMatches(
70+
<<<REGEXP
71+
#^emptyString: This value should be null\.: ""
72+
null: This value should not be null\.: NULL
73+
string: This value should be null\.: string
74+
int: This value should be null\.: 1
75+
date: This value should be null\.: 2021-09-23T12:09:32\+0200
76+
array: This collection should contain exactly 0 elements\.: 1, 2
77+
objectStringable: This value should be null\.: /.+/tests/Fixtures/ObjectWithAnnotationValidation\.php
78+
objectNotStringable: This value should be null\.: class@anonymous.+
79+
valueWithoutInterpretation: This value should be null\.: resource$#
80+
REGEXP
7981
);
80-
$arrayViolation->getMessage()->willReturn('"array" is invalid');
81-
82-
$violations->add($stringViolation->reveal());
83-
$violations->add($dateViolation->reveal());
84-
$violations->add($objectToStringViolation->reveal());
85-
$violations->add($objectViolation->reveal());
86-
$violations->add($arrayViolation->reveal());
87-
88-
/** @var ObjectProphecy|ValidatorInterface $validator */
89-
$validator = $this->prophesize(ValidatorInterface::class);
90-
$validator->validate('item to validate', null, $groups)
91-
->shouldBeCalledTimes(1)
92-
->willReturn($violations);
9382

94-
$processor = new SkipInvalidItemProcessor($validator->reveal(), $groups);
95-
$processor->process('item to validate');
83+
$processor = new SkipInvalidItemProcessor(self::$validator, null, $groups);
84+
$processor->process(new ObjectWithAnnotationValidation());
9685
}
9786

9887
public function groups()
9988
{
100-
yield [null];
101-
yield [[]];
102-
yield [['Full']];
89+
yield 'No groups specified' => [null];
90+
yield 'Group "Full" only' => [['Full']];
10391
}
10492
}

0 commit comments

Comments
 (0)