woocommerce-paypal-payments/tests/integration/PHPUnit/StoreSync/ReplaceCartEndpointTest.php

148 lines
5.5 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare( strict_types = 1 );
namespace WooCommerce\PayPalCommerce\Tests\Integration\StoreSync;
use Mockery;
use Psr\Log\NullLogger;
use WP_REST_Request;
use WP_REST_Response;
use WooCommerce\PayPalCommerce\StoreSync\Endpoint\ReplaceCartEndpoint;
use WooCommerce\PayPalCommerce\StoreSync\Helper\PayPalOrderManager;
use WooCommerce\PayPalCommerce\StoreSync\Schema\PayPalCart;
use WooCommerce\PayPalCommerce\StoreSync\Session\AgenticSessionHandler;
use WooCommerce\PayPalCommerce\StoreSync\Validation\StoreValidation;
use WooCommerce\PayPalCommerce\Tests\Integration\IntegrationMockedTestCase;
/**
* @covers \WooCommerce\PayPalCommerce\StoreSync\Endpoint\ReplaceCartEndpoint
*/
class ReplaceCartEndpointTest extends IntegrationMockedTestCase {
private ReplaceCartEndpoint $endpoint;
private AgenticSessionHandler $session_handler;
public function setUp(): void {
parent::setUp();
$c = $this->getContainer();
$this->session_handler = $c->get( 'agentic.session.handler' );
$order_manager = Mockery::mock( PayPalOrderManager::class );
$order_manager->allows( 'update_order' )->andReturn( true );
$this->endpoint = new ReplaceCartEndpoint(
$c->get( 'agentic.auth.provider' ),
$this->session_handler,
$c->get( 'agentic.helper.session-manager' ),
$c->get( 'agentic.response.factory' ),
$c->get( 'agentic.validation.processor' ),
new NullLogger(),
$order_manager,
$c->get( 'agentic.store.data' )
);
}
/**
* GIVEN a cart session was created with a known ec_token
* WHEN replace_cart() is called with a valid replacement payload
* THEN the response status is 200
* AND payment_method.token equals the ec_token from the original session (not the request body)
* AND id matches the cart session id
* AND status is INCOMPLETE
* AND validation_status is VALID with no validation_issues
* AND totals contains subtotal, shipping, tax and total with currency_code and value
* AND available_shipping_options has at least one entry with exactly one selected option
* AND customer and shipping_address keys exist in the response
* AND the sum of item price × quantity equals the subtotal (in cents)
*/
public function test_replace_cart_preserves_ec_token_in_response(): void {
$ec_token = 'test-preserved-session-ec-token';
// Data for create cart.
$cart_data_1 = array(
'items' => array(
array(
'variant_id' => 'DUMMY_SIMPLE_SKU_01',
'quantity' => 1,
),
),
'payment_method' => array( 'type' => 'paypal' ),
);
// Replacement payload — no token in payment_method; token must come from session.
$cart_data_2 = array(
'items' => array(
array(
'variant_id' => 'DUMMY_SIMPLE_SKU_01',
'quantity' => 2,
),
),
'shipping_address' => array(
'address_line_1' => '456 Fictional St',
'admin_area_2' => 'San Jose',
'postal_code' => '90210',
'country_code' => 'US',
),
'payment_method' => array( 'type' => 'paypal' ),
);
$initial_cart = PayPalCart::from_array( $cart_data_1, new StoreValidation() );
$cart_id = $this->session_handler->create_cart_session( $initial_cart, $ec_token );
$body = (string) json_encode( $cart_data_2 );
$request = new WP_REST_Request( 'PUT' );
$request->set_param( 'cart_id', $cart_id );
$request->set_body( $body );
$request->set_header( 'Content-Type', 'application/json' );
$response = $this->endpoint->replace_cart( $request );
$this->assertInstanceOf( WP_REST_Response::class, $response );
$this->assertSame( 200, $response->get_status() );
$data = $response->get_data();
$this->assertSame(
$ec_token,
$data['payment_method']['token'] ?? null,
'Replace cart response must include the preserved ec_token from the session'
);
$this->assertSame( $cart_id, $data['id'] ?? null, 'Response must echo the cart id' );
$this->assertSame( 'INCOMPLETE', $data['status'] ?? null );
$this->assertSame( 'VALID', $data['validation_status'] ?? null );
$this->assertIsArray( $data['validation_issues'] ?? null );
$this->assertEmpty( $data['validation_issues'] );
$this->assertArrayHasKey( 'totals', $data, 'Response must include calculated totals' );
foreach ( array( 'subtotal', 'shipping', 'tax', 'total' ) as $key ) {
$this->assertArrayHasKey( $key, $data['totals'], "totals must include $key" );
$this->assertNotEmpty( $data['totals'][ $key ]['currency_code'] ?? '' );
$this->assertNotEmpty( $data['totals'][ $key ]['value'] ?? '' );
}
$shipping_options = $data['available_shipping_options'] ?? array();
$this->assertIsArray( $shipping_options );
$this->assertGreaterThanOrEqual( 1, count( $shipping_options ), 'At least one shipping option must be present' );
$selected = array_filter( $shipping_options, fn( $opt ) => ( $opt['is_selected'] ?? false ) === true );
$this->assertCount( 1, $selected, 'Exactly one shipping option must be selected' );
$this->assertArrayHasKey( 'customer', $data, 'Response must include customer' );
$this->assertArrayHasKey( 'shipping_address', $data, 'Response must include shipping_address' );
$items_subtotal_cents = 0;
foreach ( $data['items'] as $item ) {
$price = (float) ( $item['price']['value'] ?? 0 );
$quantity = (int) ( $item['quantity'] ?? 0 );
$items_subtotal_cents += (int) round( $price * 100 ) * $quantity;
}
$subtotal_cents = (int) round( (float) ( $data['totals']['subtotal']['value'] ?? 0 ) * 100 );
$this->assertSame( $subtotal_cents, $items_subtotal_cents, 'Sum of item price × quantity must equal totals.subtotal' );
}
}