Skip to content

Commit f35cd9d

Browse files
committed
Merge branch 'main' into use-kit-domain
2 parents c334b92 + 389a483 commit f35cd9d

File tree

5 files changed

+154
-1
lines changed

5 files changed

+154
-1
lines changed

.env.dist.testing

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ TEST_SITE_ADMIN_USERNAME=admin
88
TEST_SITE_ADMIN_PASSWORD=password
99
TEST_SITE_WP_ADMIN_PATH=/wp-admin
1010
WP_ROOT_FOLDER="/home/runner/work/convertkit-wordpress-libraries/convertkit-wordpress-libraries/wordpress"
11+
WP_ENVIRONMENT_TYPE=local
1112
TEST_DB_NAME=test
1213
TEST_DB_HOST=localhost
1314
TEST_DB_USER=root
@@ -16,6 +17,7 @@ TEST_TABLE_PREFIX=wp_
1617
TEST_SITE_WP_URL=http://127.0.0.1
1718
TEST_SITE_WP_DOMAIN=127.0.0.1
1819
20+
TEST_SITE_CONFIG_FILE="/home/runner/work/convertkit-wordpress-libraries/convertkit-wordpress-libraries/wordpress/wp-content/plugins/convertkit-wordpress-libraries/tests/_support/WpunitTesterConfig.php"
1921
CONVERTKIT_API_BROADCAST_ID="8697158"
2022
CONVERTKIT_API_CUSTOM_FIELD_ID="258240"
2123
CONVERTKIT_API_FORM_ID="2765139"

src/class-convertkit-api-v4.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,6 +1468,14 @@ public function request( $endpoint, $method = 'get', $params = array(), $retry_i
14681468
break;
14691469
}
14701470

1471+
// Don't automatically refresh the expired access token if we're not on a production environment.
1472+
// This prevents the same ConvertKit account used on both a staging and production site from
1473+
// reaching a race condition where the staging site refreshes the token first, resulting in
1474+
// the production site unable to later refresh its same expired access token.
1475+
if ( ! $this->is_production_site() ) {
1476+
break;
1477+
}
1478+
14711479
// Refresh the access token.
14721480
$result = $this->refresh_token();
14731481

@@ -1506,6 +1514,27 @@ public function request( $endpoint, $method = 'get', $params = array(), $retry_i
15061514

15071515
}
15081516

1517+
/**
1518+
* Helper method to determine the WordPress environment type, checking
1519+
* if the wp_get_environment_type() function exists in WordPress (versions
1520+
* older than WordPress 5.5 won't have this function).
1521+
*
1522+
* @since 2.0.2
1523+
*
1524+
* @return bool
1525+
*/
1526+
private function is_production_site() {
1527+
1528+
// If the WordPress wp_get_environment_type() function isn't available,
1529+
// assume this is a production site.
1530+
if ( ! function_exists( 'wp_get_environment_type' ) ) {
1531+
return true;
1532+
}
1533+
1534+
return ( wp_get_environment_type() === 'production' );
1535+
1536+
}
1537+
15091538
/**
15101539
* Inspects the given API response for errors, returning them as a string.
15111540
*

tests/_support/WpunitTesterConfig.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
/**
3+
* Define any constants not supported by WPLoader, such as the environment type.
4+
*
5+
* See: https://github.com/lucatume/wp-browser/blob/master/docs/v3/modules/WPLoader.md,
6+
* parameter `configFile`.
7+
*
8+
* @package ConvertKit
9+
*/
10+
11+
define( 'WP_ENVIRONMENT_TYPE', $_ENV['WP_ENVIRONMENT_TYPE'] );

tests/wpunit.suite.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ modules:
2121
tablePrefix: "%TEST_TABLE_PREFIX%"
2222
domain: "%TEST_SITE_WP_DOMAIN%"
2323
adminEmail: "%TEST_SITE_ADMIN_EMAIL%"
24-
title: "Test"
24+
title: "Test"
25+
configFile: "%TEST_SITE_CONFIG_FILE%"

tests/wpunit/APITest.php

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,32 @@ public function testRefreshTokenWithInvalidToken()
491491
$this->assertEquals($result->get_error_code(), 'convertkit_api_error');
492492
}
493493

