Skip to content

Commit fbfa494

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

8 files changed

Lines changed: 760 additions & 0 deletions

File tree

tests/Config/FiltersTest.php

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
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([['id' => 'foo']]);
35+
$filters->addInclude([['id' => 'bar']]);
36+
37+
static::assertCount(2, $filters->getInclude());
38+
static::assertSame('foo', $filters->getInclude()[0]['id']);
39+
static::assertSame('bar', $filters->getInclude()[1]['id']);
40+
}
41+
42+
public function testAddExcludeAppendsRules(): void
43+
{
44+
$filters = new Filters();
45+
$filters->addExclude([['method' => 'DELETE']]);
46+
47+
static::assertCount(1, $filters->getExclude());
48+
}
49+
50+
public function testIncludesWithEmptyFiltersReturnsTrue(): void
51+
{
52+
$filters = new Filters();
53+
$object = $this->createFilterable(['id' => 'anything']);
54+
55+
static::assertTrue($filters->includes($object));
56+
}
57+
58+
public function testIncludesWithMatchingIncludeRule(): void
59+
{
60+
$filters = new Filters([['id' => 'foo']]);
61+
$matching = $this->createFilterable(['id' => 'foo']);
62+
$nonMatching = $this->createFilterable(['id' => 'bar']);
63+
64+
static::assertTrue($filters->includes($matching));
65+
static::assertFalse($filters->includes($nonMatching));
66+
}
67+
68+
public function testIncludesWithExcludeRule(): void
69+
{
70+
$filters = new Filters(null, [['id' => 'excluded']]);
71+
$excluded = $this->createFilterable(['id' => 'excluded']);
72+
$included = $this->createFilterable(['id' => 'other']);
73+
74+
static::assertFalse($filters->includes($excluded));
75+
static::assertTrue($filters->includes($included));
76+
}
77+
78+
public function testHandleTagsNotProducesNotEqual(): void
79+
{
80+
$filters = new Filters([['method' => new TaggedValue('NOT', 'DELETE')]]);
81+
$delete = $this->createFilterable(['method' => 'DELETE']);
82+
$get = $this->createFilterable(['method' => 'GET']);
83+
84+
static::assertFalse($filters->includes($delete));
85+
static::assertTrue($filters->includes($get));
86+
}
87+
88+
public function testHandleTagsInProducesContains(): void
89+
{
90+
$filters = new Filters([['tags' => new TaggedValue('IN', 'pet')]]);
91+
$matching = $this->createFilterable(['tags' => 'pet,dog']);
92+
$nonMatching = $this->createFilterable(['tags' => 'car']);
93+
94+
static::assertTrue($filters->includes($matching));
95+
static::assertFalse($filters->includes($nonMatching));
96+
}
97+
98+
public function testHandleTagsNullStringConvertsToNull(): void
99+
{
100+
$filters = new Filters(null, [['method' => 'null']]);
101+
$nullMethod = $this->createFilterable(['method' => null]);
102+
103+
static::assertFalse($filters->includes($nullMethod));
104+
}
105+
106+
public function testWriteBaselineAndGetBaselineExcludeRoundTrip(): void
107+
{
108+
$this->tempFile = tempnam(sys_get_temp_dir(), 'api-tester-test-') . '.yaml';
109+
$filters = new Filters(null, null, $this->tempFile);
110+
111+
$exclude = [['id' => 'test_1'], ['id' => 'test_2']];
112+
$filters->writeBaseline($exclude);
113+
114+
$result = $filters->getBaseLineExclude();
115+
116+
static::assertCount(2, $result);
117+
static::assertSame('test_1', $result[0]['id']);
118+
static::assertSame('test_2', $result[1]['id']);
119+
}
120+
121+
/**
122+
* @param array<string, mixed> $props
123+
*/
124+
private function createFilterable(array $props): Filterable
125+
{
126+
return new class($props) implements Filterable {
127+
/**
128+
* @param array<string, mixed> $props
129+
*/
130+
public function __construct(
131+
private readonly array $props
132+
) {
133+
}
134+
135+
public function has(string $prop, $value, string $operator = '='): bool
136+
{
137+
if (!array_key_exists($prop, $this->props)) {
138+
return false;
139+
}
140+
141+
$propValue = $this->props[$prop];
142+
143+
return match ($operator) {
144+
'=' => $propValue === $value,
145+
'!=' => $propValue !== $value,
146+
'contains' => is_string($propValue) && is_string($value) && str_contains($propValue, $value),
147+
default => false,
148+
};
149+
}
150+
};
151+
}
152+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
$method->setAccessible(true);
38+
39+
$result = $method->invoke(null, $content);
40+
41+
static::assertSame('replaced_value', $result);
42+
} finally {
43+
unset($_ENV['TEST_API_VAR']);
44+
}
45+
}
46+
47+
public function testMissingEnvVarThrowsConfigurationException(): void
48+
{
49+
unset($_ENV['NONEXISTENT_VAR_FOR_TEST']);
50+
51+
$this->expectException(ConfigurationException::class);
52+
$this->expectExceptionMessage("'NONEXISTENT_VAR_FOR_TEST'");
53+
54+
$ref = new \ReflectionClass(PlanConfigLoader::class);
55+
$method = $ref->getMethod('process');
56+
$method->setAccessible(true);
57+
58+
$method->invoke(null, '%env(NONEXISTENT_VAR_FOR_TEST)%');
59+
}
60+
}

