Skip to content

Commit da82d4d

Browse files
committed
feat(api tester): enable specific user authentication for given endpoint
1 parent 189326d commit da82d4d

File tree

8 files changed

+83
-70
lines changed

8 files changed

+83
-70
lines changed

src/Authenticator/Authenticator.php

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public function authenticate(Auth $config, Api $api, Requester $requester): Toke
4747
$body['access_token'],
4848
explode(' ', $config->getBody()['scope'] ?? ''),
4949
$body['refresh_token'] ?? null,
50+
$config->getFilters(),
5051
$body['token_type'] ?? null,
5152
$body['expires_in'] ?? null,
5253
);

src/Config/Auth.php

+12
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ final class Auth
1616
*/
1717
private array $body = [];
1818

19+
private ?Filters $filters = null;
20+
1921
public function __construct(
2022
private readonly string $name
2123
) {
@@ -57,4 +59,14 @@ public function setBody(array $body): void
5759
{
5860
$this->body = $body;
5961
}
62+
63+
public function getFilters(): ?Filters
64+
{
65+
return $this->filters;
66+
}
67+
68+
public function setFilters(Filters $filters): void
69+
{
70+
$this->filters = $filters;
71+
}
6072
}

src/Config/Filters.php

+52
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace APITester\Config;
66

7+
use APITester\Util\Filterable;
8+
use Symfony\Component\Yaml\Tag\TaggedValue;
79
use Symfony\Component\Yaml\Yaml;
810

911
final class Filters
@@ -108,6 +110,39 @@ public function writeBaseline(array $exclude): void
108110
);
109111
}
110112

113+
public function includes(Filterable $object): bool
114+
{
115+
$include = true;
116+
foreach ($this->getInclude() as $item) {
117+
$include = true;
118+
foreach ($item as $key => $value) {
119+
[$operator, $value] = $this->handleTags($value);
120+
if (!$object->has($key, $value, $operator)) {
121+
$include = false;
122+
continue 2;
123+
}
124+
}
125+
break;
126+
}
127+
128+
if (!$include) {
129+
return false;
130+
}
131+
132+
foreach ($this->getExclude() as $item) {
133+
foreach ($item as $key => $value) {
134+
[$operator, $value] = $this->handleTags($value);
135+
if (!$object->has($key, $value, $operator)) {
136+
continue 2;
137+
}
138+
}
139+
$include = false;
140+
break;
141+
}
142+
143+
return $include;
144+
}
145+
111146
/**
112147
* @return array{'exclude': ?array<int, array<string, string>>}
113148
*/
@@ -116,4 +151,21 @@ private function getBaseLineData(): array
116151
/** @var array{'exclude': ?array<int, array<string, string>>} */
117152
return Yaml::parseFile($this->getBaseline());
118153
}
154+
155+
/**
156+
* @return array{0: string, 1: string|int}
157+
*/
158+
private function handleTags(string|int|TaggedValue $value): array
159+
{
160+
$operator = '=';
161+
162+
if ($value instanceof TaggedValue) {
163+
if ($value->getTag() === 'NOT') {
164+
$operator = '!=';
165+
}
166+
$value = (string) $value->getValue();
167+
}
168+
169+
return [$operator, $value];
170+
}
119171
}

src/Definition/Example/OperationExample.php

+7-5
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ public function setParameter(
9696
foreach ($value as $attribute => $attributeValue) {
9797
$this->{$paramProp}[$name][$attribute] = (string) $attributeValue;
9898
}
99+
99100
return $this;
100101
}
101102

@@ -388,11 +389,12 @@ public function setAuthenticationHeaders(Tokens $tokens, bool $ignoreScope = fal
388389
$token = $tokens->first();
389390
} else {
390391
/** @var Token|null $token */
391-
$token = $tokens->where(
392-
'scopes',
393-
'includes',
394-
$scopes
395-
)->first();
392+
$token = $tokens->filter(
393+
static fn (Token $token) => $token->getFilters()?->includes($operation) ?? false
394+
)
395+
->first() ?? $tokens->where('scopes', 'includes', $scopes)
396+
->first()
397+
;
396398
}
397399