494+
/**
495+
* Test that making a call with an expired access token results in refresh_token()
496+
* not being automatically called, when the WordPress site isn't a production site.
497+
*
498+
* @since 2.0.2
499+
*
500+
* @return void
501+
*/
502+
public function testRefreshTokenWhenAccessTokenExpiredErrorOnNonProductionSite()
503+
{
504+
// If the refresh token action in the libraries is triggered when calling get_account(), the test failed.
505+
add_action(
506+
'convertkit_api_refresh_token',
507+
function() {
508+
$this->fail('`convertkit_api_refresh_token` was triggered when calling `get_account` with an expired access token on a non-production site.');
509+
}
510+
);
511+
512+
// Filter requests to mock the token expiry and refreshing the token.
513+
add_filter( 'pre_http_request', array( $this, 'mockAccessTokenExpiredResponse' ), 10, 3 );
514+
add_filter( 'pre_http_request', array( $this, 'mockRefreshTokenResponse' ), 10, 3 );
515+
516+
// Run request, which will trigger the above filters as if the token expired and refreshes automatically.
517+
$result = $this->api->get_account();
518+
}
519+
494520
/**
495521
* Test that supplying no API credentials to the API class returns a WP_Error.
496522
*
@@ -6240,6 +6266,90 @@ function( $response ) use ( $httpCode, $httpMessage, $body ) { // phpcs:ignore G
62406266
);
62416267
}
62426268

6269+
/**
6270+
* Mocks an API response as if the Access Token expired.
6271+
*
6272+
* @since 2.0.2
6273+
*
6274+
* @param mixed $response HTTP Response.
6275+
* @param array $parsed_args Request arguments.
6276+
* @param string $url Request URL.
6277+
* @return mixed
6278+
*/
6279+
public function mockAccessTokenExpiredResponse( $response, $parsed_args, $url )
6280+
{
6281+
// Only mock requests made to the /account endpoint.
6282+
if ( strpos( $url, 'https://api.convertkit.com/v4/account' ) === false ) {
6283+
return $response;
6284+
}
6285+
6286+
// Remove this filter, so we don't end up in a loop when retrying the request.
6287+
remove_filter( 'pre_http_request', array( $this, 'mockAccessTokenExpiredResponse' ) );
6288+
6289+
// Return a 401 unauthorized response with the errors body as if the API
6290+
// returned "The access token expired".
6291+
return array(
6292+
'headers' => array(),
6293+
'body' => wp_json_encode(
6294+
array(
6295+
'errors' => array(
6296+
'The access token expired',
6297+
),
6298+
)
6299+
),
6300+
'response' => array(
6301+
'code' => 401,
6302+
'message' => 'The access token expired',
6303+
),
6304+
'cookies' => array(),
6305+
'http_response' => null,
6306+
);
6307+
}
6308+
6309+
/**
6310+
* Mocks an API response as if a refresh token was used to fetch new tokens.
6311+
*
6312+
* @since 2.0.2
6313+
*
6314+
* @param mixed $response HTTP Response.
6315+
* @param array $parsed_args Request arguments.
6316+
* @param string $url Request URL.
6317+
* @return mixed
6318+
*/
6319+
public function mockRefreshTokenResponse( $response, $parsed_args, $url )
6320+
{
6321+
// Only mock requests made to the /token endpoint.
6322+
if ( strpos( $url, 'https://api.convertkit.com/oauth/token' ) === false ) {
6323+
return $response;
6324+
}
6325+
6326+
// Remove this filter, so we don't end up in a loop when retrying the request.
6327+
remove_filter( 'pre_http_request', array( $this, 'mockRefreshTokenResponse' ) );
6328+
6329+
// Return a mock access and refresh token for this API request, as calling
6330+
// refresh_token results in a new access and refresh token being provided,
6331+
// which would result in other tests breaking due to changed tokens.
6332+
return array(
6333+
'headers' => array(),
6334+
'body' => wp_json_encode(
6335+
array(
6336+
'access_token' => 'new-' . $_ENV['CONVERTKIT_OAUTH_ACCESS_TOKEN'],
6337+
'refresh_token' => 'new-' . $_ENV['CONVERTKIT_OAUTH_REFRESH_TOKEN'],
6338+
'token_type' => 'bearer',
6339+
'created_at' => strtotime( 'now' ),
6340+
'expires_in' => 10000,
6341+
'scope' => 'public',
6342+
)
6343+
),
6344+
'response' => array(
6345+
'code' => 200,
6346+
'message' => 'OK',
6347+
),
6348+
'cookies' => array(),
6349+
'http_response' => null,
6350+
);
6351+
}
6352+
62436353
/**
62446354
* Helper method to assert the given key exists as an array
62456355
* in the API response.

0 commit comments

Comments
 (0)