tests/Requester/RequesterTest.php

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace APITester\Tests\Requester;
6+
7+
use APITester\Requester\Requester;
8+
use Nyholm\Psr7\Request;
9+
use Nyholm\Psr7\Response;
10+
use PHPUnit\Framework\TestCase;
11+
use Psr\Http\Message\RequestInterface;
12+
use Psr\Http\Message\ResponseInterface;
13+
14+
final class RequesterTest extends TestCase
15+
{
16+
public function testSetBaseUriTrimsTrailingSlashes(): void
17+
{
18+
$requester = $this->createRequester();
19+
$requester->setBaseUri('https://example.com/api/');
20+
21+
static::assertSame('https://example.com/api', $requester->getBaseUri());
22+
}
23+
24+
public function testSetBaseUriTrimsMultipleTrailingSlashes(): void
25+
{
26+
$requester = $this->createRequester();
27+
$requester->setBaseUri('https://example.com///');
28+
29+
static::assertSame('https://example.com', $requester->getBaseUri());
30+
}
31+
32+
public function testResolveUriPrependsBaseUriForRelativePath(): void
33+
{
34+
$requester = $this->createRequester();
35+
$requester->setBaseUri('https://example.com/api');
36+
37+
$request = new Request('GET', '/users/1');
38+
$resolved = $requester->resolveUri($request);
39+
40+
static::assertSame('https://example.com/api/users/1', (string) $resolved->getUri());
41+
}
42+
43+
public function testResolveUriDoesNotPrependForAbsoluteUri(): void
44+
{
45+
$requester = $this->createRequester();
46+
$requester->setBaseUri('https://example.com/api');
47+
48+
$request = new Request('GET', 'https://other.com/resource');
49+
$resolved = $requester->resolveUri($request);
50+
51+
static::assertSame('https://other.com/resource', (string) $resolved->getUri());
52+
}
53+
54+
public function testResolveUriWithEmptyBaseUriReturnsUnchanged(): void
55+
{
56+
$requester = $this->createRequester();
57+
58+
$request = new Request('GET', '/users');
59+
$resolved = $requester->resolveUri($request);
60+
61+
static::assertSame('/users', (string) $resolved->getUri());
62+
}
63+
64+
/**
65+
* @dataProvider fillVarsProvider
66+
*/
67+
public function testFillVarsReplacesPlaceholders(string $subject, string $expected): void
68+
{
69+
$requester = $this->createRequesterWithVars(['name' => 'John', 'id' => '42']);
70+
71+
static::assertSame($expected, $requester->exposeFillVars($subject));
72+
}
73+
74+
/**
75+
* @return iterable<string, array{0: string, 1: string}>
76+
*/
77+
public function fillVarsProvider(): iterable
78+
{
79+
yield 'single placeholder' => ['/users/{id}', '/users/42'];
80+
yield 'multiple placeholders' => ['{name} has id {id}', 'John has id 42'];
81+
yield 'no placeholders' => ['plain text', 'plain text'];
82+
}
83+
84+
private function createRequester(): Requester
85+
{
86+
return new class() extends Requester {
87+
public static function getName(): string
88+
{
89+
return 'test';
90+
}
91+
92+
public function request(RequestInterface $request, string $id): RequestInterface
93+
{
94+
return $request;
95+
}
96+
97+
public function getResponse(string $id): ResponseInterface
98+
{
99+
return new Response();
100+
}
101+
};
102+
}
103+
104+
/**
105+
* @param array<string, string> $vars
106+
*/
107+
private function createRequesterWithVars(array $vars): Requester
108+
{
109+
return new class($vars) extends Requester {
110+
/**
111+
* @param array<string, string> $vars
112+
*/
113+
public function __construct(array $vars)
114+
{
115+
$ref = new \ReflectionProperty(Requester::class, 'vars');
116+
$ref->setAccessible(true);
117+
$ref->setValue($this, $vars);
118+
}
119+
120+
public static function getName(): string
121+
{
122+
return 'test';
123+
}
124+
125+
public function request(RequestInterface $request, string $id): RequestInterface
126+
{
127+
return $request;
128+
}
129+
130+
public function getResponse(string $id): ResponseInterface
131+
{
132+
return new Response();
133+
}
134+
135+
public function exposeFillVars(string $subject): string
136+
{
137+
return $this->fillVars($subject);
138+
}
139+
};
140+
}
141+
}

0 commit comments

Comments
 (0)