|
4 | 4 |
|
5 | 5 | namespace Codeception\Module\Symfony; |
6 | 6 |
|
| 7 | +use PHPUnit\Framework\Assert; |
| 8 | +use Stringable; |
7 | 9 | use Symfony\Component\HttpClient\DataCollector\HttpClientDataCollector; |
8 | 10 | use Symfony\Component\VarDumper\Cloner\Data; |
9 | 11 |
|
10 | 12 | use function array_change_key_case; |
11 | | -use function array_filter; |
12 | 13 | use function array_intersect_key; |
13 | | -use function in_array; |
14 | 14 | use function is_array; |
15 | 15 | use function is_object; |
16 | | -use function is_string; |
17 | 16 | use function method_exists; |
18 | 17 | use function sprintf; |
19 | 18 |
|
@@ -45,35 +44,8 @@ public function assertHttpClientRequest( |
45 | 44 | array $expectedHeaders = [], |
46 | 45 | string $httpClientId = 'http_client', |
47 | 46 | ): void { |
48 | | - $matchingTraces = array_filter( |
49 | | - $this->getHttpClientTraces($httpClientId, __FUNCTION__), |
50 | | - function ($trace) use ($expectedUrl, $expectedMethod, $expectedBody, $expectedHeaders): bool { |
51 | | - if (!is_array($trace) || !$this->matchesUrlAndMethod($trace, $expectedUrl, $expectedMethod)) { |
52 | | - return false; |
53 | | - } |
54 | | - |
55 | | - $options = $this->extractValue($trace['options'] ?? []); |
56 | | - $options = is_array($options) ? $options : []; |
57 | | - |
58 | | - $expectedTraceBody = $this->extractValue($options['body'] ?? $options['json'] ?? null); |
59 | | - if ($expectedBody !== null && $expectedBody !== $expectedTraceBody) { |
60 | | - return false; |
61 | | - } |
62 | | - |
63 | | - if ($expectedHeaders === []) { |
64 | | - return true; |
65 | | - } |
66 | | - |
67 | | - $actualHeaders = $this->extractValue($options['headers'] ?? []); |
68 | | - $expected = array_change_key_case($expectedHeaders); |
69 | | - |
70 | | - return is_array($actualHeaders) |
71 | | - && $expected === array_intersect_key(array_change_key_case($actualHeaders), $expected); |
72 | | - }, |
73 | | - ); |
74 | | - |
75 | | - $this->assertNotEmpty( |
76 | | - $matchingTraces, |
| 47 | + $this->assertTrue( |
| 48 | + $this->hasHttpClientRequest($httpClientId, __FUNCTION__, $expectedUrl, $expectedMethod, $expectedBody, $expectedHeaders), |
77 | 49 | sprintf('The expected request has not been called: "%s" - "%s"', $expectedMethod, $expectedUrl) |
78 | 50 | ); |
79 | 51 | } |
@@ -106,53 +78,79 @@ public function assertNotHttpClientRequest( |
106 | 78 | string $unexpectedMethod = 'GET', |
107 | 79 | string $httpClientId = 'http_client', |
108 | 80 | ): void { |
109 | | - $matchingTraces = array_filter( |
110 | | - $this->getHttpClientTraces($httpClientId, __FUNCTION__), |
111 | | - fn($trace): bool => is_array($trace) && $this->matchesUrlAndMethod($trace, $unexpectedUrl, $unexpectedMethod), |
112 | | - ); |
113 | | - |
114 | | - $this->assertEmpty( |
115 | | - $matchingTraces, |
| 81 | + $this->assertFalse( |
| 82 | + $this->hasHttpClientRequest($httpClientId, __FUNCTION__, $unexpectedUrl, $unexpectedMethod), |
116 | 83 | sprintf('Unexpected URL was called: "%s" - "%s"', $unexpectedMethod, $unexpectedUrl) |
117 | 84 | ); |
118 | 85 | } |
119 | 86 |
|
| 87 | + /** |
| 88 | + * @param string|array<mixed>|null $expectedBody |
| 89 | + * @param array<string,string|string[]> $expectedHeaders |
| 90 | + */ |
| 91 | + private function hasHttpClientRequest( |
| 92 | + string $httpClientId, |
| 93 | + string $function, |
| 94 | + string $expectedUrl, |
| 95 | + string $expectedMethod, |
| 96 | + string|array|null $expectedBody = null, |
| 97 | + array $expectedHeaders = [] |
| 98 | + ): bool { |
| 99 | + $expectedHeadersLower = $expectedHeaders === [] ? [] : array_change_key_case($expectedHeaders); |
| 100 | + |
| 101 | + foreach ($this->getHttpClientTraces($httpClientId, $function) as $trace) { |
| 102 | + if (!is_array($trace) || ($trace['method'] ?? null) !== $expectedMethod) { |
| 103 | + continue; |
| 104 | + } |
| 105 | + |
| 106 | + $info = $this->extractValue($trace['info'] ?? []); |
| 107 | + $infoUrl = is_array($info) ? ($info['url'] ?? $info['original_url'] ?? null) : null; |
| 108 | + if ($expectedUrl !== $infoUrl && $expectedUrl !== ($trace['url'] ?? null)) { |
| 109 | + continue; |
| 110 | + } |
| 111 | + |
| 112 | + if ($expectedBody === null && $expectedHeadersLower === []) { |
| 113 | + return true; |
| 114 | + } |
| 115 | + |
| 116 | + $options = $this->extractValue($trace['options'] ?? []); |
| 117 | + $options = is_array($options) ? $options : []; |
| 118 | + if ($expectedBody !== null && $expectedBody !== $this->extractValue($options['body'] ?? $options['json'] ?? null)) { |
| 119 | + continue; |
| 120 | + } |
| 121 | + |
| 122 | + if ($expectedHeadersLower === []) { |
| 123 | + return true; |
| 124 | + } |
| 125 | + |
| 126 | + $actualHeaders = $this->extractValue($options['headers'] ?? []); |
| 127 | + if (is_array($actualHeaders) && $expectedHeadersLower === array_intersect_key(array_change_key_case($actualHeaders), $expectedHeadersLower)) { |
| 128 | + return true; |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | + return false; |
| 133 | + } |
| 134 | + |
120 | 135 | /** @return array<mixed> */ |
121 | 136 | private function getHttpClientTraces(string $httpClientId, string $function): array |
122 | 137 | { |
123 | 138 | $clients = $this->grabHttpClientCollector($function)->getClients(); |
124 | | - |
125 | | - if (!isset($clients[$httpClientId])) { |
126 | | - $this->fail(sprintf('HttpClient "%s" is not registered.', $httpClientId)); |
| 139 | + if (!isset($clients[$httpClientId]) || !is_array($clients[$httpClientId])) { |
| 140 | + Assert::fail(sprintf('HttpClient "%s" is not registered.', $httpClientId)); |
127 | 141 | } |
128 | 142 |
|
129 | 143 | /** @var array{traces: array<mixed>} $clientData */ |
130 | 144 | $clientData = $clients[$httpClientId]; |
131 | 145 | return $clientData['traces']; |
132 | 146 | } |
133 | 147 |
|
134 | | - /** @param array<mixed> $trace */ |
135 | | - private function matchesUrlAndMethod(array $trace, string $expectedUrl, string $expectedMethod): bool |
136 | | - { |
137 | | - $method = $trace['method'] ?? null; |
138 | | - $url = $trace['url'] ?? null; |
139 | | - |
140 | | - if (!is_string($method) || !is_string($url) || $expectedMethod !== $method) { |
141 | | - return false; |
142 | | - } |
143 | | - |
144 | | - $info = $this->extractValue($trace['info'] ?? []); |
145 | | - $infoUrl = is_array($info) ? ($info['url'] ?? $info['original_url'] ?? null) : null; |
146 | | - |
147 | | - return in_array($expectedUrl, [$infoUrl, $url], true); |
148 | | - } |
149 | | - |
150 | 148 | private function extractValue(mixed $value): mixed |
151 | 149 | { |
152 | 150 | return match (true) { |
153 | 151 | $value instanceof Data => $value->getValue(true), |
154 | 152 | is_object($value) && method_exists($value, 'getValue') => $value->getValue(true), |
155 | | - is_object($value) && method_exists($value, '__toString') => (string) $value, |
| 153 | + $value instanceof Stringable => (string) $value, |
156 | 154 | default => $value, |
157 | 155 | }; |
158 | 156 | } |
|
0 commit comments