mirror of
https://github.com/wp-graphql/wp-graphql-woocommerce.git
synced 2026-08-14 12:53:44 +02:00
* fix: resolve REQUEST_URI fatal error and JWT key length issues in CI QLSessionHandlerTest::tearDown() was calling unset($_SERVER) which destroyed the entire superglobal. WordPress cron.php then fataled on shutdown when accessing $_SERVER['REQUEST_URI']. Changed to only unset the specific HTTP_WOOCOMMERCE_SESSION key. Also updated JWT secret keys to meet firebase/php-jwt v7's minimum 32-byte requirement for HS256 in both test config and Docker entrypoint. * fix: the rest of the files added * devops: php7.4 removed from matrix * chore: Linter compliances met * devops: More broken test updated * devops: Tests updated for CI * fix: QLSessionHandlerCest fixed * fix: QLSessionHandlerCest fixed * devops: CI fixed
296 lines
10 KiB
PHP
296 lines
10 KiB
PHP
<?php
|
|
/**
|
|
* Unit test for QL_Session_Handler
|
|
*/
|
|
|
|
use WPGraphQL\WooCommerce\Utils\QL_Session_Handler;
|
|
use WPGraphQL\WooCommerce\Vendor\Firebase\JWT\JWT;
|
|
use WPGraphQL\WooCommerce\Vendor\Firebase\JWT\Key;
|
|
|
|
if ( ! defined( 'GRAPHQL_WOOCOMMERCE_SECRET_KEY' ) ) {
|
|
define( 'GRAPHQL_WOOCOMMERCE_SECRET_KEY', 'testestestestestestestestestest!!' );
|
|
}
|
|
|
|
class QLSessionHandlerTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGraphQLTestCase {
|
|
|
|
/**
|
|
* @var int Original error reporting level, restored in tearDown.
|
|
*/
|
|
private $original_error_reporting;
|
|
|
|
public function setUp(): void {
|
|
parent::setUp();
|
|
|
|
// Clear session state.
|
|
unset( $_SERVER['HTTP_WOOCOMMERCE_SESSION'] );
|
|
unset( $_SERVER['HTTP_CART_TOKEN'] );
|
|
$customer_cookie_key = apply_filters( 'woocommerce_cookie', 'wp_woocommerce_session_' . COOKIEHASH );
|
|
unset( $_COOKIE[ $customer_cookie_key ] );
|
|
|
|
// Suppress E_USER_NOTICE from wc_setcookie() which fires when
|
|
// headers have already been sent (always the case in PHPUnit).
|
|
// WooCommerce's session handler calls setcookie() in many paths
|
|
// (init_session_cookie, destroy_session, forget_session, etc.)
|
|
// and triggers notices that Codeception promotes to exceptions.
|
|
$this->original_error_reporting = error_reporting();
|
|
error_reporting( $this->original_error_reporting & ~E_USER_NOTICE );
|
|
}
|
|
public function tearDown(): void {
|
|
unset( $_SERVER['HTTP_WOOCOMMERCE_SESSION'] );
|
|
unset( $_SERVER['HTTP_CART_TOKEN'] );
|
|
|
|
// Restore error reporting.
|
|
error_reporting( $this->original_error_reporting );
|
|
|
|
parent::tearDown();
|
|
}
|
|
|
|
// Tests
|
|
public function test_initializes() {
|
|
// Create session handler.
|
|
$session = new QL_Session_Handler();
|
|
|
|
$this->assertInstanceOf( QL_Session_Handler::class, $session );
|
|
}
|
|
|
|
public function test_init_on_graphql_request() {
|
|
// Simulate GraphQL HTTP Request.
|
|
add_filter( 'graphql_is_graphql_http_request', '__return_true' );
|
|
|
|
// Create session handler.
|
|
$session = new QL_Session_Handler();
|
|
|
|
// Assert session hasn't started.
|
|
$this->assertFalse( $session->has_session(), 'Shouldn\'t have a session yet' );
|
|
|
|
// Initialize session.
|
|
$session->init();
|
|
|
|
// Assert session has started.
|
|
$this->assertTrue( $session->has_session(), 'Should have session.' );
|
|
|
|
// Get token for future request.
|
|
$old_token = $session->build_token();
|
|
$decoded_old_token = JWT::decode( $old_token, new Key( GRAPHQL_WOOCOMMERCE_SECRET_KEY, 'HS256' ) );
|
|
|
|
// Sent token to HTTP header to simulate a new request.
|
|
$_SERVER['HTTP_WOOCOMMERCE_SESSION'] = 'Session ' . $old_token;
|
|
|
|
// Stale for 5 seconds so timers can update.
|
|
usleep( 1000000 );
|
|
|
|
// Initialize session token for next request.
|
|
remove_action( 'woocommerce_set_cart_cookies', [ $session, 'set_customer_session_token' ] );
|
|
remove_action( 'woographql_update_session', [ $session, 'set_customer_session_token' ] );
|
|
remove_action( 'shutdown', [ $session, 'save_data' ] );
|
|
|
|
// Create new session handler.
|
|
$session = new QL_Session_Handler();
|
|
$session->init();
|
|
$new_token = $session->build_token();
|
|
$decoded_new_token = JWT::decode( $new_token, new Key( GRAPHQL_WOOCOMMERCE_SECRET_KEY, 'HS256' ) );
|
|
|
|
// Assert new token is different than old token.
|
|
$this->assertNotEquals( $old_token, $new_token, 'New token should not match token from last request.' );
|
|
$this->assertGreaterThan( $decoded_old_token->exp, $decoded_new_token->exp );
|
|
|
|
// Assert customer ID match
|
|
$this->assertEquals( $decoded_old_token->data->customer_id, $decoded_new_token->data->customer_id );
|
|
}
|
|
|
|
public function test_init_on_non_graphql_request() {
|
|
// Create session handler.
|
|
$session = new QL_Session_Handler();
|
|
|
|
// Assert session hasn't started.
|
|
$this->assertFalse( $session->has_session(), 'Shouldn\'t have a session yet' );
|
|
|
|
// Initialize session.
|
|
$session->init();
|
|
|
|
// Add product to cart and start the session.
|
|
$this->factory->cart->add(
|
|
$this->factory->product->createSimple(),
|
|
$this->factory->product->createSimple(),
|
|
$this->factory->product->createSimple(),
|
|
$this->factory->product->createSimple(),
|
|
$this->factory->product->createSimple(),
|
|
$this->factory->product->createSimple(),
|
|
$this->factory->product->createSimple(),
|
|
);
|
|
|
|
// Assert no tokens are being issued.
|
|
$this->assertFalse( $session->sending_token(), 'Should not be issuing a new customer token.' );
|
|
$this->assertFalse( $session->build_token(), 'Should not be issuing a new customer token.' );
|
|
}
|
|
|
|
public function test_init_on_non_graphql_request_with_session_token() {
|
|
// Simulate GraphQL HTTP Request.
|
|
add_filter( 'graphql_is_graphql_http_request', '__return_true' );
|
|
|
|
// Create session handler.
|
|
$session = new QL_Session_Handler();
|
|
|
|
// Assert session hasn't started.
|
|
$this->assertFalse( $session->has_session(), 'Shouldn\'t have a session yet' );
|
|
|
|
// Initialize session.
|
|
$session->init();
|
|
|
|
// Assert session has started.
|
|
$this->assertTrue( $session->has_session(), 'Should have session.' );
|
|
|
|
// Get token for future request.
|
|
$token_to_session = $session->build_token();
|
|
|
|
// Remove GraphQL HTTP Request filter to simulate normal request.
|
|
remove_filter( 'graphql_is_graphql_http_request', '__return_true' );
|
|
|
|
// Sent token to HTTP header to simulate a new request.
|
|
$_SERVER['HTTP_WOOCOMMERCE_SESSION'] = 'Session ' . $token_to_session;
|
|
|
|
// Create session handler.
|
|
$session = new QL_Session_Handler();
|
|
|
|
// Assert session hasn't started.
|
|
$this->assertFalse( $session->has_session(), 'Shouldn\'t have a session yet' );
|
|
|
|
// Initialize session.
|
|
$session->init();
|
|
|
|
// Assert session has started.
|
|
$this->assertTrue( $session->has_session(), 'Should have session.' );
|
|
|
|
// Assert tokens are being issued.
|
|
$this->assertNotFalse( $session->build_token() );
|
|
}
|
|
|
|
public function test_get_session_token() {
|
|
// Simulate GraphQL HTTP Request.
|
|
add_filter( 'graphql_is_graphql_http_request', '__return_true' );
|
|
|
|
// Create session handler.
|
|
$session = new QL_Session_Handler();
|
|
|
|
// Expect token to be null.
|
|
$null_token = $session->get_session_token();
|
|
$this->assertFalse( $null_token, 'No token should exist.' );
|
|
|
|
// Set token in header.
|
|
$session->init_session_token();
|
|
$_SERVER['HTTP_WOOCOMMERCE_SESSION'] = 'Session ' . $session->build_token();
|
|
|
|
// Expect token to be value.
|
|
$token = $session->get_session_token();
|
|
$this->assertTrue( ! empty( $token->iat ) );
|
|
$this->assertTrue( ! empty( $token->exp ) );
|
|
$this->assertTrue( ! empty( $token->data ) );
|
|
}
|
|
|
|
public function test_get_session_header() {
|
|
// Create session handler.
|
|
$session = new QL_Session_Handler();
|
|
$session->init_session_token();
|
|
|
|
// Get the Auth header.
|
|
$null_header = $session->get_session_header();
|
|
|
|
$this->assertFalse( $null_header, 'No HTTP Header with session token should exist.' );
|
|
|
|
// Set token in header.
|
|
$_SERVER['HTTP_WOOCOMMERCE_SESSION'] = 'Session ' . $session->build_token();
|
|
|
|
$this->assertIsString( $session->get_session_header() );
|
|
}
|
|
|
|
public function test_build_token() {
|
|
// Simulate GraphQL HTTP Request.
|
|
add_filter( 'graphql_is_graphql_http_request', '__return_true' );
|
|
|
|
// Create session handler.
|
|
$session = new QL_Session_Handler();
|
|
|
|
// Should be invalid if run before initialization.
|
|
$invalid_token = $session->build_token();
|
|
$this->assertFalse( $invalid_token, 'Should be an invalid session token' );
|
|
|
|
// Should valid when run after initialization.
|
|
$session->init_session_token();
|
|
$token = $session->build_token();
|
|
|
|
$decode_token = JWT::decode( $token, new Key( GRAPHQL_WOOCOMMERCE_SECRET_KEY, 'HS256' ) );
|
|
$this->assertTrue( ! empty( $decode_token->iat ) );
|
|
$this->assertTrue( ! empty( $decode_token->exp ) );
|
|
$this->assertTrue( ! empty( $decode_token->data ) );
|
|
|
|
$this->assertEquals( $token, $session->build_token() );
|
|
}
|
|
|
|
public function test_set_customer_session_token() {
|
|
// Simulate GraphQL HTTP Request.
|
|
add_filter( 'graphql_is_graphql_http_request', '__return_true' );
|
|
|
|
// Create session handler.
|
|
$session = new QL_Session_Handler();
|
|
|
|
// Before initialization, the session should not be issuing tokens.
|
|
$this->assertFalse( $session->sending_token(), 'Should not be issuing a token before initialization.' );
|
|
|
|
// After initialization, session token should be available in response headers.
|
|
$session->init_session_token();
|
|
$session->set_customer_session_token( true );
|
|
|
|
// Remove any leftover header filters and re-apply to test the current session only.
|
|
remove_all_filters( 'graphql_response_headers_to_send' );
|
|
$session->set_customer_session_token( true );
|
|
$graphql_response_headers = apply_filters( 'graphql_response_headers_to_send', [] );
|
|
$this->assertArrayHasKey( 'woocommerce-session', $graphql_response_headers );
|
|
}
|
|
|
|
public function test_forget_session() {
|
|
// Simulate GraphQL HTTP Request.
|
|
add_filter( 'graphql_is_graphql_http_request', '__return_true' );
|
|
|
|
// Create session handler.
|
|
$session = new QL_Session_Handler();
|
|
$session->init_session_token();
|
|
|
|
// Get old token
|
|
$old_token = $session->build_token();
|
|
$this->assertIsString( $old_token );
|
|
|
|
// Forget session (suppress cookie notice in PHPUnit context).
|
|
@$session->forget_session();
|
|
|
|
// Get new token.
|
|
$new_token = $session->build_token();
|
|
$this->assertIsString( $old_token );
|
|
|
|
$this->assertNotEquals( $old_token, $new_token, 'Tokens should not match' );
|
|
}
|
|
|
|
public function test_get_client_session_id() {
|
|
// Create session handler.
|
|
$session = new QL_Session_Handler();
|
|
|
|
// Assert an random string returned, when valid "client_session_id" and "client_session_id_expiration" are not set.
|
|
$session->init_session_cookie();
|
|
$this->assertNotEquals( '', $session->get_client_session_id() );
|
|
|
|
$session->init_session_cookie();
|
|
$_REQUEST['_wc_cart'] = 'test';
|
|
$this->assertNotEquals( '', $session->get_client_session_id() );
|
|
|
|
$session->init_session_cookie();
|
|
$session->set( 'client_session_id', 'test-client-session-id' );
|
|
$session->set( 'client_session_id_expiration', '1' );
|
|
$this->assertNotEquals( 'test-client-session-id', $session->get_client_session_id() );
|
|
|
|
// Assert "test-client-session-id" is returned, when valid "client_session_id" and "client_session_id_expiration" are set.
|
|
$session->init_session_cookie();
|
|
$session->set( 'client_session_id', 'test-client-session-id' );
|
|
$session->set( 'client_session_id_expiration', ( time() + 3600 ) );
|
|
$this->assertEquals( 'test-client-session-id', $session->get_client_session_id() );
|
|
}
|
|
}
|