Skip to content

Commit 6077dd4

Browse files
eceltovkrulis-martin
authored andcommitted
added format tests
1 parent 78d7650 commit 6077dd4

File tree

1 file changed

+246
-0
lines changed

1 file changed

+246
-0
lines changed

tests/Validation/Formats.phpt

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
<?php
2+
3+
use App\Exceptions\BadRequestException;
4+
use App\Exceptions\InternalServerException;
5+
use App\Exceptions\InvalidApiArgumentException;
6+
use App\Helpers\MetaFormats\Attributes\FPath;
7+
use App\Helpers\MetaFormats\Attributes\FPost;
8+
use App\Helpers\MetaFormats\Attributes\FQuery;
9+
use App\Helpers\MetaFormats\FormatCache;
10+
use App\Helpers\MetaFormats\FormatDefinitions\UserFormat;
11+
use App\Helpers\MetaFormats\MetaFormat;
12+
use App\Helpers\MetaFormats\MetaFormatHelper;
13+
use App\Helpers\MetaFormats\Validators\BaseValidator;
14+
use App\Helpers\MetaFormats\Validators\VArray;
15+
use App\Helpers\MetaFormats\Validators\VBool;
16+
use App\Helpers\MetaFormats\Validators\VDouble;
17+
use App\Helpers\MetaFormats\Validators\VInt;
18+
use App\Helpers\MetaFormats\Validators\VMixed;
19+
use App\Helpers\MetaFormats\Validators\VObject;
20+
use App\Helpers\MetaFormats\Validators\VString;
21+
use App\Helpers\MetaFormats\Validators\VTimestamp;
22+
use App\Helpers\MetaFormats\Validators\VUuid;
23+
use Tester\Assert;
24+
25+
$container = require_once __DIR__ . "/../bootstrap.php";
26+
27+
#[Format(RequiredNullabilityTestFormat::class)]
28+
class RequiredNullabilityTestFormat extends MetaFormat
29+
{
30+
#[FPost(new VInt(), required: true, nullable: false)]
31+
public ?int $requiredNotNullable;
32+
33+
#[FPost(new VInt(), required: false, nullable: false)]
34+
public ?int $notRequiredNotNullable;
35+
36+
#[FPost(new VInt(), required: true, nullable: true)]
37+
public ?int $requiredNullable;
38+
39+
#[FPost(new VInt(), required: false, nullable: true)]
40+
public ?int $notRequiredNullable;
41+
}
42+
43+
#[Format(ValidationTestFormat::class)]
44+
class ValidationTestFormat extends MetaFormat
45+
{
46+
#[FPost(new VInt(), required: true, nullable: false)]
47+
public ?int $post;
48+
49+
#[FPath(new VInt(), required: true, nullable: false)]
50+
public ?int $path;
51+
52+
#[FQuery(new VInt(), required: true, nullable: false)]
53+
public ?int $query;
54+
55+
#[FQuery(new VInt(), required: false)]
56+
public ?int $queryOptional;
57+
58+
public function validateStructure()
59+
{
60+
return $this->query == 1;
61+
}
62+
}
63+
64+
/**
65+
* @testCase
66+
*/
67+
class TestFormats extends Tester\TestCase
68+
{
69+
/** @var Nette\DI\Container */
70+
protected $container;
71+
72+
public function __construct()
73+
{
74+
global $container;
75+
$this->container = $container;
76+
}
77+
78+
private function injectFormat(string $format)
79+
{
80+
// initialize the cache
81+
FormatCache::getFormatToFieldDefinitionsMap();
82+
FormatCache::getFormatNamesHashSet();
83+
84+
// inject the format name
85+
$hashSetReflector = new ReflectionProperty(FormatCache::class, "formatNamesHashSet");
86+
$hashSetReflector->setAccessible(true);
87+
$formatNamesHashSet = $hashSetReflector->getValue();
88+
$formatNamesHashSet[$format] = true;
89+
$hashSetReflector->setValue(null, $formatNamesHashSet);
90+
91+
// inject the format definitions
92+
$formatMapReflector = new ReflectionProperty(FormatCache::class, "formatToFieldFormatsMap");
93+
$formatMapReflector->setAccessible(true);
94+
$formatToFieldFormatsMap = $formatMapReflector->getValue();
95+
$formatToFieldFormatsMap[$format] = MetaFormatHelper::createNameToFieldDefinitionsMap($format);
96+
$formatMapReflector->setValue(null, $formatToFieldFormatsMap);
97+
98+
Assert::notNull(FormatCache::getFieldDefinitions($format), "Tests whether a format was injected successfully.");
99+
}
100+
101+
public function testInvalidFieldName()
102+
{
103+
self::injectFormat(RequiredNullabilityTestFormat::class);
104+
105+
Assert::throws(
106+
function () {
107+
try {
108+
$format = new RequiredNullabilityTestFormat();
109+
$format->checkedAssign("invalidIdentifier", null);
110+
} catch (Exception $e) {
111+
Assert::true(strlen($e->getMessage()) > 0);
112+
throw $e;
113+
}
114+
},
115+
InternalServerException::class
116+
);
117+
}
118+
119+
public function testRequiredNotNullable()
120+
{
121+
self::injectFormat(RequiredNullabilityTestFormat::class);
122+
$fieldName = "requiredNotNullable";
123+
124+
// it is not nullable so this has to throw
125+
Assert::throws(
126+
function () use ($fieldName) {
127+
try {
128+
$format = new RequiredNullabilityTestFormat();
129+
$format->checkedAssign($fieldName, null);
130+
} catch (Exception $e) {
131+
Assert::true(strlen($e->getMessage()) > 0);
132+
throw $e;
133+
}
134+
},
135+
InvalidApiArgumentException::class
136+
);
137+
138+
// assign 1
139+
$format = new RequiredNullabilityTestFormat();
140+
$format->checkedAssign($fieldName, 1);
141+
Assert::equal($format->$fieldName, 1);
142+
}
143+
144+
public function testNullAssign()
145+
{
146+
self::injectFormat(RequiredNullabilityTestFormat::class);
147+
$format = new RequiredNullabilityTestFormat();
148+
149+
// not required and not nullable fields can contain null (not required overrides not nullable)
150+
foreach (["requiredNullable", "notRequiredNullable", "notRequiredNotNullable"] as $fieldName) {
151+
// assign 1
152+
$format->checkedAssign($fieldName, 1);
153+
Assert::equal($format->$fieldName, 1);
154+
155+
// assign null
156+
$format->checkedAssign($fieldName, null);
157+
Assert::equal($format->$fieldName, null);
158+
}
159+
}
160+
161+
public function testIndividualParamValidation()
162+
{
163+
self::injectFormat(ValidationTestFormat::class);
164+
$format = new ValidationTestFormat();
165+
166+
// path and query parameters do not have strict validation
167+
$format->checkedAssign("query", "1");
168+
$format->checkedAssign("query", 1);
169+
$format->checkedAssign("path", "1");
170+
$format->checkedAssign("path", 1);
171+
172+
// post parameters have strict validation, assigning a string will throw
173+
$format->checkedAssign("post", 1);
174+
Assert::throws(
175+
function () use ($format) {
176+
try {
177+
$format->checkedAssign("post", "1");
178+
} catch (Exception $e) {
179+
Assert::true(strlen($e->getMessage()) > 0);
180+
throw $e;
181+
}
182+
},
183+
InvalidApiArgumentException::class
184+
);
185+
186+
// null cannot be assigned unless the parameter is nullable or not required
187+
$format->checkedAssign("queryOptional", null);
188+
Assert::throws(
189+
function () use ($format) {
190+
try {
191+
$format->checkedAssign("query", null);
192+
} catch (Exception $e) {
193+
Assert::true(strlen($e->getMessage()) > 0);
194+
throw $e;
195+
}
196+
},
197+
InvalidApiArgumentException::class
198+
);
199+
}
200+
201+
public function testAggregateParamValidation()
202+
{
203+
self::injectFormat(ValidationTestFormat::class);
204+
$format = new ValidationTestFormat();
205+
206+
$format->checkedAssign("query", 1);
207+
$format->checkedAssign("path", 1);
208+
$format->checkedAssign("post", 1);
209+
$format->checkedAssign("queryOptional", null);
210+
$format->validate();
211+
212+
// invalidate a format field
213+
Assert::throws(
214+
function () use ($format) {
215+
try {
216+
// bypass the checkedAssign
217+
$format->path = null;
218+
$format->validate();
219+
} catch (Exception $e) {
220+
Assert::true(strlen($e->getMessage()) > 0);
221+
throw $e;
222+
}
223+
},
224+
InvalidApiArgumentException::class
225+
);
226+
227+
// assign valid values to all fields, but fail the structural constraint of $query == 1
228+
$format->checkedAssign("path", 1);
229+
$format->checkedAssign("query", 2);
230+
Assert::throws(
231+
function () use ($format) {
232+
try {
233+
$format->validate();
234+
} catch (Exception $e) {
235+
Assert::true(strlen($e->getMessage()) > 0);
236+
throw $e;
237+
}
238+
},
239+
BadRequestException::class
240+
);
241+
}
242+
243+
///TODO: nested formats, loose format
244+
}
245+
246+
(new TestFormats())->run();

0 commit comments

Comments
 (0)