Skip to content

Commit db8f284

Browse files
committed
Add unit test nullable type declaration
1 parent 88bf5f0 commit db8f284

File tree

1 file changed

+161
-0
lines changed

1 file changed

+161
-0
lines changed
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
<?php
2+
3+
namespace Adyen\Tests\Unit;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use ReflectionClass;
7+
use ReflectionException;
8+
use ReflectionMethod;
9+
10+
class NullableTypeDeclarationTest extends TestCase
11+
{
12+
/**
13+
* @return array<string,array<string>>
14+
*/
15+
private function getGeneratedClasses(): array
16+
{
17+
$modelDir = dirname(__DIR__, 2) . '/src/Adyen/Model';
18+
$serviceDir = dirname(__DIR__, 2) . '/src/Adyen/Service';
19+
20+
return [
21+
'models' => $this->getPhpClasses($modelDir, 'Adyen\\Model'),
22+
'services' => $this->getPhpClasses($serviceDir, 'Adyen\\Service')
23+
];
24+
}
25+
26+
/**
27+
* @param string $directory
28+
* @param string $baseNamespace
29+
* @return array<string>
30+
*/
31+
private function getPhpClasses(string $directory, string $baseNamespace): array
32+
{
33+
$classes = [];
34+
$iterator = new \RecursiveIteratorIterator(
35+
new \RecursiveDirectoryIterator($directory)
36+
);
37+
38+
foreach ($iterator as $file) {
39+
if ($file->isFile() && $file->getExtension() === 'php') {
40+
$relativePath = substr($file->getPath(), strlen($directory));
41+
$namespace = $baseNamespace . str_replace('/', '\\', $relativePath);
42+
43+
$content = file_get_contents($file->getRealPath());
44+
if (preg_match('/class\s+(\w+)(?:\s+extends|\s+implements|\s*{)/', $content, $matches)) {
45+
$className = $namespace . '\\' . $matches[1];
46+
// Only add if the class exists
47+
if (class_exists($className)) {
48+
$classes[] = $className;
49+
}
50+
}
51+
}
52+
}
53+
54+
return $classes;
55+
}
56+
57+
/**
58+
* Test that model constructors properly declare nullable parameters
59+
* @throws ReflectionException
60+
*/
61+
public function testModelConstructorsHaveNullableParameters(): void
62+
{
63+
$classes = $this->getGeneratedClasses()['models'];
64+
$this->assertNotEmpty($classes, 'No model classes found');
65+
66+
foreach ($classes as $class) {
67+
$reflection = new ReflectionClass($class);
68+
69+
if ($reflection->hasMethod('__construct')) {
70+
$constructor = $reflection->getMethod('__construct');
71+
$params = $constructor->getParameters();
72+
73+
foreach ($params as $param) {
74+
if ($param->allowsNull() && $param->hasType()) {
75+
$this->assertTrue(
76+
$param->getType()->allowsNull(),
77+
sprintf(
78+
'Constructor parameter $%s in %s should be explicitly nullable',
79+
$param->getName(),
80+
$class
81+
)
82+
);
83+
}
84+
}
85+
}
86+
}
87+
}
88+
89+
/**
90+
* Test that service methods properly declare nullable parameters and return types
91+
* @throws ReflectionException
92+
*/
93+
public function testServiceMethodsHaveNullableTypes(): void
94+
{
95+
$classes = $this->getGeneratedClasses()['services'];
96+
$this->assertNotEmpty($classes, 'No service classes found');
97+
98+
foreach ($classes as $class) {
99+
$reflection = new ReflectionClass($class);
100+
101+
foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
102+
// Skip magic methods and inherited methods
103+
if (strpos($method->getName(), '__') === 0 || $method->getDeclaringClass()->getName() !== $class) {
104+
continue;
105+
}
106+
107+
// Check method parameters
108+
foreach ($method->getParameters() as $param) {
109+
if ($param->allowsNull() && $param->hasType()) {
110+
$this->assertTrue(
111+
$param->getType()->allowsNull(),
112+
sprintf(
113+
'Method parameter $%s in %s::%s should be explicitly nullable',
114+
$param->getName(),
115+
$class,
116+
$method->getName()
117+
)
118+
);
119+
}
120+
}
121+
122+
// Check return type
123+
if ($method->hasReturnType()) {
124+
$returnType = $method->getReturnType();
125+
if ($returnType && !in_array($method->getName(), ['getModelName', 'valid'])) {
126+
$this->assertTrue(
127+
$returnType->allowsNull(),
128+
sprintf(
129+
'Method %s::%s should have a nullable return type',
130+
$class,
131+
$method->getName()
132+
)
133+
);
134+
}
135+
}
136+
}
137+
}
138+
}
139+
140+
/**
141+
* Test Service class has proper nullable type declarations
142+
* @throws ReflectionException
143+
*/
144+
public function testServiceClassHasNullableTypes(): void
145+
{
146+
$reflection = new ReflectionClass('Adyen\\Service');
147+
148+
// Test requestHttp
149+
$method = $reflection->getMethod('requestHttp');
150+
$params = $method->getParameters();
151+
152+
$this->assertTrue(
153+
$params[2]->getType()->allowsNull(),
154+
'requestHttp $bodyParams parameter should be nullable'
155+
);
156+
$this->assertTrue(
157+
$params[3]->getType()->allowsNull(),
158+
'requestHttp $requestOptions parameter should be nullable'
159+
);
160+
}
161+
}

0 commit comments

Comments
 (0)