Skip to content

Commit 32a7539

Browse files
committed
Additional test cases
1 parent 59383d5 commit 32a7539

File tree

6 files changed

+116
-11
lines changed

6 files changed

+116
-11
lines changed

src/Model/RenderJob.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,9 @@ protected function generateModelDirectory(): void
9292
{
9393
$destination = dirname($this->schema->getTargetFileName());
9494
if (!is_dir($destination) && !mkdir($destination, 0777, true)) {
95+
// @codeCoverageIgnoreStart
9596
throw new FileSystemException("Can't create path $destination");
97+
// @codeCoverageIgnoreEnd
9698
}
9799
}
98100

@@ -127,11 +129,13 @@ protected function renderClass(GeneratorConfiguration $generatorConfiguration):
127129
],
128130
);
129131
} catch (PHPMicroTemplateException $exception) {
132+
// @codeCoverageIgnoreStart
130133
throw new RenderException(
131134
"Can't render class {$this->schema->getClassPath()}\\{$this->schema->getClassName()}",
132135
0,
133136
$exception,
134137
);
138+
// @codeCoverageIgnoreEnd
135139
}
136140

137141
return $class;

src/SchemaProcessor/PostProcessor/BuilderClassPostProcessor.php

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,22 @@ class BuilderClassPostProcessor extends PostProcessor
2424
{
2525
/** @var Schema[] */
2626
private array $schemas = [];
27-
private GeneratorConfiguration $generatorConfiguration;
27+
private ?GeneratorConfiguration $generatorConfiguration;
28+
private ?RenderHelper $renderHelper;
2829

2930
public function process(Schema $schema, GeneratorConfiguration $generatorConfiguration): void
3031
{
3132
// collect the schemas and generate builder classes in postProcess hook to make sure the related model class
3233
// already has been created
3334
$this->schemas[] = $schema;
34-
$this->generatorConfiguration = $generatorConfiguration;
35+
$this->generatorConfiguration ??= $generatorConfiguration;
36+
$this->renderHelper ??= new RenderHelper($generatorConfiguration);
37+
}
38+
39+
public function preProcess(): void
40+
{
41+
$this->generatorConfiguration = null;
42+
$this->renderHelper = null;
3543
}
3644

3745
public function postProcess(): void
@@ -107,9 +115,12 @@ private function getBuilderClassImports(array $properties, array $originalClassI
107115

108116
foreach ($properties as $property) {
109117
// use typehint instead of type to cover multi-types
110-
foreach (array_unique(
111-
[...explode('|', $property->getTypeHint()), ...explode('|', $property->getTypeHint(true))]
112-
) as $type) {
118+
foreach (array_unique([
119+
...explode('|', $this->renderHelper->getTypeHintAnnotation($property)),
120+
...explode('|', $this->renderHelper->getTypeHintAnnotation($property, true)),
121+
]) as $typeAnnotation) {
122+
$type = str_replace('[]', '', $typeAnnotation);
123+
113124
// as the typehint only knows the class name but not the fqcn, lookup in the original imports
114125
foreach ($originalClassImports as $originalClassImport) {
115126
if (str_ends_with($originalClassImport, "\\$type")) {
@@ -128,7 +139,10 @@ private function getBuilderClassImports(array $properties, array $originalClassI
128139
// for nested objects, allow additionally to pass an instance of the nested model also just plain
129140
// arrays which will result in an object instantiation and validation during the build process
130141
if (in_array(JSONModelInterface::class, class_implements($type))) {
131-
$property->addTypeHintDecorator(new TypeHintDecorator([basename($type) . 'Builder', 'array']));
142+
$property->addTypeHintDecorator(new TypeHintDecorator(
143+
[basename($type) . 'Builder' . (str_contains($typeAnnotation, '[]') ? '[]' : ''), 'array'],
144+
));
145+
132146
$property->setType();
133147
}
134148
}

tests/AbstractPHPModelGeneratorTestCase.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ protected function expectValidationErrorRegExp(
332332

333333
if ($configuration->collectErrors()) {
334334
$this->expectException(ErrorRegistryException::class);
335-
$this->expectExceptionMessageMatches(join("\n", $messages));
335+
$this->expectExceptionMessageMatches(str_replace("/\n/", "\n", join("\n", $messages)));
336336
} else {
337337
$this->expectException(ValidationException::class);
338338
$this->expectExceptionMessageMatches($messages[0]);

tests/PostProcessor/BuilderClassPostProcessorTest.php

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,29 @@ public function testBuilder(): void
6565
$this->assertEqualsCanonicalizing(['name' => 'Albert', 'age' => 65], $validatedObject->toArray());
6666
}
6767

68+
/**
69+
* @dataProvider validationMethodDataProvider
70+
*/
71+
public function testInvalidBuilderDataThrowsAnExceptionOnValidate(GeneratorConfiguration $configuration): void
72+
{
73+
$className = $this->generateClassFromFile('BasicSchema.json', $configuration);
74+
75+
$builderClassName = $className . 'Builder';
76+
$builderObject = new $builderClassName();
77+
78+
$builderObject->setName('Al')->setAge(-2);
79+
80+
$this->expectValidationErrorRegExp(
81+
$configuration,
82+
[
83+
'/Value for name must not be shorter than 5/',
84+
'/Value for age must not be smaller than 0/'
85+
],
86+
);
87+
88+
$builderObject->validate();
89+
}
90+
6891
public function testImplicitNull(): void
6992
{
7093
$className = $this->generateClassFromFile('BasicSchema.json');
@@ -96,8 +119,10 @@ public function testNestedObject(): void
96119
}
97120
}
98121

122+
$nestedBuilderClassName = $nestedObjectClassName . 'Builder';
123+
99124
$this->assertNotEmpty($nestedObjectClassName);
100-
$expectedTypeHint = "$nestedObjectClassName|{$nestedObjectClassName}Builder|array|null";
125+
$expectedTypeHint = "$nestedObjectClassName|$nestedBuilderClassName|array|null";
101126
$this->assertSame($expectedTypeHint, $this->getParameterTypeAnnotation($builderObject, 'setAddress'));
102127
$this->assertSame($expectedTypeHint, $this->getReturnTypeAnnotation($builderObject, 'getAddress'));
103128

@@ -111,7 +136,6 @@ public function testNestedObject(): void
111136
$this->assertSame(10, $object->getAddress()->getNumber());
112137

113138
// test generate nested object from nested builder
114-
$nestedBuilderClassName = $nestedObjectClassName . 'Builder';
115139
$nestedBuilderObject = new $nestedBuilderClassName();
116140
$this->assertSame('string|null', $this->getParameterTypeAnnotation($nestedBuilderObject, 'setStreet'));
117141
$this->assertSame('int|null', $this->getParameterTypeAnnotation($nestedBuilderObject, 'setNumber'));
@@ -135,6 +159,48 @@ public function testNestedObject(): void
135159
$this->assertSame(10, $object->getAddress()->getNumber());
136160
}
137161

162+
public function testNestedObjectArray(): void
163+
{
164+
$className = $this->generateClassFromFile('NestedObjectArray.json');
165+
166+
$this->assertGeneratedBuilders(2);
167+
168+
$builderClassName = $className . 'Builder';
169+
$builderObject = new $builderClassName();
170+
171+
$nestedObjectClassName = null;
172+
foreach ($this->getGeneratedFiles() as $file) {
173+
if (str_contains($file, 'Itemofarray')) {
174+
$nestedObjectClassName = str_replace('.php', '', basename($file));
175+
176+
break;
177+
}
178+
}
179+
180+
$nestedBuilderClassName = $nestedObjectClassName . 'Builder';
181+
182+
$this->assertNotEmpty($nestedObjectClassName);
183+
$expectedTypeHint = "{$nestedObjectClassName}[]|{$nestedBuilderClassName}[]|array|null";
184+
$this->assertSame($expectedTypeHint, $this->getParameterTypeAnnotation($builderObject, 'setAddressList'));
185+
$this->assertSame($expectedTypeHint, $this->getReturnTypeAnnotation($builderObject, 'getAddressList'));
186+
187+
$builderObject->setAddressList([
188+
['street' => 'Test street 0', 'number' => 10],
189+
(new $nestedBuilderClassName())->setStreet('Test street 1')->setNumber(11),
190+
new $nestedObjectClassName(['street' => 'Test street 2', 'number' => 12]),
191+
]);
192+
193+
$object = $builderObject->validate();
194+
195+
$this->assertCount(3, $object->getAddressList());
196+
197+
for ($i = 0; $i <= 2; $i++) {
198+
$this->assertInstanceOf($nestedObjectClassName, $object->getAddressList()[$i]);
199+
$this->assertSame("Test street {$i}", $object->getAddressList()[$i]->getStreet());
200+
$this->assertSame(10 + $i, $object->getAddressList()[$i]->getNumber());
201+
}
202+
}
203+
138204
private function assertGeneratedBuilders(int $expectedGeneratedBuilders): void
139205
{
140206
$dir = sys_get_temp_dir() . '/PHPModelGeneratorTest/Models';

tests/Schema/BuilderClassPostProcessorTest/BasicSchema.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
"type": "object",
33
"properties": {
44
"name": {
5-
"type": "string"
5+
"type": "string",
6+
"minLength": 5
67
},
78
"age": {
8-
"type": "integer"
9+
"type": "integer",
10+
"minimum": 0
911
}
1012
},
1113
"required": [
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"type": "object",
3+
"properties": {
4+
"addressList": {
5+
"type": "array",
6+
"items": {
7+
"type": "object",
8+
"properties": {
9+
"street": {
10+
"type": "string"
11+
},
12+
"number": {
13+
"type": "integer"
14+
}
15+
}
16+
}
17+
}
18+
}
19+
}

0 commit comments

Comments
 (0)