Skip to content

Commit b0df879

Browse files
committed
HttpClient PHPStan fix
1 parent 6e6d709 commit b0df879

File tree

1 file changed

+79
-126
lines changed

1 file changed

+79
-126
lines changed

src/Codeception/Module/Symfony/HttpClientAssertionsTrait.php

Lines changed: 79 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,15 @@
66

77
use Symfony\Component\HttpClient\DataCollector\HttpClientDataCollector;
88
use Symfony\Component\VarDumper\Cloner\Data;
9+
use function array_change_key_case;
10+
use function array_filter;
11+
use function array_intersect_key;
912
use function array_key_exists;
10-
use function is_string;
13+
use function in_array;
14+
use function is_array;
15+
use function is_object;
16+
use function method_exists;
17+
use function sprintf;
1118

1219
trait HttpClientAssertionsTrait
1320
{
@@ -31,104 +38,35 @@ trait HttpClientAssertionsTrait
3138
* @param array<string,string|string[]> $expectedHeaders
3239
*/
3340
public function assertHttpClientRequest(
34-
string $expectedUrl,
35-
string $expectedMethod = 'GET',
36-
string|array|null $expectedBody = null,
37-
array $expectedHeaders = [],
38-
string $httpClientId = 'http_client',
41+
string $expectedUrl,
42+
string $expectedMethod = 'GET',
43+
string|array|null $expectedBody = null,
44+
array $expectedHeaders = [],
45+
string $httpClientId = 'http_client',
3946
): void {
40-
$httpClientCollector = $this->grabHttpClientCollector(__FUNCTION__);
41-
42-
/**
43-
* @var array<string, array{traces: list<array{
44-
* info: array{url: string},
45-
* url: string,
46-
* method: string,
47-
* options: array{body: mixed, json: mixed, headers?: mixed}
48-
* }>} > $clients
49-
*/
50-
$clients = $httpClientCollector->getClients();
51-
52-
if (!array_key_exists($httpClientId, $clients)) {
53-
$this->fail(sprintf('HttpClient "%s" is not registered.', $httpClientId));
54-
}
55-
56-
/**
57-
* @var list<array{
58-
* info: array{url: string},
59-
* url: string,
60-
* method: string,
61-
* options: array{body: mixed, json: mixed, headers?: mixed}
62-
* }> $traces
63-
*/
64-
$traces = $clients[$httpClientId]['traces'];
65-
66-
$expectedRequestHasBeenFound = false;
67-
68-
foreach ($traces as $trace) {
69-
if (($expectedUrl !== $trace['info']['url'] && $expectedUrl !== $trace['url'])
70-
|| $expectedMethod !== $trace['method']
71-
) {
72-
continue;
73-
}
74-
75-
if ($expectedBody !== null) {
76-
$actualBody = null;
77-
78-
if (isset($trace['options']['body']) && !isset($trace['options']['json'])) {
79-
$body = $trace['options']['body'];
80-
$actualBody = is_string($body)
81-
? $body
82-
: ($body instanceof Data ? $body->getValue(true) : null);
83-
}
84-
85-
if (!isset($trace['options']['body']) && isset($trace['options']['json'])) {
86-
$json = $trace['options']['json'];
87-
$actualBody = is_string($json)
88-
? $json
89-
: ($json instanceof Data ? $json->getValue(true) : null);
47+
$matchingRequests = array_filter(
48+
$this->getHttpClientTraces($httpClientId, __FUNCTION__),
49+
function (array $trace) use ($expectedUrl, $expectedMethod, $expectedBody, $expectedHeaders): bool {
50+
if (!$this->matchesUrlAndMethod($trace, $expectedUrl, $expectedMethod)) {
51+
return false;
9052
}
9153

92-
if ($actualBody === null || $expectedBody !== $actualBody) {
93-
continue;
94-
}
54+
$options = $trace['options'] ?? [];
55+
$actualBody = $this->extractValue($options['body'] ?? $options['json'] ?? null);
56+
$bodyMatches = $expectedBody === null || $expectedBody === $actualBody;
9557

96-
if ($expectedHeaders === []) {
97-
$expectedRequestHasBeenFound = true;
98-
break;
99-
}
100-
}
101-
102-
if ($expectedHeaders !== []) {
103-
/**
104-
* @var array<string, mixed> $actualHeaders
105-
*/
106-
$actualHeaders = $trace['options']['headers'] ?? [];
107-
108-
foreach ($actualHeaders as $headerKey => $actualHeaderValue) {
109-
if (!array_key_exists($headerKey, $expectedHeaders)) {
110-
continue;
111-
}
112-
113-
$actualHeaderValue = is_object($actualHeaderValue) && method_exists($actualHeaderValue, 'getValue')
114-
? $actualHeaderValue->getValue(true)
115-
: $actualHeaderValue;
116-
117-
if ($expectedHeaders[$headerKey] === $actualHeaderValue) {
118-
$expectedRequestHasBeenFound = true;
119-
break 2;
120-
}
121-
}
122-
}
58+
$headersMatch = $expectedHeaders === [] || (
59+
is_array($headerValues = $this->extractValue($options['headers'] ?? []))
60+
&& ($normalizedExpected = array_change_key_case($expectedHeaders))
61+
=== array_intersect_key(array_change_key_case($headerValues), $normalizedExpected)
62+
);
12363

124-
if ($expectedBody === null && $expectedHeaders === []) {
125-
$expectedRequestHasBeenFound = true;
126-
break;
127-
}
128-
}
64+
return $bodyMatches && $headersMatch;
65+
},
66+
);
12967

130-
$this->assertTrue(
131-
$expectedRequestHasBeenFound,
68+
$this->assertNotEmpty(
69+
$matchingRequests,
13270
sprintf('The expected request has not been called: "%s" - "%s"', $expectedMethod, $expectedUrl)
13371
);
13472
}
@@ -142,22 +80,9 @@ public function assertHttpClientRequest(
14280
* $I->assertHttpClientRequestCount(3);
14381
* ```
14482
*/
145-
public function assertHttpClientRequestCount(
146-
int $count,
147-
string $httpClientId = 'http_client',
148-
): void {
149-
$httpClientCollector = $this->grabHttpClientCollector(__FUNCTION__);
150-
151-
/**
152-
* @var array<string, array{traces: list<mixed>}> $clients
153-
*/
154-
$clients = $httpClientCollector->getClients();
155-
156-
if (!array_key_exists($httpClientId, $clients)) {
157-
$this->fail(sprintf('HttpClient "%s" is not registered.', $httpClientId));
158-
}
159-
160-
$this->assertCount($count, $clients[$httpClientId]['traces']);
83+
public function assertHttpClientRequestCount(int $count, string $httpClientId = 'http_client'): void
84+
{
85+
$this->assertCount($count, $this->getHttpClientTraces($httpClientId, __FUNCTION__));
16186
}
16287

16388
/**
@@ -170,35 +95,63 @@ public function assertHttpClientRequestCount(
17095
*/
17196
public function assertNotHttpClientRequest(
17297
string $unexpectedUrl,
173-
string $expectedMethod = 'GET',
98+
string $unexpectedMethod = 'GET',
17499
string $httpClientId = 'http_client',
175100
): void {
176-
$httpClientCollector = $this->grabHttpClientCollector(__FUNCTION__);
101+
$matchingRequests = array_filter(
102+
$this->getHttpClientTraces($httpClientId, __FUNCTION__),
103+
fn(array $trace): bool => $this->matchesUrlAndMethod($trace, $unexpectedUrl, $unexpectedMethod)
104+
);
177105

178-
/**
179-
* @var array<string, array{traces: list<array{info: array{url: string}, url: string, method: string}>}> $clients
106+
$this->assertEmpty(
107+
$matchingRequests,
108+
sprintf('Unexpected URL was called: "%s" - "%s"', $unexpectedMethod, $unexpectedUrl)
109+
);
110+
}
111+
112+
/**
113+
* @return list<array{
114+
* info: array{url: string},
115+
* url: string,
116+
* method: string,
117+
* options?: array{body?: mixed, json?: mixed, headers?: mixed}
118+
* }>
119+
*/
120+
private function getHttpClientTraces(string $httpClientId, string $function): array
121+
{
122+
$httpClientCollector = $this->grabHttpClientCollector($function);
123+
124+
/** @var array<string, array{traces: list<array{
125+
* info: array{url: string},
126+
* url: string,
127+
* method: string,
128+
* options?: array{body?: mixed, json?: mixed, headers?: mixed}
129+
* }>}> $clients
180130
*/
181131
$clients = $httpClientCollector->getClients();
182132

183133
if (!array_key_exists($httpClientId, $clients)) {
184134
$this->fail(sprintf('HttpClient "%s" is not registered.', $httpClientId));
185135
}
186136

187-
$unexpectedUrlHasBeenFound = false;
137+
return $clients[$httpClientId]['traces'];
138+
}
188139

189-
foreach ($clients[$httpClientId]['traces'] as $trace) {
190-
if (($unexpectedUrl === $trace['info']['url'] || $unexpectedUrl === $trace['url'])
191-
&& $expectedMethod === $trace['method']
192-
) {
193-
$unexpectedUrlHasBeenFound = true;
194-
break;
195-
}
196-
}
140+
/** @param array{info: array{url: string}, url: string, method: string} $trace */
141+
private function matchesUrlAndMethod(array $trace, string $expectedUrl, string $expectedMethod): bool
142+
{
143+
return in_array($expectedUrl, [$trace['info']['url'], $trace['url']], true)
144+
&& $expectedMethod === $trace['method'];
145+
}
197146

198-
$this->assertFalse(
199-
$unexpectedUrlHasBeenFound,
200-
sprintf('Unexpected URL called: "%s" - "%s"', $expectedMethod, $unexpectedUrl)
201-
);
147+
private function extractValue(mixed $value): mixed
148+
{
149+
return match (true) {
150+
$value instanceof Data => $value->getValue(true),
151+
is_object($value) && method_exists($value, 'getValue') => $value->getValue(true),
152+
is_object($value) && method_exists($value, '__toString') => (string) $value,
153+
default => $value,
154+
};
202155
}
203156

204157
protected function grabHttpClientCollector(string $function): HttpClientDataCollector

0 commit comments

Comments
 (0)