From bbab382f7779adf8955c47cb41bb399f79c923fb Mon Sep 17 00:00:00 2001 From: Daniel Brotsky Date: Tue, 18 Nov 2025 22:02:46 -0800 Subject: [PATCH 1/3] Fix exception thrown by debug logging. The response to the get call is not a string; its body is a string. --- src/BMLT/Integration.php | 74 ++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/BMLT/Integration.php b/src/BMLT/Integration.php index 0d1e1796..b7064ba2 100644 --- a/src/BMLT/Integration.php +++ b/src/BMLT/Integration.php @@ -1,18 +1,18 @@ . @@ -24,7 +24,7 @@ class Integration { use \bmltwf\BMLTWF_Debug; - + /** * Wrapper for wp_remote_request that logs both request and response * @@ -41,10 +41,10 @@ private function bmltwf_wp_remote_request($url, $args) if ($body) { $this->debug_log("REQUEST BODY: " . $body); } - + // Make the request $response = \wp_remote_request($url, $args); - + // Log the response if (is_wp_error($response)) { $this->debug_log("RESPONSE ERROR: " . $response->get_error_message()); @@ -53,21 +53,21 @@ private function bmltwf_wp_remote_request($url, $args) $response_body = \wp_remote_retrieve_body($response); $this->debug_log("RESPONSE CODE: " . $response_code); $this->debug_log("RESPONSE BODY: " . $response_body); - + // Log error responses with their body if ($response_code >= 400) { $this->debug_log("ERROR RESPONSE ($response_code): " . $response_body); - + // Use the new debug_http_error function for 500 errors if ($response_code >= 500) { $this->debug_http_error($response); } } } - + return $response; } - + /** * Wrapper for wp_remote_get that logs both request and response * @@ -80,7 +80,7 @@ private function bmltwf_wp_remote_get($url, $args = array()) $args['method'] = 'GET'; return $this->bmltwf_wp_remote_request($url, $args); } - + /** * Wrapper for wp_remote_post that logs both request and response * @@ -230,7 +230,7 @@ function updateMeeting($change) { // Validate meeting data $filtered_change = $this->validateMeetingData($change); - + // Workaround for timeZone required $filtered_change["timeZone"] = null; @@ -238,10 +238,10 @@ function updateMeeting($change) if (is_wp_error($filtered_change)) { return $filtered_change; } - + $this->debug_log("updateMeeting change"); $this->debug_log($filtered_change); - + if (array_key_exists('formatIds', $filtered_change)) { $filtered_change['formatIds'] = $this->removeLocations($filtered_change['formatIds']); } @@ -263,7 +263,7 @@ function updateMeeting($change) $url = get_option('bmltwf_bmlt_server_address') . 'api/v1/meetings/' . $filtered_change['id']; $this->debug_bmlt_payload($url, 'PATCH', $filtered_change); - + $response = $this->bmltwf_wp_remote_request($url, array( 'method' => 'PATCH', 'headers' => array( @@ -367,7 +367,7 @@ public function getAllMeetings() 'longitude', 'comments' ]; - + $body = json_decode(\wp_remote_retrieve_body($response),true); if ($body === null) { return new \WP_Error('bmltwf', 'Invalid JSON response from BMLT server'); @@ -570,7 +570,7 @@ public function getMeetingStates() if (is_wp_error($response) || (\wp_remote_retrieve_response_code($response) != 200)) { return new \WP_Error('bmltwf', __('BMLT Configuration Error - Unable to retrieve server info', 'bmlt-workflow')); } - // $this->debug_log(\wp_remote_retrieve_body($response)); + // $this->debug_log(\wp_remote_retrieve_body($response)); $arr = json_decode(\wp_remote_retrieve_body($response), true); if ($arr === null || !isset($arr[0])) { return new \WP_Error('bmltwf', __('Invalid server info response', 'bmlt-workflow')); @@ -609,7 +609,7 @@ public function getMeetingCounties() * getGmapsKey * * workaround for client/server side maps key issues - * + * * @return \WP_Error|string */ public function getGmapsKey() @@ -641,9 +641,9 @@ public function getGmapsKey() $this->debug_log("*** ADMIN URL " . $url); $response = $this->getv2($url, $this->cookies); - - preg_match('/"google_api_key":"(.*?)",/', \wp_remote_retrieve_body($response), $matches); - $this->debug_log("bmlt gmaps response - ".$response); + $body = \wp_remote_retrieve_body($response); + preg_match('/"google_api_key":"(.*?)",/', $body, $matches); + $this->debug_log("bmlt gmaps response - ".$body); $this->debug_log("retrieved gmaps key"); $gmaps_key = isset($matches[1]) ? $matches[1] : ''; @@ -655,7 +655,7 @@ public function getGmapsKey() /** * Get the schema for meeting data validation - * + * * @return array Schema with field names and their expected types */ private function getMeetingSchema() @@ -701,10 +701,10 @@ private function getMeetingSchema() 'id' => 'integer' ]; } - + /** * Validate meeting data against schema - * + * * @param array $meeting Meeting data to validate * @return array|\WP_Error Filtered meeting data or error */ @@ -713,12 +713,12 @@ private function validateMeetingData($meeting) $allowed_keys = $this->getMeetingSchema(); $filtered_meeting = []; $errors = []; - + foreach ($meeting as $key => $value) { if (array_key_exists($key, $allowed_keys)) { $expected_type = $allowed_keys[$key]; $valid = false; - + switch ($expected_type) { case 'integer': $valid = is_int($value) || (is_string($value) && ctype_digit($value)); @@ -750,7 +750,7 @@ private function validateMeetingData($meeting) default: $valid = true; // Unknown type, accept as is } - + if ($valid) { $filtered_meeting[$key] = $value; } else { @@ -758,26 +758,26 @@ private function validateMeetingData($meeting) } } } - + if (!empty($errors)) { $this->debug_log("Type validation errors:"); $this->debug_log($errors); return new \WP_Error('bmltwf', __('Invalid meeting data format', 'bmlt-workflow'), $errors); } - + return $filtered_meeting; } - + function createMeeting($meeting) { // Validate meeting data $filtered_meeting = $this->validateMeetingData($meeting); - + // If validation returned an error, return it if (is_wp_error($filtered_meeting)) { return $filtered_meeting; } - + $this->debug_log("createMeeting change"); $this->debug_log($filtered_meeting); @@ -788,7 +788,7 @@ function createMeeting($meeting) $this->debug_log("formatIds missing or not an array"); return new \WP_Error('bmltwf', __('formatIds is required and must be an array', 'bmlt-workflow')); } - + // Workaround for timeZone required $filtered_meeting["timeZone"] = null; @@ -872,7 +872,7 @@ public function getDefaultLatLong() if (is_wp_error($response) || (\wp_remote_retrieve_response_code($response) != 200)) { return new \WP_Error('bmltwf', __('BMLT Configuration Error - Unable to retrieve server info', 'bmlt-workflow')); } - // $this->debug_log(\wp_remote_retrieve_body($response)); + // $this->debug_log(\wp_remote_retrieve_body($response)); $arr = json_decode(\wp_remote_retrieve_body($response), true); if ($arr === null || !isset($arr[0])) { return new \WP_Error('bmltwf', __('Invalid server info response', 'bmlt-workflow')); @@ -993,7 +993,7 @@ function authenticateRootServer() $this->debug_log("inside authenticateRootServer v3 auth"); $url = get_option('bmltwf_bmlt_server_address') . "api/v1/auth/token"; $response = \wp_remote_post($url, array('body' => http_build_query($postargs))); - + if (is_wp_error($response)) { return $response; } @@ -1163,4 +1163,4 @@ public function postUnauthenticatedRootServerRequest($url, $postargs) return $val; } -} \ No newline at end of file +} From e25f7592c44c2968e62aff7804fbb84e5492dc02 Mon Sep 17 00:00:00 2001 From: Daniel Brotsky Date: Tue, 18 Nov 2025 22:26:09 -0800 Subject: [PATCH 2/3] Fix search pattern for GMaps key. Now we look for a pattern that matches both old and new server versions. --- src/BMLT/Integration.php | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/BMLT/Integration.php b/src/BMLT/Integration.php index b7064ba2..a16c3b7e 100644 --- a/src/BMLT/Integration.php +++ b/src/BMLT/Integration.php @@ -642,12 +642,19 @@ public function getGmapsKey() $response = $this->getv2($url, $this->cookies); $body = \wp_remote_retrieve_body($response); - preg_match('/"google_api_key":"(.*?)",/', $body, $matches); - $this->debug_log("bmlt gmaps response - ".$body); - $this->debug_log("retrieved gmaps key"); - $gmaps_key = isset($matches[1]) ? $matches[1] : ''; - - \update_option('bmltwf_bmlt_google_maps_key', $gmaps_key); + $this->debug_log("bmlt gmaps response - ".$body); + preg_match('/"google_api_key":"(.*?)",/', $body, $matches); + $gmaps_key = isset($matches[1]) ? $matches[1] : ''; + if ($gmaps_key == '') { + preg_match('/googleApiKey: *\'(.*?)\',/', $body, $matches); + $gmaps_key = isset($matches[1]) ? $matches[1] : ''; + } + if ($gmaps_key != '') { + $this->debug_log("retrieved gmaps key: '" . $gmaps_key . "'"); + \update_option('bmltwf_bmlt_google_maps_key', $gmaps_key); + } else { + $this->debug_log("failed to retrieve gmaps key"); + } return $gmaps_key; } From 454b3330f613bd8153b3941a30584fb0e16aa8ed Mon Sep 17 00:00:00 2001 From: Daniel Brotsky Date: Thu, 20 Nov 2025 23:03:18 -0800 Subject: [PATCH 3/3] Add tests for gmaps key response parsing. --- tests/phpunit/src/BMLT/IntegrationTest.php | 148 +++++++++++++-------- 1 file changed, 92 insertions(+), 56 deletions(-) diff --git a/tests/phpunit/src/BMLT/IntegrationTest.php b/tests/phpunit/src/BMLT/IntegrationTest.php index 5794f5c8..b5629d7a 100644 --- a/tests/phpunit/src/BMLT/IntegrationTest.php +++ b/tests/phpunit/src/BMLT/IntegrationTest.php @@ -1,18 +1,18 @@ . @@ -224,7 +224,7 @@ public function test_can_call_wp_locale_to_bmlt_locale_with_french(): void $response = $integration->wp_locale_to_bmlt_locale(); $this->assertEquals($response, "fr"); } - + /** * @covers bmltwf\BMLT\Integration::wp_locale_to_bmlt_locale */ @@ -338,7 +338,7 @@ public function test_is_valid_bmlt_server_returns_false_if_not_valid(): void $response = $integration->is_valid_bmlt_server(""); $this->assertFalse(($response)); } - + /** * @covers bmltwf\BMLT\Integration::is_valid_bmlt_server **/ @@ -385,7 +385,7 @@ public function test_is_supported_server_returns_false_if_not_supported(): void * @covers bmltwf\BMLT\Integration::getMeetingFormats * @covers bmltwf\BMLT\Integration::getMeetingFormatsv3 **/ - + public function test_getMeetingFormatsv3(): void { @@ -409,7 +409,7 @@ public function test_getMeetingFormatsv3(): void * @covers bmltwf\BMLT\Integration::deleteMeeting * @covers bmltwf\BMLT\Integration::deleteMeetingv3 **/ - + public function test_deleteMeeting_against_v3_with_valid_meeting(): void { Functions\when('wp_remote_retrieve_response_code')->justReturn(204); @@ -423,7 +423,7 @@ public function test_deleteMeeting_against_v3_with_valid_meeting(): void * @covers bmltwf\BMLT\Integration::deleteMeeting * @covers bmltwf\BMLT\Integration::deleteMeetingv3 **/ - + public function test_deleteMeeting_against_v3_with_invalid_meeting(): void { Functions\when('wp_remote_retrieve_response_code')->justReturn(404); @@ -438,7 +438,7 @@ public function test_deleteMeeting_against_v3_with_invalid_meeting(): void * @covers bmltwf\BMLT\Integration::updateMeeting * @covers bmltwf\BMLT\Integration::updateMeetingv3 **/ - + public function test_changeMeeting_against_v3_with_valid_meeting(): void { @@ -457,7 +457,7 @@ public function test_changeMeeting_against_v3_with_valid_meeting(): void * @covers bmltwf\BMLT\Integration::updateMeeting * @covers bmltwf\BMLT\Integration::updateMeetingv3 **/ - + public function test_updateMeeting_against_v3_with_invalid_meeting(): void { @@ -477,7 +477,7 @@ public function test_updateMeeting_against_v3_with_invalid_meeting(): void * @covers bmltwf\BMLT\Integration::updateMeeting * @covers bmltwf\BMLT\Integration::updateMeetingv3 **/ - + public function test_updateMeeting_against_v3_with_invalid_change(): void { @@ -495,7 +495,7 @@ public function test_updateMeeting_against_v3_with_invalid_change(): void * @covers bmltwf\BMLT\Integration::getServiceBodies * @covers bmltwf\BMLT\Integration::getServiceBodiesv3 **/ - + public function test_getServiceBodies_against_v3(): void { $servicebodies = <<returnArg(); @@ -541,7 +541,7 @@ public function test_createMeeting_against_v3_with_valid_meeting(): void $response = $integration->createMeeting($meeting); $this->assertNotInstanceOf(WP_Error::class, $response); } - + /** * @covers bmltwf\BMLT\Integration::createMeeting */ @@ -549,17 +549,17 @@ public function test_createMeeting_adds_timezone_null(): void { // Capture the request body $capturedBody = null; - + Functions\when('\wp_remote_request')->alias(function($url, $args) use (&$capturedBody) { if (isset($args['body'])) { $capturedBody = $args['body']; } return ['response' => ['code' => 201]]; // Mock successful response }); - + Functions\when('\wp_remote_retrieve_body')->justReturn($this->formats); Functions\when('wp_remote_retrieve_response_code')->justReturn(201); - + $meeting = array( "name" => "Test Meeting", "startTime" => "11:01:00", @@ -570,10 +570,10 @@ public function test_createMeeting_adds_timezone_null(): void "venueType" => "3", "published" => "1" ); - + $integration = new Integration(true, "3.0.0", "token", time()+2000); $integration->createMeeting($meeting); - + // Verify timeZone is null in the request body $this->assertNotNull($capturedBody, "Request body was not captured"); $decodedBody = json_decode($capturedBody, true); @@ -585,7 +585,7 @@ public function test_createMeeting_adds_timezone_null(): void /** * @covers bmltwf\BMLT\Integration::createMeeting **/ - + public function test_createMeeting_against_v3_with_invalid_meeting(): void { Functions\when('\wp_remote_retrieve_body')->justReturn($this->formats); @@ -615,7 +615,7 @@ public function test_createMeeting_against_v3_with_invalid_meeting(): void $response = $integration->createMeeting($invalid_meeting); $this->assertInstanceOf(WP_Error::class, $response); } - + /** * @covers bmltwf\BMLT\Integration::createMeeting * @covers bmltwf\BMLT\Integration::validateMeetingData @@ -624,17 +624,17 @@ public function test_createMeeting_filters_invalid_data(): void { // Capture the request body $capturedBody = null; - + Functions\when('\wp_remote_request')->alias(function($url, $args) use (&$capturedBody) { if (isset($args['body'])) { $capturedBody = $args['body']; } return ['response' => ['code' => 201]]; // Mock successful response }); - + Functions\when('\wp_remote_retrieve_body')->justReturn($this->formats); Functions\when('wp_remote_retrieve_response_code')->justReturn(201); - + // Meeting data with valid fields and invalid fields $meeting = array( "name" => "Test Meeting", @@ -648,17 +648,17 @@ public function test_createMeeting_filters_invalid_data(): void "invalid_field" => "This should be filtered out", // Invalid field "another_invalid" => ["this", "should", "be", "removed"] // Invalid field ); - + $integration = new Integration(true, "3.0.0", "token", time()+2000); $integration->createMeeting($meeting); - + // Verify invalid fields are filtered out $this->assertNotNull($capturedBody, "Request body was not captured"); $decodedBody = json_decode($capturedBody, true); $this->assertIsArray($decodedBody, "Request body is not valid JSON"); $this->assertArrayNotHasKey('invalid_field', $decodedBody, "Invalid field was not filtered out"); $this->assertArrayNotHasKey('another_invalid', $decodedBody, "Invalid field was not filtered out"); - + // Verify valid fields are present $this->assertArrayHasKey('name', $decodedBody); $this->assertArrayHasKey('startTime', $decodedBody); @@ -734,10 +734,10 @@ public function test_getMeeting_with_invalid_id(): void Functions\when('wp_remote_retrieve_response_code')->justReturn(404); Functions\when('\wp_remote_retrieve_response_message')->justReturn('Not Found'); Functions\when('\wp_remote_retrieve_body')->justReturn(''); - + $integration = new Integration(true, "3.0.0", "token", time() + 2000); $result = $integration->getMeeting(99999); - + $this->assertInstanceOf(WP_Error::class, $result); } @@ -748,14 +748,14 @@ public function test_updateMeeting_removes_virtual_formats(): void { // Capture the request body $capturedBody = null; - + Functions\when('\wp_remote_request')->alias(function($url, $args) use (&$capturedBody) { if (isset($args['body'])) { $capturedBody = $args['body']; } return ['response' => ['code' => 204]]; // Mock successful response }); - + // Mock format data that would be returned by getMeetingFormats $mockFormats = [ 54 => ['key_string' => 'VM', 'name_string' => 'Virtual Meeting'], @@ -764,10 +764,10 @@ public function test_updateMeeting_removes_virtual_formats(): void 1 => ['key_string' => 'B', 'name_string' => 'Beginners'], 2 => ['key_string' => 'O', 'name_string' => 'Open'] ]; - + Functions\when('\wp_remote_retrieve_body')->justReturn(json_encode($mockFormats)); Functions\when('wp_remote_retrieve_response_code')->justReturn(204); - + // Meeting data with formats that should be filtered $meeting = array( "id" => 123, @@ -776,18 +776,18 @@ public function test_updateMeeting_removes_virtual_formats(): void "venueType" => "1", "published" => "1" ); - + $integration = Mockery::mock(Integration::class)->makePartial(); $integration->shouldReceive('getMeetingFormats')->andReturn($mockFormats); $integration->shouldReceive('is_v3_token_valid')->andReturn(true); - + $integration->updateMeeting($meeting); - + // Verify the request body $this->assertNotNull($capturedBody, "Request body was not captured"); $decodedBody = json_decode($capturedBody, true); $this->assertIsArray($decodedBody, "Request body is not valid JSON"); - + // Verify that formatIds were filtered correctly $this->assertArrayHasKey('formatIds', $decodedBody, "formatIds field is missing"); $this->assertCount(2, $decodedBody['formatIds'], "Should only have 2 formats after filtering"); @@ -797,7 +797,7 @@ public function test_updateMeeting_removes_virtual_formats(): void $this->assertNotContains(55, $decodedBody['formatIds'], "Format 55 (TC) should be removed"); $this->assertNotContains(56, $decodedBody['formatIds'], "Format 56 (HY) should be removed"); } - + /** * @covers bmltwf\BMLT\Integration::updateMeeting */ @@ -805,17 +805,17 @@ public function test_updateMeeting_adds_timezone_null(): void { // Capture the request body $capturedBody = null; - + Functions\when('\wp_remote_request')->alias(function($url, $args) use (&$capturedBody) { if (isset($args['body'])) { $capturedBody = $args['body']; } return ['response' => ['code' => 204]]; // Mock successful response }); - + Functions\when('\wp_remote_retrieve_body')->justReturn($this->formats); Functions\when('wp_remote_retrieve_response_code')->justReturn(204); - + $meeting = array( "id" => 123, "name" => "Test Meeting Update", @@ -827,10 +827,10 @@ public function test_updateMeeting_adds_timezone_null(): void "venueType" => "1", "published" => "1" ); - + $integration = new Integration(true, "3.0.0", "token", time()+2000); $integration->updateMeeting($meeting); - + // Verify timeZone is null in the request body $this->assertNotNull($capturedBody, "Request body was not captured"); $decodedBody = json_decode($capturedBody, true); @@ -838,7 +838,7 @@ public function test_updateMeeting_adds_timezone_null(): void $this->assertArrayHasKey('timeZone', $decodedBody, "timeZone field is missing"); $this->assertNull($decodedBody['timeZone'], "timeZone should be null"); } - + /** * @covers bmltwf\BMLT\Integration::updateMeeting * @covers bmltwf\BMLT\Integration::validateMeetingData @@ -847,17 +847,17 @@ public function test_updateMeeting_filters_invalid_data(): void { // Capture the request body $capturedBody = null; - + Functions\when('\wp_remote_request')->alias(function($url, $args) use (&$capturedBody) { if (isset($args['body'])) { $capturedBody = $args['body']; } return ['response' => ['code' => 204]]; // Mock successful response }); - + Functions\when('\wp_remote_retrieve_body')->justReturn($this->formats); Functions\when('wp_remote_retrieve_response_code')->justReturn(204); - + // Meeting data with valid fields and invalid fields $meeting = array( "id" => 123, // Required for update @@ -873,10 +873,10 @@ public function test_updateMeeting_filters_invalid_data(): void "random_data" => array("foo" => "bar"), // Invalid field "non_schema_field" => true // Invalid field ); - + $integration = new Integration(true, "3.0.0", "token", time()+2000); $integration->updateMeeting($meeting); - + // Verify invalid fields are filtered out $this->assertNotNull($capturedBody, "Request body was not captured"); $decodedBody = json_decode($capturedBody, true); @@ -884,7 +884,7 @@ public function test_updateMeeting_filters_invalid_data(): void $this->assertArrayNotHasKey('invalid_field', $decodedBody, "Invalid field was not filtered out"); $this->assertArrayNotHasKey('random_data', $decodedBody, "Invalid field was not filtered out"); $this->assertArrayNotHasKey('non_schema_field', $decodedBody, "Invalid field was not filtered out"); - + // Verify valid fields are present $this->assertArrayHasKey('id', $decodedBody); $this->assertArrayHasKey('name', $decodedBody); @@ -899,11 +899,11 @@ public function test_token_expiration_handling(): void { // Test expired token $integration = new Integration(true, "3.0.0", "token", time() - 1000); - + $reflection = new ReflectionClass($integration); $method = $reflection->getMethod('is_v3_token_valid'); $method->setAccessible(true); - + $this->assertFalse($method->invoke($integration)); } @@ -916,11 +916,47 @@ public function test_authentication_failure(): void Functions\when('\wp_remote_retrieve_response_message')->justReturn('Unauthorized'); Functions\when('\wp_remote_post')->returnArg(); Functions\when('\wp_remote_retrieve_body')->justReturn(''); - + $integration = new Integration(true, "3.0.0"); $result = $integration->authenticateRootServer(); - + $this->assertInstanceOf(WP_Error::class, $result); } -} \ No newline at end of file + /** + * covers bmltwf\BMLT\Integration::getGmapApiKey + */ + public function test_getGmapsKey() { + $mock_body_v3 = '...stuff...\n "google_api_key":"test-api-key",\n...more stuff...\n,'; + $mock_body_v4 = '...stuff...\n googleApiKey: \'test-api-key\',\n...more stuff...\n,'; + $mock_body_bad = 'something unexpected'; + Functions\when('\get_option')->alias(function($value, $default = false) { + if($value === 'bmltwf_bmlt_password') { + return(json_decode('{"config":{"size":"MzI=","salt":"\/5ObzNuYZ\/Y5aoYTsr0sZw==","limit_ops":"OA==","limit_mem":"NTM2ODcwOTEy","alg":"Mg==","nonce":"VukDVzDkAaex\/jfB"},"encrypted":"fertj+qRqQrs9tC+Cc32GrXGImHMfiLyAW7sV6Xojw=="}',true)); + } elseif($value === 'bmltwf_google_maps_key' || $value === 'bmltwf_bmlt_google_maps_key') { + return ''; + } elseif($value === 'bmltwf_bmlt_server_address') { + return 'https://example.com/'; + } + return 'true'; + }); + Functions\when('\update_option')->justReturn(true); + Functions\when('wp_remote_retrieve_response_code')->justReturn(200); + Functions\when('\wp_remote_retrieve_cookies')->justReturn([]); + + Functions\when('\wp_remote_retrieve_body')->justReturn($mock_body_bad); + $integration = new Integration(true, "3.0.0", 'token', time()+2000); + $result = $integration->getGmapsKey(); + $this->assertEquals('', $result); + + Functions\when('\wp_remote_retrieve_body')->justReturn($mock_body_v3); + $integration = new Integration(true, "3.0.0", 'token', time()+2000); + $result = $integration->getGmapsKey(); + $this->assertEquals('test-api-key', $result); + + Functions\when('\wp_remote_retrieve_body')->justReturn($mock_body_v4); + $integration = new Integration(true, "4.0.0", 'token', time()+2000); + $result = $integration->getGmapsKey(); + $this->assertEquals('test-api-key', $result); + } +}