CartIntegrationTest.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. <?php
  2. namespace Webkul\BagistoApi\Tests\Feature;
  3. use Illuminate\Foundation\Testing\RefreshDatabase;
  4. use Tests\TestCase;
  5. use Webkul\Checkout\Models\Cart;
  6. use Webkul\Customer\Models\Customer;
  7. use Webkul\Product\Models\Product;
  8. class CartIntegrationTest extends TestCase
  9. {
  10. use RefreshDatabase;
  11. private string $graphqlUrl = '/graphql';
  12. /**
  13. * Helper to create customer with Sanctum token
  14. */
  15. private function createCustomerWithToken(): array
  16. {
  17. $customer = Customer::factory()->create([
  18. 'email' => 'test@example.com',
  19. 'password' => bcrypt('password123'),
  20. ]);
  21. $token = $customer->createToken('api-token')->plainTextToken;
  22. return compact('customer', 'token');
  23. }
  24. /**
  25. * Add product to cart
  26. */
  27. private function addProductToCart(string $token, int $productId, int $quantity, ?array $options = null): array
  28. {
  29. $mutation = <<<'GQL'
  30. mutation addProductToCart($token: String!, $productId: Int!, $quantity: Int!, $options: Object) {
  31. addProductToCartCartToken(input: {
  32. token: $token
  33. productId: $productId
  34. quantity: $quantity
  35. options: $options
  36. }) {
  37. success
  38. cart {
  39. id
  40. itemsCount
  41. formattedGrandTotal
  42. items {
  43. id
  44. productId
  45. quantity
  46. }
  47. }
  48. }
  49. }
  50. GQL;
  51. $variables = compact('token', 'productId', 'quantity');
  52. if ($options !== null) {
  53. $variables['options'] = $options;
  54. }
  55. return $this->postJson($this->graphqlUrl, [
  56. 'query' => $mutation,
  57. 'variables' => $variables,
  58. ])->json();
  59. }
  60. /**
  61. * Update cart item
  62. */
  63. private function updateCartItem(string $token, int $cartItemId, int $quantity): array
  64. {
  65. $mutation = <<<'GQL'
  66. mutation updateCartItem($token: String!, $cartItemId: Int!, $quantity: Int!) {
  67. updateItemCartToken(input: {
  68. token: $token
  69. cartItemId: $cartItemId
  70. quantity: $quantity
  71. }) {
  72. success
  73. cart {
  74. itemsCount
  75. items {
  76. id
  77. quantity
  78. }
  79. }
  80. }
  81. }
  82. GQL;
  83. return $this->postJson($this->graphqlUrl, [
  84. 'query' => $mutation,
  85. 'variables' => compact('token', 'cartItemId', 'quantity'),
  86. ])->json();
  87. }
  88. /**
  89. * Remove cart item
  90. */
  91. private function removeCartItem(string $token, int $cartItemId): array
  92. {
  93. $mutation = <<<'GQL'
  94. mutation removeCartItem($token: String!, $cartItemId: Int!) {
  95. removeItemCartToken(input: {
  96. token: $token
  97. cartItemId: $cartItemId
  98. }) {
  99. success
  100. cart {
  101. itemsCount
  102. items {
  103. id
  104. }
  105. }
  106. }
  107. }
  108. GQL;
  109. return $this->postJson($this->graphqlUrl, [
  110. 'query' => $mutation,
  111. 'variables' => compact('token', 'cartItemId'),
  112. ])->json();
  113. }
  114. /**
  115. * Get cart details
  116. */
  117. private function getCart(string $token, int $cartId): array
  118. {
  119. $mutation = <<<'GQL'
  120. query getCart($token: String!, $cartId: Int!) {
  121. readCartToken(input: {
  122. token: $token
  123. cartId: $cartId
  124. }) {
  125. cart {
  126. id
  127. itemsCount
  128. items {
  129. id
  130. productId
  131. quantity
  132. }
  133. formattedGrandTotal
  134. }
  135. }
  136. }
  137. GQL;
  138. return $this->postJson($this->graphqlUrl, [
  139. 'query' => $mutation,
  140. 'variables' => compact('token', 'cartId'),
  141. ])->json();
  142. }
  143. /**
  144. * Test: Complete shopping flow - add, update, remove
  145. */
  146. public function test_complete_shopping_flow_add_update_remove(): void
  147. {
  148. ['customer' => $customer, 'token' => $token] = $this->createCustomerWithToken();
  149. $product = Product::factory()->create();
  150. // Step 1: Add product to cart
  151. $addResponse = $this->addProductToCart($token, $product->id, 2);
  152. $this->assertTrue($addResponse['data']['addProductToCartCartToken']['success']);
  153. $cartId = $addResponse['data']['addProductToCartCartToken']['cart']['id'];
  154. $cartItemId = $addResponse['data']['addProductToCartCartToken']['cart']['items'][0]['id'];
  155. $this->assertEquals(2, $addResponse['data']['addProductToCartCartToken']['cart']['itemsCount']);
  156. // Step 2: Update quantity
  157. $updateResponse = $this->updateCartItem($token, $cartItemId, 5);
  158. $this->assertTrue($updateResponse['data']['updateItemCartToken']['success']);
  159. $this->assertEquals(5, $updateResponse['data']['updateItemCartToken']['cart']['items'][0]['quantity']);
  160. // Step 3: Remove item
  161. $removeResponse = $this->removeCartItem($token, $cartItemId);
  162. $this->assertTrue($removeResponse['data']['removeItemCartToken']['success']);
  163. $this->assertEquals(0, $removeResponse['data']['removeItemCartToken']['cart']['itemsCount']);
  164. }
  165. /**
  166. * Test: Multiple products in cart
  167. */
  168. public function test_multiple_products_in_cart(): void
  169. {
  170. ['customer' => $customer, 'token' => $token] = $this->createCustomerWithToken();
  171. $product1 = Product::factory()->create();
  172. $product2 = Product::factory()->create();
  173. $product3 = Product::factory()->create();
  174. // Add first product
  175. $response1 = $this->addProductToCart($token, $product1->id, 1);
  176. $cartId = $response1['data']['addProductToCartCartToken']['cart']['id'];
  177. $this->assertEquals(1, $response1['data']['addProductToCartCartToken']['cart']['itemsCount']);
  178. // Add second product
  179. $response2 = $this->addProductToCart($token, $product2->id, 2);
  180. $this->assertEquals(3, $response2['data']['addProductToCartCartToken']['cart']['itemsCount']);
  181. // Add third product
  182. $response3 = $this->addProductToCart($token, $product3->id, 3);
  183. $this->assertEquals(6, $response3['data']['addProductToCartCartToken']['cart']['itemsCount']);
  184. // Verify cart has 3 items
  185. $getResponse = $this->getCart($token, $cartId);
  186. $this->assertCount(3, $getResponse['data']['readCartToken']['cart']['items']);
  187. }
  188. /**
  189. * Test: Guest to authenticated checkout flow
  190. */
  191. public function test_guest_to_authenticated_checkout_flow(): void
  192. {
  193. // Step 1: Guest adds product
  194. $guestCart = Cart::factory()->create(['customer_id' => null]);
  195. $product = Product::factory()->create();
  196. $guestResponse = $this->addProductToCart((string) $guestCart->id, $product->id, 2);
  197. $this->assertTrue($guestResponse['data']['addProductToCartCartToken']['success']);
  198. $this->assertEquals(2, $guestResponse['data']['addProductToCartCartToken']['cart']['itemsCount']);
  199. // Step 2: Guest registers/logs in
  200. ['customer' => $customer, 'token' => $token] = $this->createCustomerWithToken();
  201. // Step 3: Merge guest cart
  202. $mergeResponse = $this->addProductToCart($token, $product->id, 1);
  203. $this->assertTrue($mergeResponse['data']['addProductToCartCartToken']['success']);
  204. // After merge, customer should have product
  205. $this->assertGreaterThan(0, $mergeResponse['data']['addProductToCartCartToken']['cart']['itemsCount']);
  206. }
  207. /**
  208. * Test: Update same product multiple times
  209. */
  210. public function test_update_same_product_multiple_times(): void
  211. {
  212. ['customer' => $customer, 'token' => $token] = $this->createCustomerWithToken();
  213. $product = Product::factory()->create();
  214. // Add product
  215. $response = $this->addProductToCart($token, $product->id, 1);
  216. $cartItemId = $response['data']['addProductToCartCartToken']['cart']['items'][0]['id'];
  217. // Update to 5
  218. $this->updateCartItem($token, $cartItemId, 5);
  219. // Update to 10
  220. $this->updateCartItem($token, $cartItemId, 10);
  221. // Update to 3
  222. $response = $this->updateCartItem($token, $cartItemId, 3);
  223. $this->assertEquals(3, $response['data']['updateItemCartToken']['cart']['items'][0]['quantity']);
  224. }
  225. /**
  226. * Test: Add product with options
  227. */
  228. public function test_add_product_with_options_and_update(): void
  229. {
  230. ['customer' => $customer, 'token' => $token] = $this->createCustomerWithToken();
  231. $product = Product::factory()->create();
  232. $options = ['size' => 'M', 'color' => 'red'];
  233. $response = $this->addProductToCart($token, $product->id, 1, $options);
  234. $this->assertTrue($response['data']['addProductToCartCartToken']['success']);
  235. $this->assertEquals($options, $response['data']['addProductToCartCartToken']['cart']['items'][0]);
  236. }
  237. /**
  238. * Test: Empty cart after removing all items
  239. */
  240. public function test_empty_cart_after_removing_all_items(): void
  241. {
  242. ['customer' => $customer, 'token' => $token] = $this->createCustomerWithToken();
  243. $products = Product::factory()->count(3)->create();
  244. // Add 3 products
  245. foreach ($products as $index => $product) {
  246. $this->addProductToCart($token, $product->id, $index + 1);
  247. }
  248. // Get cart to find items
  249. $getResponse = $this->addProductToCart($token, $products[0]->id, 0); // Dummy to get cart ID
  250. $cartId = $getResponse['data']['addProductToCartCartToken']['cart']['id'];
  251. $cartDetails = $this->getCart($token, $cartId);
  252. $items = $cartDetails['data']['readCartToken']['cart']['items'];
  253. // Remove all items
  254. foreach ($items as $item) {
  255. $this->removeCartItem($token, $item['id']);
  256. }
  257. // Verify cart is empty
  258. $finalCart = $this->getCart($token, $cartId);
  259. $this->assertCount(0, $finalCart['data']['readCartToken']['cart']['items']);
  260. }
  261. /**
  262. * Test: Cart persistence across operations
  263. */
  264. public function test_cart_persistence_across_operations(): void
  265. {
  266. ['customer' => $customer, 'token' => $token] = $this->createCustomerWithToken();
  267. $product = Product::factory()->create();
  268. // Add product
  269. $addResponse = $this->addProductToCart($token, $product->id, 1);
  270. $cartId = $addResponse['data']['addProductToCartCartToken']['cart']['id'];
  271. // Fetch cart
  272. $getResponse1 = $this->getCart($token, $cartId);
  273. $this->assertEquals($cartId, $getResponse1['data']['readCartToken']['cart']['id']);
  274. // Update item
  275. $cartItemId = $getResponse1['data']['readCartToken']['cart']['items'][0]['id'];
  276. $this->updateCartItem($token, $cartItemId, 5);
  277. // Fetch again
  278. $getResponse2 = $this->getCart($token, $cartId);
  279. $this->assertEquals($cartId, $getResponse2['data']['readCartToken']['cart']['id']);
  280. $this->assertEquals(5, $getResponse2['data']['readCartToken']['cart']['items'][0]['quantity']);
  281. }
  282. }