Skip to content

Commit 39ec2cd

Browse files
committed
feat(tests): add some coverage
1 parent d501a2a commit 39ec2cd

9 files changed

Lines changed: 791 additions & 0 deletions

File tree

src/Config/Loader/PlanConfigLoader.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ final class PlanConfigLoader
1717
*/
1818
public static function load(string $path): Plan
1919
{
20+
if (!is_file($path)) {
21+
throw new ConfigurationException("File '{$path}' does not exist.");
22+
}
2023
$content = file_get_contents($path);
2124
if ($content === false) {
2225
throw new ConfigurationException("Could not load file '{$path}'");

tests/Config/FiltersTest.php

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace APITester\Tests\Config;
6+
7+
use APITester\Config\Filters;
8+
use APITester\Util\Filterable;
9+
use PHPUnit\Framework\TestCase;
10+
use Symfony\Component\Yaml\Tag\TaggedValue;
11+
12+
final class FiltersTest extends TestCase
13+
{
14+
private ?string $tempFile = null;
15+
16+
protected function tearDown(): void
17+
{
18+
if ($this->tempFile !== null && file_exists($this->tempFile)) {
19+
unlink($this->tempFile);
20+
}
21+
}
22+
23+
public function testDefaultConstructorHasEmptyArrays(): void
24+
{
25+
$filters = new Filters();
26+
27+
static::assertSame([], $filters->getInclude());
28+
static::assertSame([], $filters->getExclude());
29+
}
30+
31+
public function testAddIncludeAppendsRules(): void
32+
{
33+
$filters = new Filters();
34+
$filters->addInclude([
35+
[
36+
'id' => 'foo',
37+
],
38+
]);
39+
$filters->addInclude([
40+
[
41+
'id' => 'bar',
42+
],
43+
]);
44+
45+
static::assertCount(2, $filters->getInclude());
46+
static::assertSame('foo', $filters->getInclude()[0]['id']);
47+
static::assertSame('bar', $filters->getInclude()[1]['id']);
48+
}
49+
50+
public function testAddExcludeAppendsRules(): void
51+
{
52+
$filters = new Filters();
53+
$filters->addExclude([
54+
[
55+
'method' => 'DELETE',
56+
],
57+
]);
58+
59+
static::assertCount(1, $filters->getExclude());
60+
}
61+
62+
public function testIncludesWithEmptyFiltersReturnsTrue(): void
63+
{
64+
$filters = new Filters();
65+
$object = $this->createFilterable([
66+
'id' => 'anything',
67+
]);
68+
69+
static::assertTrue($filters->includes($object));
70+
}
71+
72+
public function testIncludesWithMatchingIncludeRule(): void
73+
{
74+
$filters = new Filters([
75+
[
76+
'id' => 'foo',
77+
],
78+
]);
79+
$matching = $this->createFilterable([
80+
'id' => 'foo',
81+
]);
82+
$nonMatching = $this->createFilterable([
83+
'id' => 'bar',
84+
]);
85+
86+
static::assertTrue($filters->includes($matching));
87+
static::assertFalse($filters->includes($nonMatching));
88+
}
89+
90+
public function testIncludesWithExcludeRule(): void
91+
{
92+
$filters = new Filters(null, [
93+
[
94+
'id' => 'excluded',
95+
],
96+
]);
97+
$excluded = $this->createFilterable([
98+
'id' => 'excluded',
99+
]);
100+
$included = $this->createFilterable([
101+
'id' => 'other',
102+
]);
103+
104+
static::assertFalse($filters->includes($excluded));
105+
static::assertTrue($filters->includes($included));
106+
}
107+
108+
public function testHandleTagsNotProducesNotEqual(): void
109+
{
110+
$ref = new \ReflectionMethod(Filters::class, 'handleTags');
111+
$ref->setAccessible(true);
112+
$filters = new Filters();
113+
114+
/** @var array{0: string, 1: string|int|null} $result */
115+
$result = $ref->invoke($filters, new TaggedValue('NOT', 'DELETE'));
116+
117+
static::assertSame('!=', $result[0]);
118+
static::assertSame('DELETE', $result[1]);
119+
}
120+
121+
public function testHandleTagsInProducesContains(): void
122+
{
123+
$ref = new \ReflectionMethod(Filters::class, 'handleTags');
124+
$ref->setAccessible(true);
125+
$filters = new Filters();
126+
127+
/** @var array{0: string, 1: string|int|null} $result */
128+
$result = $ref->invoke($filters, new TaggedValue('IN', 'pet'));
129+
130+
static::assertSame('contains', $result[0]);
131+
static::assertSame('pet', $result[1]);
132+
}
133+
134+
public function testHandleTagsNullStringConvertsToNull(): void
135+
{
136+
$ref = new \ReflectionMethod(Filters::class, 'handleTags');
137+
$ref->setAccessible(true);
138+
$filters = new Filters();
139+
140+
/** @var array{0: string, 1: string|int|null} $result */
141+
$result = $ref->invoke($filters, 'null');
142+
143+
static::assertSame('=', $result[0]);
144+
static::assertNull($result[1]);
145+
}
146+
147+
public function testWriteBaselineAndGetBaselineExcludeRoundTrip(): void
148+
{
149+
$this->tempFile = tempnam(sys_get_temp_dir(), 'api-tester-test-') . '.yaml';
150+
$filters = new Filters(null, null, $this->tempFile);
151+
152+
$exclude = [
153+
[
154+
'id' => 'test_1',
155+
],
156+
[
157+
'id' => 'test_2',
158+
],
159+
];
160+
$filters->writeBaseline($exclude);
161+
162+
$result = $filters->getBaseLineExclude();
163+
164+
static::assertCount(2, $result);
165+
static::assertSame('test_1', $result[0]['id']);
166+
static::assertSame('test_2', $result[1]['id']);
167+
}
168+
169+
/**
170+
* @param array<string, mixed> $props
171+
*/
172+
private function createFilterable(array $props): Filterable
173+
{
174+
return new class($props) implements Filterable {
175+
/**
176+
* @param array<string, mixed> $props
177+
*/
178+
public function __construct(
179+
private readonly array $props
180+
) {
181+
}
182+
183+
public function has(string $prop, $value, string $operator = '='): bool
184+
{
185+
if (!array_key_exists($prop, $this->props)) {
186+
return false;
187+
}
188+
189+
$propValue = $this->props[$prop];
190+
191+
return match ($operator) {
192+
'=' => $propValue === $value,
193+
'!=' => $propValue !== $value,
194+
'contains' => is_string($propValue) && is_string($value) && str_contains($propValue, $value),
195+
default => false,
196+
};
197+
}
198+
};
199+
}
200+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace APITester\Tests\Config\Loader;
6+
7+
use APITester\Config\Exception\ConfigurationException;
8+
use APITester\Config\Loader\PlanConfigLoader;
9+
use APITester\Tests\Fixtures\FixturesLocation;
10+
use PHPUnit\Framework\TestCase;
11+
12+
final class PlanConfigLoaderTest extends TestCase
13+
{
14+
public function testLoadReturnsPlanWithSuites(): void
15+
{
16+
$plan = PlanConfigLoader::load(FixturesLocation::CONFIG_OPENAPI);
17+
18+
static::assertNotEmpty($plan->getSuites());
19+
static::assertSame('oc', $plan->getSuites()[0]->getName());
20+
}
21+
22+
public function testLoadThrowsForNonExistentFile(): void
23+
{
24+
$this->expectException(ConfigurationException::class);
25+
26+
PlanConfigLoader::load('/non/existent/file.yaml');
27+
}
28+
29+
public function testEnvVarSubstitution(): void
30+
{
31+
$_ENV['TEST_API_VAR'] = 'replaced_value';
32+
33+
try {
34+
$content = '%env(TEST_API_VAR)%';
35+
$ref = new \ReflectionClass(PlanConfigLoader::class);
36+
$method = $ref->getMethod('process');
37+
38+
$result = $method->invoke(null, $content);
39+
40+
static::assertSame('replaced_value', $result);
41+
} finally {
42+
unset($_ENV['TEST_API_VAR']);
43+
}
44+
}
45+
46+
public function testMissingEnvVarThrowsConfigurationException(): void
47+
{
48+
unset($_ENV['NONEXISTENT_VAR_FOR_TEST']);
49+
50+
$this->expectException(ConfigurationException::class);
51+
$this->expectExceptionMessage("'NONEXISTENT_VAR_FOR_TEST'");
52+
53+
$ref = new \ReflectionClass(PlanConfigLoader::class);
54+
$method = $ref->getMethod('process');
55+
56+
$method->invoke(null, '%env(NONEXISTENT_VAR_FOR_TEST)%');
57+
}
58+
}

tests/Requester/RequesterTest.php

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace APITester\Tests\Requester;
6+
7+
use APITester\Requester\HttpDumpRequester;
8+
use APITester\Requester\Requester;
9+
use Nyholm\Psr7\Request;
10+
use PHPUnit\Framework\TestCase;
11+
12+
final class RequesterTest extends TestCase
13+
{
14+
public function testSetBaseUriTrimsTrailingSlashes(): void
15+
{
16+
$requester = new HttpDumpRequester();
17+
$requester->setBaseUri('https://example.com/api/');
18+
19+
static::assertSame('https://example.com/api', $requester->getBaseUri());
20+
}
21+
22+
public function testSetBaseUriTrimsMultipleTrailingSlashes(): void
23+
{
24+
$requester = new HttpDumpRequester();
25+
$requester->setBaseUri('https://example.com///');
26+
27+
static::assertSame('https://example.com', $requester->getBaseUri());
28+
}
29+
30+
public function testResolveUriPrependsBaseUriForRelativePath(): void
31+
{
32+
$requester = new HttpDumpRequester();
33+
$requester->setBaseUri('https://example.com/api');
34+
35+
$request = new Request('GET', '/users/1');
36+
$resolved = $requester->resolveUri($request);
37+
38+
static::assertSame('https://example.com/api/users/1', (string) $resolved->getUri());
39+
}
40+
41+
public function testResolveUriDoesNotPrependForAbsoluteUri(): void
42+
{
43+
$requester = new HttpDumpRequester();
44+
$requester->setBaseUri('https://example.com/api');
45+
46+
$request = new Request('GET', 'https://other.com/resource');
47+
$resolved = $requester->resolveUri($request);
48+
49+
static::assertSame('https://other.com/resource', (string) $resolved->getUri());
50+
}
51+
52+
public function testResolveUriWithEmptyBaseUriReturnsUnchanged(): void
53+
{
54+
$requester = new HttpDumpRequester();
55+
56+
$request = new Request('GET', '/users');
57+
$resolved = $requester->resolveUri($request);
58+
59+
static::assertSame('/users', (string) $resolved->getUri());
60+
}
61+
62+
/**
63+
* @dataProvider fillVarsProvider
64+
*/
65+
public function testFillVarsReplacesPlaceholders(string $subject, string $expected): void
66+
{
67+
$requester = new HttpDumpRequester();
68+
69+
$varsRef = new \ReflectionProperty(Requester::class, 'vars');
70+
$varsRef->setAccessible(true);
71+
$varsRef->setValue($requester, [
72+
'name' => 'John',
73+
'id' => '42',
74+
]);
75+
76+
$fillVarsRef = new \ReflectionMethod(Requester::class, 'fillVars');
77+
$fillVarsRef->setAccessible(true);
78+
79+
$result = $fillVarsRef->invoke($requester, $subject);
80+
81+
static::assertSame($expected, $result);
82+
}
83+
84+
/**
85+
* @return iterable<string, array{0: string, 1: string}>
86+
*/
87+
public function fillVarsProvider(): iterable
88+
{
89+
yield 'single placeholder' => ['/users/{id}', '/users/42'];
90+
yield 'multiple placeholders' => ['{name} has id {id}', 'John has id 42'];
91+
yield 'no placeholders' => ['plain text', 'plain text'];
92+
}
93+
}

0 commit comments

Comments
 (0)