@@ -210,6 +210,163 @@ public function mock_unauthorized_response() {
210210 ];
211211 }
212212
213+ /**
214+ * Test WC_Stripe_API::log_error_response() as called from WC_Stripe_API::request() and WC_Stripe_API::retrieve().
215+ *
216+ * @param array|WP_Error $response The mock response.
217+ * @param string $api The API endpoint.
218+ * @param string $method The HTTP method used for the request.
219+ * @param array|null $request_data The mock request data. Only used for POST requests.
220+ * @dataProvider provide_test_log_error_response_tests
221+ */
222+ public function test_log_error_response ( $ response , string $ api , string $ method , ?array $ request_data = null ) {
223+ $ expected_url = WC_Stripe_API::ENDPOINT . $ api ;
224+
225+ $ pre_http_filter = function ( $ return_value , $ parsed_args , $ url ) use ( $ response , $ method , $ expected_url ) {
226+ if ( $ url !== $ expected_url ) {
227+ return $ return_value ;
228+ }
229+ if ( ( $ parsed_args ['method ' ] ?? null ) !== $ method ) {
230+ return $ return_value ;
231+ }
232+
233+ return $ response ;
234+ };
235+
236+ $ mock_logger = $ this ->createMock ( \WC_Logger::class );
237+ \WC_Stripe_Logger::$ logger = $ mock_logger ;
238+
239+ $ expected_data_keys = [
240+ 'stripe_request_id ' ,
241+ 'response ' ,
242+ ];
243+
244+ if ( 'POST ' === $ method ) {
245+ $ expected_data_keys [] = 'idempotency_key ' ;
246+ $ expected_data_keys [] = 'request ' ;
247+ }
248+
249+ if (
250+ is_wp_error ( $ response ) &&
251+ 'http_request_failed ' === $ response ->get_error_code () &&
252+ // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
253+ __ ( 'A valid URL was not provided. ' ) === $ response ->get_error_message ()
254+ ) {
255+ $ expected_data_keys [] = 'resolved_ip_address ' ;
256+ $ expected_data_keys [] = 'validation_details ' ;
257+ }
258+
259+ $ expected_data_keys_callback = function ( $ context ) use ( $ expected_data_keys ) {
260+ $ this ->assertLessThanOrEqual ( count ( $ context ), count ( $ expected_data_keys ) );
261+ foreach ( $ expected_data_keys as $ key ) {
262+ $ this ->assertArrayHasKey ( $ key , $ context );
263+ }
264+ return true ;
265+ };
266+
267+ $ mock_logger ->expects ( $ this ->once () )
268+ ->method ( 'error ' )
269+ ->with (
270+ $ this ->stringStartsWith ( "Stripe API error: $ method $ api " ),
271+ $ this ->callback ( $ expected_data_keys_callback )
272+ );
273+
274+ add_filter ( 'pre_http_request ' , $ pre_http_filter , 10 , 3 );
275+
276+ if ( 'GET ' === $ method ) {
277+ $ result = WC_Stripe_API::retrieve ( $ api );
278+ } else {
279+ $ caught_exception = null ;
280+ try {
281+ $ result = WC_Stripe_API::request ( $ request_data , $ api , $ method , false );
282+ } catch ( \WC_Stripe_Exception $ stripe_exception ) {
283+ $ caught_exception = $ stripe_exception ;
284+ }
285+ }
286+
287+ // Clean up before we perform any assertions.
288+ remove_filter ( 'pre_http_request ' , $ pre_http_filter );
289+ \WC_Stripe_Logger::$ logger = null ;
290+
291+ if ( 'GET ' === $ method ) {
292+ $ this ->assertInstanceof ( \WP_Error::class, $ result );
293+ $ this ->assertEquals ( 'stripe_error ' , $ result ->get_error_code () );
294+ $ this ->assertEquals ( __ ( 'There was a problem connecting to the Stripe API endpoint. ' , 'woocommerce-gateway-stripe ' ), $ result ->get_error_message () );
295+ } else {
296+ $ this ->assertInstanceof ( \WC_Stripe_Exception::class, $ caught_exception );
297+ $ this ->assertEquals ( print_r ( $ response , true ), $ caught_exception ->getMessage () );
298+ $ this ->assertEquals ( __ ( 'There was a problem connecting to the Stripe API endpoint. ' , 'woocommerce-gateway-stripe ' ), $ caught_exception ->getLocalizedMessage () );
299+ }
300+ }
301+
302+ /**
303+ * Data provider for {@see test_log_error_response()}.
304+ *
305+ * @return array
306+ */
307+ public function provide_test_log_error_response_tests (): array {
308+ return [
309+ 'generic error for GET account ' => [
310+ 'response ' => new \WP_Error ( 'mock_error ' , 'Mock Error ' ),
311+ 'api ' => 'account ' ,
312+ 'method ' => 'GET ' ,
313+ ],
314+ 'generic error for POST account ' => [
315+ 'response ' => new \WP_Error ( 'mock_error ' , 'Mock Error ' ),
316+ 'api ' => 'account ' ,
317+ 'method ' => 'POST ' ,
318+ 'request_data ' => [ 'test ' => 'test ' ],
319+ ],
320+ 'general http_request_failed error for GET account ' => [
321+ 'response ' => new \WP_Error ( 'http_request_failed ' , 'Mock Error ' ),
322+ 'api ' => 'account ' ,
323+ 'method ' => 'GET ' ,
324+ ],
325+ 'general http_request_failed error for POST account ' => [
326+ 'response ' => new \WP_Error ( 'http_request_failed ' , 'Mock Error ' ),
327+ 'api ' => 'account ' ,
328+ 'method ' => 'POST ' ,
329+ 'request_data ' => [ 'test ' => 'test ' ],
330+ ],
331+ 'URL validation http_request_failed error for GET account ' => [
332+ // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
333+ 'response ' => new \WP_Error ( 'http_request_failed ' , __ ( 'A valid URL was not provided. ' ) ),
334+ 'api ' => 'account ' ,
335+ 'method ' => 'GET ' ,
336+ ],
337+ 'URL validation http_request_failed error for POST account ' => [
338+ // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
339+ 'response ' => new \WP_Error ( 'http_request_failed ' , __ ( 'A valid URL was not provided. ' ) ),
340+ 'api ' => 'account ' ,
341+ 'method ' => 'POST ' ,
342+ 'request_data ' => [ 'test ' => 'test ' ],
343+ ],
344+ 'empty response body for GET account ' => [
345+ 'response ' => [
346+ 'response ' => [
347+ 'code ' => 200 ,
348+ 'message ' => 'OK ' ,
349+ ],
350+ 'body ' => '' ,
351+ ],
352+ 'api ' => 'account ' ,
353+ 'method ' => 'GET ' ,
354+ ],
355+ 'empty response body for POST account ' => [
356+ 'response ' => [
357+ 'response ' => [
358+ 'code ' => 200 ,
359+ 'message ' => 'OK ' ,
360+ ],
361+ 'body ' => '' ,
362+ ],
363+ 'api ' => 'account ' ,
364+ 'method ' => 'POST ' ,
365+ 'request_data ' => [ 'test ' => 'test ' ],
366+ ],
367+ ];
368+ }
369+
213370 public function provide_test_should_detach_payment_method_from_customer (): array {
214371 return [
215372 'test mode from non-admin context should detach ' => [
0 commit comments