55namespace Chubbyphp \Tests \Parsing \Unit \Schema ;
66
77use Chubbyphp \Parsing \ErrorsException ;
8+ use Chubbyphp \Parsing \Schema \BoolSchema ;
89use Chubbyphp \Parsing \Schema \FloatSchema ;
910use Chubbyphp \Parsing \Schema \IntSchema ;
1011use Chubbyphp \Parsing \Schema \ObjectConstructorSchema ;
@@ -17,6 +18,7 @@ public function __construct(
1718 public readonly string $ field1 ,
1819 public readonly int $ field2 ,
1920 public readonly ?float $ field3 = null ,
21+ public readonly ?float $ field4 = null ,
2022 ) {}
2123
2224 public function jsonSerialize (): array
@@ -25,6 +27,7 @@ public function jsonSerialize(): array
2527 'field1 ' => $ this ->field1 ,
2628 'field2 ' => $ this ->field2 ,
2729 'field3 ' => $ this ->field3 ,
30+ 'field4 ' => $ this ->field4 ,
2831 ];
2932 }
3033}
@@ -108,28 +111,30 @@ public function testConstructWithAdditionalFieldSchema(): void
108111 new ObjectConstructorSchema ([
109112 'field1 ' => new StringSchema (),
110113 'field2 ' => new IntSchema (),
111- 'field3 ' => new FloatSchema (),
112- 'field4 ' => new FloatSchema (),
113- ], ObjectConstructorDemo::class)->optional (['field3 ' ]);
114+ 'field3 ' => new FloatSchema ()->nullable (),
115+ 'field4 ' => new FloatSchema ()->nullable (),
116+ 'field5 ' => new FloatSchema (),
117+ ], ObjectConstructorDemo::class)->optional (['field3 ' , 'field4 ' ]);
114118
115119 throw new \Exception ('code should not be reached ' );
116120 } catch (\InvalidArgumentException $ invalidArgumentException ) {
117121 self ::assertSame (
118- 'Additional fieldToSchema for " ' .ObjectConstructorDemo::class.'" __construct parameters: "field4 " ' ,
122+ 'Additional fieldToSchema for " ' .ObjectConstructorDemo::class.'" __construct parameters: "field5 " ' ,
119123 $ invalidArgumentException ->getMessage ()
120124 );
121125 }
122126 }
123127
124128 public function testSuccessWithAllParameters (): void
125129 {
126- $ input = ['field1 ' => 'test ' , 'field2 ' => 5 , 'field3 ' => 3.14159 ];
130+ $ input = ['field1 ' => 'test ' , 'field2 ' => 5 , 'field3 ' => 3.14159 , ' field4 ' => 1.61803 ];
127131
128132 $ schema = new ObjectConstructorSchema ([
129133 'field1 ' => new StringSchema (),
130134 'field2 ' => new IntSchema (),
131- 'field3 ' => new FloatSchema (),
132- ], ObjectConstructorDemo::class)->optional (['field3 ' ]);
135+ 'field3 ' => new FloatSchema ()->nullable (),
136+ 'field4 ' => new FloatSchema ()->nullable (),
137+ ], ObjectConstructorDemo::class)->optional (['field3 ' , 'field4 ' ]);
133138
134139 /** @var ObjectConstructorDemo $object */
135140 $ object = $ schema ->parse ($ input );
@@ -141,20 +146,21 @@ public function testSuccessWithAllParameters(): void
141146
142147 public function testSuccessWithAllParametersOptionalConsidered (): void
143148 {
144- $ input = ['field1 ' => 'test ' , 'field2 ' => 5 ];
149+ $ input = ['field1 ' => 'test ' , 'field2 ' => 5 , ' field4 ' => 1.61803 ];
145150
146151 $ schema = new ObjectConstructorSchema ([
147152 'field1 ' => new StringSchema (),
148153 'field2 ' => new IntSchema (),
149- 'field3 ' => new FloatSchema (),
150- ], ObjectConstructorDemo::class)->optional (['field3 ' ]);
154+ 'field3 ' => new FloatSchema ()->nullable (),
155+ 'field4 ' => new FloatSchema ()->nullable (),
156+ ], ObjectConstructorDemo::class)->optional (['field3 ' , 'field4 ' ]);
151157
152158 /** @var ObjectConstructorDemo $object */
153159 $ object = $ schema ->parse ($ input );
154160
155161 self ::assertInstanceOf (ObjectConstructorDemo::class, $ object );
156162
157- self ::assertSame ([... $ input , 'field3 ' => null ], $ object ->jsonSerialize ());
163+ self ::assertSame ([' field1 ' => ' test ' , 'field2 ' => 5 , ' field3 ' => null , ' field4 ' => 1.61803 ], $ object ->jsonSerialize ());
158164 }
159165
160166 public function testSuccessWithRequiredParameters (): void
@@ -164,14 +170,14 @@ public function testSuccessWithRequiredParameters(): void
164170 $ schema = new ObjectConstructorSchema ([
165171 'field1 ' => new StringSchema (),
166172 'field2 ' => new IntSchema (),
167- ], ObjectConstructorDemo::class)->optional (['field3 ' ]);
173+ ], ObjectConstructorDemo::class)->optional (['field3 ' , ' field4 ' ]);
168174
169175 /** @var ObjectConstructorDemo $object */
170176 $ object = $ schema ->parse ($ input );
171177
172178 self ::assertInstanceOf (ObjectConstructorDemo::class, $ object );
173179
174- self ::assertSame ([...$ input , 'field3 ' => null ], $ object ->jsonSerialize ());
180+ self ::assertSame ([...$ input , 'field3 ' => null , ' field4 ' => null ], $ object ->jsonSerialize ());
175181 }
176182
177183 public function testFailedWithInvalidValue (): void
@@ -181,8 +187,9 @@ public function testFailedWithInvalidValue(): void
181187 $ schema = new ObjectConstructorSchema ([
182188 'field1 ' => new StringSchema (),
183189 'field2 ' => new IntSchema (),
184- 'field3 ' => new FloatSchema (),
185- ], ObjectConstructorDemo::class)->optional (['field3 ' ]);
190+ 'field3 ' => new FloatSchema ()->nullable (),
191+ 'field4 ' => new FloatSchema ()->nullable (),
192+ ], ObjectConstructorDemo::class)->optional (['field3 ' , 'field4 ' ]);
186193
187194 try {
188195 $ schema ->parse ($ input );
@@ -211,8 +218,9 @@ public function testFailedWithInvalidValueNotCatchedByFieldSchema(): void
211218 $ schema = new ObjectConstructorSchema ([
212219 'field1 ' => new StringSchema (),
213220 'field2 ' => new IntSchema (),
214- 'field3 ' => new StringSchema (),
215- ], ObjectConstructorDemo::class)->optional (['field3 ' ]);
221+ 'field3 ' => new StringSchema ()->nullable (),
222+ 'field4 ' => new FloatSchema ()->nullable (),
223+ ], ObjectConstructorDemo::class)->optional (['field3 ' , 'field4 ' ]);
216224
217225 try {
218226 $ schema ->parse ($ input );
@@ -246,8 +254,9 @@ public function testFailedWithUnknownException(): void
246254 $ schema = new ObjectConstructorSchema ([
247255 'field1 ' => new StringSchema (),
248256 'field2 ' => new IntSchema (),
249- 'field3 ' => new FloatSchema ()->postParse (static fn () => throw $ exception ),
250- ], ObjectConstructorDemo::class)->optional (['field3 ' ]);
257+ 'field3 ' => new FloatSchema ()->nullable ()->postParse (static fn () => throw $ exception ),
258+ 'field4 ' => new FloatSchema ()->nullable (),
259+ ], ObjectConstructorDemo::class)->optional (['field3 ' , 'field4 ' ]);
251260
252261 try {
253262 $ schema ->parse ($ input );
@@ -274,4 +283,141 @@ public function testFailedWithTypeErrorNotMatchingPattern(): void
274283 self ::assertSame ('some unrelated type error ' , $ e ->getMessage ());
275284 }
276285 }
286+
287+ public function testParseSuccessWithPreParse (): void
288+ {
289+ $ input = ['field1 ' => 'test ' , 'field2 ' => 5 , 'field3 ' => null , 'field4 ' => null ];
290+
291+ $ schema = new ObjectConstructorSchema ([
292+ 'field1 ' => new StringSchema (),
293+ 'field2 ' => new IntSchema (),
294+ 'field3 ' => new FloatSchema ()->nullable (),
295+ 'field4 ' => new FloatSchema ()->nullable (),
296+ ], ObjectConstructorDemo::class)
297+ ->optional (['field3 ' , 'field4 ' ])
298+ ->preParse (static fn () => $ input )
299+ ;
300+
301+ self ::assertSame ($ input , $ schema ->parse (null )->jsonSerialize ());
302+ }
303+
304+ public function testParseSuccessWithPostParse (): void
305+ {
306+ $ input = ['field1 ' => 'test ' , 'field2 ' => 5 ];
307+
308+ $ schema = new ObjectConstructorSchema ([
309+ 'field1 ' => new StringSchema (),
310+ 'field2 ' => new IntSchema (),
311+ 'field3 ' => new FloatSchema ()->nullable (),
312+ 'field4 ' => new FloatSchema ()->nullable (),
313+ ], ObjectConstructorDemo::class)
314+ ->optional (['field3 ' , 'field4 ' ])
315+ ->postParse (static fn (ObjectConstructorDemo $ output ) => new ObjectConstructorDemo (...[...$ output ->jsonSerialize (), 'field2 ' => 10 ]))
316+ ;
317+
318+ self ::assertSame ([...$ input , 'field2 ' => 10 , 'field3 ' => null , 'field4 ' => null ], (array ) $ schema ->parse ($ input ));
319+ }
320+
321+ public function testParseFailedWithCatch (): void
322+ {
323+ $ schema = (new ObjectConstructorSchema ([
324+ 'field1 ' => new StringSchema (),
325+ 'field2 ' => new IntSchema (),
326+ 'field3 ' => new FloatSchema ()->nullable (),
327+ 'field4 ' => new FloatSchema ()->nullable (),
328+ ], ObjectConstructorDemo::class))
329+ ->optional (['field3 ' , 'field4 ' ])
330+ ->catch (static function (mixed $ input , ErrorsException $ errorsException ) {
331+ self ::assertNull ($ input );
332+ self ::assertSame ([
333+ [
334+ 'path ' => '' ,
335+ 'error ' => [
336+ 'code ' => 'object.type ' ,
337+ 'template ' => 'Type should be "array|\stdClass|\Traversable", {{given}} given ' ,
338+ 'variables ' => [
339+ 'given ' => 'NULL ' ,
340+ ],
341+ ],
342+ ],
343+ ], $ errorsException ->errors ->jsonSerialize ());
344+
345+ return 'catched ' ;
346+ })
347+ ;
348+
349+ self ::assertSame ('catched ' , $ schema ->parse (null ));
350+ }
351+
352+ public function testSafeParseSuccess (): void
353+ {
354+ $ input = ['field1 ' => 'test ' , 'field2 ' => 1 ];
355+
356+ $ schema = (new ObjectConstructorSchema ([
357+ 'field1 ' => new StringSchema (),
358+ 'field2 ' => new IntSchema (),
359+ 'field3 ' => new FloatSchema ()->nullable (),
360+ 'field4 ' => new FloatSchema ()->nullable (),
361+ ], ObjectConstructorDemo::class))
362+ ->optional (['field3 ' , 'field4 ' ])
363+ ;
364+
365+ self ::assertSame ([...$ input , 'field3 ' => null , 'field4 ' => null ], $ schema ->safeParse ($ input )->data ->jsonSerialize ());
366+ }
367+
368+ public function testSafeParseFailed (): void
369+ {
370+ $ schema = (new ObjectConstructorSchema ([
371+ 'field1 ' => new StringSchema (),
372+ 'field2 ' => new IntSchema (),
373+ 'field3 ' => new FloatSchema ()->nullable (),
374+ 'field4 ' => new FloatSchema ()->nullable (),
375+ ], ObjectConstructorDemo::class))
376+ ->optional (['field3 ' , 'field4 ' ])
377+ ;
378+
379+ self ::assertSame ([
380+ [
381+ 'path ' => '' ,
382+ 'error ' => [
383+ 'code ' => 'object.type ' ,
384+ 'template ' => 'Type should be "array|\stdClass|\Traversable", {{given}} given ' ,
385+ 'variables ' => [
386+ 'given ' => 'NULL ' ,
387+ ],
388+ ],
389+ ],
390+ ], $ schema ->safeParse (null )->exception ->errors ->jsonSerialize ());
391+ }
392+
393+ public function testGetFieldToSchema (): void
394+ {
395+ $ fieldToSchema = ['field1 ' => new StringSchema (), 'field2 ' => new IntSchema ()];
396+
397+ $ schema = new ObjectConstructorSchema ($ fieldToSchema , ObjectConstructorDemo::class);
398+
399+ self ::assertSame ($ fieldToSchema , $ schema ->getFieldToSchema ());
400+
401+ $ fieldToSchema2 = [...$ schema ->getFieldToSchema (), 'field3 ' => new BoolSchema ()];
402+
403+ $ schema2 = new ObjectConstructorSchema ($ fieldToSchema2 , ObjectConstructorDemo::class);
404+
405+ self ::assertSame ($ fieldToSchema2 , $ schema2 ->getFieldToSchema ());
406+ }
407+
408+ public function testGetFieldSchemaSuccess (): void
409+ {
410+ $ field2Schema = new IntSchema ();
411+
412+ $ schema = new ObjectConstructorSchema (['field1 ' => new StringSchema (), 'field2 ' => $ field2Schema ], ObjectConstructorDemo::class);
413+
414+ self ::assertSame ($ field2Schema , $ schema ->getFieldSchema ('field2 ' ));
415+ }
416+
417+ public function testGetFieldSchemaFailed (): void
418+ {
419+ $ schema = new ObjectConstructorSchema (['field1 ' => new StringSchema (), 'field2 ' => new IntSchema ()], ObjectConstructorDemo::class);
420+
421+ self ::assertNull ($ schema ->getFieldSchema ('field3 ' ));
422+ }
277423}
0 commit comments