398400
if ($token !== null) {

src/Definition/Token.php

+8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace APITester\Definition;
66

7+
use APITester\Config\Filters;
8+
79
final class Token
810
{
911
private readonly string $type;
@@ -19,13 +21,19 @@ public function __construct(
1921
private readonly string $accessToken,
2022
private readonly array $scopes = [],
2123
private readonly ?string $refreshToken = null,
24+
private readonly ?Filters $filters = null,
2225
?string $type = null,
2326
?int $expiresIn = null
2427
) {
2528
$this->type = $type ?? 'Bearer';
2629
$this->expiresIn = $expiresIn ?? 3600;
2730
}
2831

32+
public function getFilters(): ?Filters
33+
{
34+
return $this->filters;
35+
}
36+
2937
public function getAccessToken(): string
3038
{
3139
return $this->accessToken;

src/Test/Suite.php

+1-63
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,12 @@
1111
use APITester\Preparator\Exception\PreparatorLoadingException;
1212
use APITester\Preparator\TestCasesPreparator;
1313
use APITester\Requester\Requester;
14-
use APITester\Util\Filterable;
1514
use APITester\Util\Traits\TimeBoundTrait;
1615
use Illuminate\Support\Collection;
1716
use PHPUnit\Framework\TestResult;
1817
use PHPUnit\Framework\TestSuite;
1918
use Psr\Log\LoggerInterface;
2019
use Psr\Log\NullLogger;
21-
use Symfony\Component\Yaml\Tag\TaggedValue;
2220

2321
/**
2422
* @internal
@@ -87,43 +85,6 @@ public function setLogger(LoggerInterface $logger): void
8785
$this->logger = $logger;
8886
}
8987

90-
/**
91-
* @param array<array<string, string>> $includeFilters
92-
* @param array<array<string, string>> $excludeFilters
93-
*/
94-
public function includes(Filterable $object, array $includeFilters = [], array $excludeFilters = []): bool
95-
{
96-
$include = true;
97-
foreach ($includeFilters as $item) {
98-
$include = true;
99-
foreach ($item as $key => $value) {
100-
[$operator, $value] = $this->handleTags($value);
101-
if (!$object->has($key, $value, $operator)) {
102-
$include = false;
103-
continue 2;
104-
}
105-
}
106-
break;
107-
}
108-
109-
if (!$include) {
110-
return false;
111-
}
112-
113-
foreach ($excludeFilters as $item) {
114-
foreach ($item as $key => $value) {
115-
[$operator, $value] = $this->handleTags($value);
116-
if (!$object->has($key, $value, $operator)) {
117-
continue 2;
118-
}
119-
}
120-
$include = false;
121-
break;
122-
}
123-
124-
return $include;
125-
}
126-
12788
/**
12889
* @param array<array<string, string>> $filter
12990
*
@@ -233,13 +194,7 @@ private function prepareTestCases(): void
233194

234195
private function filterOperation(Operations $operations): Operations
235196
{
236-
return $operations->filter(
237-
fn (Operation $operation) => $this->includes(
238-
$operation,
239-
$this->filters->getInclude(),
240-
$this->filters->getExclude(),
241-
)
242-
);
197+
return $operations->filter(fn (Operation $operation) => $this->filters->includes($operation));
243198
}
244199

245200
/**
@@ -301,21 +256,4 @@ private function indexInPart(?string $part, int $index, int $total): bool
301256

302257
return false;
303258
}
304-
305-
/**
306-
* @return array{0: string, 1: string|int}
307-
*/
308-
private function handleTags(string|int|\Symfony\Component\Yaml\Tag\TaggedValue $value): array
309-
{
310-
$operator = '=';
311-
312-
if ($value instanceof TaggedValue) {
313-
if ($value->getTag() === 'NOT') {
314-
$operator = '!=';
315-
}
316-
$value = (string) $value->getValue();
317-
}
318-
319-
return [$operator, $value];
320-
}
321259
}

src/Test/TestCase.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ final class TestCase implements \JsonSerializable, Filterable
7272

7373
private ?string $operation;
7474

75-
private ?string $preparator;
75+
private string $preparator;
7676

7777
private Validator $validator;
7878

tests/Test/TestCaseTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
final class TestCaseTest extends UnitTestCase
1717
{
18-
private ?TestCase $testCase;
18+
private TestCase $testCase;
1919

2020
public function testGivenValidResponseRegardingSchemaAndShouldValidateSchemaResponseOptionIsDisabledWhenAssertThenNoErrorIsThrown(
2121
): void {

0 commit comments

Comments
 (0)