6
6
7
7
use Symfony \Component \HttpClient \DataCollector \HttpClientDataCollector ;
8
8
use Symfony \Component \VarDumper \Cloner \Data ;
9
+ use function array_change_key_case ;
10
+ use function array_filter ;
11
+ use function array_intersect_key ;
9
12
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 ;
11
18
12
19
trait HttpClientAssertionsTrait
13
20
{
@@ -31,104 +38,35 @@ trait HttpClientAssertionsTrait
31
38
* @param array<string,string|string[]> $expectedHeaders
32
39
*/
33
40
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 ' ,
39
46
): 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 ;
90
52
}
91
53
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 ;
95
57
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
+ );
123
63
124
- if ($ expectedBody === null && $ expectedHeaders === []) {
125
- $ expectedRequestHasBeenFound = true ;
126
- break ;
127
- }
128
- }
64
+ return $ bodyMatches && $ headersMatch ;
65
+ },
66
+ );
129
67
130
- $ this ->assertTrue (
131
- $ expectedRequestHasBeenFound ,
68
+ $ this ->assertNotEmpty (
69
+ $ matchingRequests ,
132
70
sprintf ('The expected request has not been called: "%s" - "%s" ' , $ expectedMethod , $ expectedUrl )
133
71
);
134
72
}
@@ -142,22 +80,9 @@ public function assertHttpClientRequest(
142
80
* $I->assertHttpClientRequestCount(3);
143
81
* ```
144
82
*/
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__ ));
161
86
}
162
87
163
88
/**
@@ -170,35 +95,63 @@ public function assertHttpClientRequestCount(
170
95
*/
171
96
public function assertNotHttpClientRequest (
172
97
string $ unexpectedUrl ,
173
- string $ expectedMethod = 'GET ' ,
98
+ string $ unexpectedMethod = 'GET ' ,
174
99
string $ httpClientId = 'http_client ' ,
175
100
): 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
+ );
177
105
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
180
130
*/
181
131
$ clients = $ httpClientCollector ->getClients ();
182
132
183
133
if (!array_key_exists ($ httpClientId , $ clients )) {
184
134
$ this ->fail (sprintf ('HttpClient "%s" is not registered. ' , $ httpClientId ));
185
135
}
186
136
187
- $ unexpectedUrlHasBeenFound = false ;
137
+ return $ clients [$ httpClientId ]['traces ' ];
138
+ }
188
139
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
+ }
197
146
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
+ };
202
155
}
203
156
204
157
protected function grabHttpClientCollector (string $ function ): HttpClientDataCollector
0 commit comments