Skip to content

Commit 6c5d6f4

Browse files
committed
test(blocks): verify ownership check in UpdateShippingEndpoint
1 parent 0caba9f commit 6c5d6f4

1 file changed

Lines changed: 127 additions & 0 deletions

File tree

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace WooCommerce\PayPalCommerce\Blocks\Endpoint;
6+
7+
use Mockery;
8+
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
9+
use Psr\Log\LoggerInterface;
10+
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
11+
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
12+
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
13+
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
14+
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
15+
use WooCommerce\PayPalCommerce\Session\SessionHandler;
16+
use WooCommerce\PayPalCommerce\TestCase;
17+
use function Brain\Monkey\Functions\expect;
18+
19+
/**
20+
* @covers \WooCommerce\PayPalCommerce\Blocks\Endpoint\UpdateShippingEndpoint
21+
*/
22+
class UpdateShippingEndpointTest extends TestCase
23+
{
24+
use MockeryPHPUnitIntegration;
25+
26+
private RequestData $request_data;
27+
private OrderEndpoint $order_endpoint;
28+
private PurchaseUnitFactory $purchase_unit_factory;
29+
private SessionHandler $session_handler;
30+
private LoggerInterface $logger;
31+
private UpdateShippingEndpoint $sut;
32+
33+
public function setUp(): void
34+
{
35+
parent::setUp();
36+
$this->request_data = Mockery::mock( RequestData::class );
37+
$this->order_endpoint = Mockery::mock( OrderEndpoint::class );
38+
$this->purchase_unit_factory = Mockery::mock( PurchaseUnitFactory::class );
39+
$this->session_handler = Mockery::mock( SessionHandler::class );
40+
$this->logger = Mockery::mock( LoggerInterface::class );
41+
42+
$this->sut = new UpdateShippingEndpoint(
43+
$this->request_data,
44+
$this->order_endpoint,
45+
$this->purchase_unit_factory,
46+
$this->session_handler,
47+
$this->logger
48+
);
49+
}
50+
51+
/**
52+
* @scenario When the request order_id matches the session's stored PayPal order,
53+
* the PATCH is issued and a success response is returned.
54+
*/
55+
public function test_patch_proceeds_when_order_id_matches_session(): void
56+
{
57+
$order_id = 'ORDER-MATCH-123';
58+
59+
$session_order = Mockery::mock( Order::class );
60+
$session_order->shouldReceive( 'id' )->andReturn( $order_id );
61+
62+
$pu = Mockery::mock( PurchaseUnit::class );
63+
$pu->shouldReceive( 'to_array' )->andReturn( array() );
64+
$pu->shouldReceive( 'reference_id' )->andReturn( 'default' );
65+
66+
$this->request_data->shouldReceive( 'read_request' )
67+
->with( UpdateShippingEndpoint::nonce() )
68+
->andReturn( array( 'order_id' => $order_id ) );
69+
70+
$this->session_handler->shouldReceive( 'order' )->andReturn( $session_order );
71+
72+
$this->purchase_unit_factory->shouldReceive( 'from_wc_cart' )
73+
->with( null, true )
74+
->andReturn( $pu );
75+
76+
$this->order_endpoint->shouldReceive( 'patch' )
77+
->with( $order_id, Mockery::any() )
78+
->once();
79+
80+
expect( 'wp_send_json_success' )->once();
81+
82+
$this->sut->handle_request();
83+
}
84+
85+
/**
86+
* @scenario When the request order_id belongs to a different session (IDOR attack),
87+
* no PATCH is issued and an error response is returned.
88+
*/
89+
public function test_patch_is_rejected_when_order_id_does_not_match_session(): void
90+
{
91+
$session_order = Mockery::mock( Order::class );
92+
$session_order->shouldReceive( 'id' )->andReturn( 'ORDER-VICTIM' );
93+
94+
$this->request_data->shouldReceive( 'read_request' )
95+
->with( UpdateShippingEndpoint::nonce() )
96+
->andReturn( array( 'order_id' => 'ORDER-ATTACKER' ) );
97+
98+
$this->session_handler->shouldReceive( 'order' )->andReturn( $session_order );
99+
100+
$this->purchase_unit_factory->shouldReceive( 'from_wc_cart' )->never();
101+
$this->order_endpoint->shouldReceive( 'patch' )->never();
102+
103+
expect( 'wp_send_json_error' )->once();
104+
105+
$this->sut->handle_request();
106+
}
107+
108+
/**
109+
* @scenario When there is no PayPal order stored in the session,
110+
* no PATCH is issued and an error response is returned.
111+
*/
112+
public function test_patch_is_rejected_when_no_session_order_exists(): void
113+
{
114+
$this->request_data->shouldReceive( 'read_request' )
115+
->with( UpdateShippingEndpoint::nonce() )
116+
->andReturn( array( 'order_id' => 'ORDER-123' ) );
117+
118+
$this->session_handler->shouldReceive( 'order' )->andReturn( null );
119+
120+
$this->purchase_unit_factory->shouldReceive( 'from_wc_cart' )->never();
121+
$this->order_endpoint->shouldReceive( 'patch' )->never();
122+
123+
expect( 'wp_send_json_error' )->once();
124+
125+
$this->sut->handle_request();
126+
}
127+
}

0 commit comments

Comments
 (0)