CustomerOrderTest.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. <?php
  2. namespace Webkul\BagistoApi\Tests\Feature\GraphQL;
  3. use Webkul\BagistoApi\Tests\GraphQLTestCase;
  4. use Webkul\Core\Models\Channel;
  5. use Webkul\Customer\Models\Customer;
  6. use Webkul\Product\Models\Product;
  7. use Webkul\Sales\Models\Order;
  8. use Webkul\Sales\Models\OrderItem;
  9. use Webkul\Sales\Models\OrderPayment;
  10. class CustomerOrderTest extends GraphQLTestCase
  11. {
  12. /**
  13. * Create test data — customer with orders
  14. */
  15. private function createTestData(): array
  16. {
  17. $this->seedRequiredData();
  18. $customer = $this->createCustomer();
  19. $channel = Channel::first();
  20. $product = Product::factory()->create();
  21. $order1 = Order::factory()->create([
  22. 'customer_id' => $customer->id,
  23. 'customer_email' => $customer->email,
  24. 'customer_first_name' => $customer->first_name,
  25. 'customer_last_name' => $customer->last_name,
  26. 'channel_id' => $channel->id,
  27. 'status' => 'pending',
  28. ]);
  29. OrderItem::factory()->create([
  30. 'order_id' => $order1->id,
  31. 'product_id' => $product->id,
  32. 'sku' => 'TEST-SKU-001',
  33. 'type' => 'simple',
  34. 'name' => 'Test Product One',
  35. ]);
  36. OrderPayment::factory()->create([
  37. 'order_id' => $order1->id,
  38. ]);
  39. $order2 = Order::factory()->create([
  40. 'customer_id' => $customer->id,
  41. 'customer_email' => $customer->email,
  42. 'customer_first_name' => $customer->first_name,
  43. 'customer_last_name' => $customer->last_name,
  44. 'channel_id' => $channel->id,
  45. 'status' => 'completed',
  46. ]);
  47. OrderItem::factory()->create([
  48. 'order_id' => $order2->id,
  49. 'product_id' => $product->id,
  50. 'sku' => 'TEST-SKU-002',
  51. 'type' => 'simple',
  52. 'name' => 'Test Product Two',
  53. ]);
  54. OrderPayment::factory()->create([
  55. 'order_id' => $order2->id,
  56. ]);
  57. return compact('customer', 'channel', 'product', 'order1', 'order2');
  58. }
  59. // ── Collection Queries ────────────────────────────────────
  60. /**
  61. * Test: Query all customer orders collection
  62. */
  63. public function test_get_customer_orders_collection(): void
  64. {
  65. $testData = $this->createTestData();
  66. $query = <<<'GQL'
  67. query getCustomerOrders {
  68. customerOrders(first: 10) {
  69. edges {
  70. cursor
  71. node {
  72. _id
  73. incrementId
  74. status
  75. customerEmail
  76. customerFirstName
  77. customerLastName
  78. grandTotal
  79. subTotal
  80. shippingMethod
  81. shippingTitle
  82. totalItemCount
  83. totalQtyOrdered
  84. baseCurrencyCode
  85. orderCurrencyCode
  86. createdAt
  87. }
  88. }
  89. pageInfo {
  90. endCursor
  91. startCursor
  92. hasNextPage
  93. hasPreviousPage
  94. }
  95. totalCount
  96. }
  97. }
  98. GQL;
  99. $response = $this->authenticatedGraphQL($testData['customer'], $query);
  100. $response->assertOk();
  101. $data = $response->json('data.customerOrders');
  102. expect($data['totalCount'])->toBeGreaterThanOrEqual(2);
  103. expect($data['edges'])->not()->toBeEmpty();
  104. }
  105. /**
  106. * Test: Unauthenticated request returns error
  107. */
  108. public function test_get_customer_orders_requires_authentication(): void
  109. {
  110. $query = <<<'GQL'
  111. query getCustomerOrders {
  112. customerOrders(first: 5) {
  113. edges {
  114. node {
  115. _id
  116. }
  117. }
  118. }
  119. }
  120. GQL;
  121. $response = $this->graphQL($query);
  122. $response->assertOk();
  123. $errors = $response->json('errors');
  124. expect($errors)->not()->toBeEmpty();
  125. }
  126. /**
  127. * Test: Customer only sees their own orders
  128. */
  129. public function test_customer_only_sees_own_orders(): void
  130. {
  131. $testData = $this->createTestData();
  132. /** Create another customer with their own order */
  133. $otherCustomer = $this->createCustomer();
  134. $channel = Channel::first();
  135. Order::factory()->create([
  136. 'customer_id' => $otherCustomer->id,
  137. 'customer_email' => $otherCustomer->email,
  138. 'customer_first_name' => $otherCustomer->first_name,
  139. 'customer_last_name' => $otherCustomer->last_name,
  140. 'channel_id' => $channel->id,
  141. 'status' => 'pending',
  142. ]);
  143. $query = <<<'GQL'
  144. query getCustomerOrders {
  145. customerOrders(first: 50) {
  146. edges {
  147. node {
  148. _id
  149. }
  150. }
  151. totalCount
  152. }
  153. }
  154. GQL;
  155. $response = $this->authenticatedGraphQL($testData['customer'], $query);
  156. $response->assertOk();
  157. $data = $response->json('data.customerOrders');
  158. /** Should only see the 2 orders belonging to testData customer */
  159. expect($data['totalCount'])->toBe(2);
  160. }
  161. /**
  162. * Test: Filter orders by status
  163. */
  164. public function test_filter_orders_by_status(): void
  165. {
  166. $testData = $this->createTestData();
  167. $query = <<<'GQL'
  168. query getCustomerOrders($status: String) {
  169. customerOrders(first: 10, status: $status) {
  170. edges {
  171. node {
  172. _id
  173. status
  174. }
  175. }
  176. totalCount
  177. }
  178. }
  179. GQL;
  180. $response = $this->authenticatedGraphQL($testData['customer'], $query, [
  181. 'status' => 'pending',
  182. ]);
  183. $response->assertOk();
  184. $data = $response->json('data.customerOrders');
  185. expect($data['totalCount'])->toBe(1);
  186. $node = $data['edges'][0]['node'];
  187. expect($node['status'])->toBe('pending');
  188. }
  189. /**
  190. * Test: Filter by completed status
  191. */
  192. public function test_filter_orders_by_completed_status(): void
  193. {
  194. $testData = $this->createTestData();
  195. $query = <<<'GQL'
  196. query getCustomerOrders($status: String) {
  197. customerOrders(first: 10, status: $status) {
  198. edges {
  199. node {
  200. _id
  201. status
  202. }
  203. }
  204. totalCount
  205. }
  206. }
  207. GQL;
  208. $response = $this->authenticatedGraphQL($testData['customer'], $query, [
  209. 'status' => 'completed',
  210. ]);
  211. $response->assertOk();
  212. $data = $response->json('data.customerOrders');
  213. expect($data['totalCount'])->toBe(1);
  214. $node = $data['edges'][0]['node'];
  215. expect($node['status'])->toBe('completed');
  216. }
  217. // ── Single Item Query ─────────────────────────────────────
  218. /**
  219. * Test: Query single customer order by ID
  220. */
  221. public function test_get_customer_order_by_id(): void
  222. {
  223. $testData = $this->createTestData();
  224. $orderId = "/api/shop/customer-orders/{$testData['order1']->id}";
  225. $query = <<<GQL
  226. query getCustomerOrder {
  227. customerOrder(id: "{$orderId}") {
  228. _id
  229. incrementId
  230. status
  231. customerEmail
  232. customerFirstName
  233. customerLastName
  234. grandTotal
  235. subTotal
  236. shippingMethod
  237. shippingTitle
  238. baseCurrencyCode
  239. orderCurrencyCode
  240. totalItemCount
  241. totalQtyOrdered
  242. createdAt
  243. }
  244. }
  245. GQL;
  246. $response = $this->authenticatedGraphQL($testData['customer'], $query);
  247. $response->assertOk();
  248. $data = $response->json('data.customerOrder');
  249. expect($data['_id'])->toBe($testData['order1']->id);
  250. expect($data['status'])->toBe('pending');
  251. expect($data['customerEmail'])->toBe($testData['customer']->email);
  252. expect($data['customerFirstName'])->toBe($testData['customer']->first_name);
  253. expect($data['customerLastName'])->toBe($testData['customer']->last_name);
  254. }
  255. /**
  256. * Test: Query single customer order by numeric ID
  257. */
  258. public function test_get_customer_order_by_numeric_id(): void
  259. {
  260. $testData = $this->createTestData();
  261. $orderId = (string) $testData['order1']->id;
  262. $query = <<<GQL
  263. query getCustomerOrder {
  264. customerOrder(id: "{$orderId}") {
  265. _id
  266. status
  267. customerEmail
  268. }
  269. }
  270. GQL;
  271. $response = $this->authenticatedGraphQL($testData['customer'], $query);
  272. $response->assertOk();
  273. $data = $response->json('data.customerOrder');
  274. expect($data['_id'])->toBe($testData['order1']->id);
  275. expect($data['status'])->toBe('pending');
  276. expect($data['customerEmail'])->toBe($testData['customer']->email);
  277. }
  278. /**
  279. * Test: Invalid customer order ID format returns validation error
  280. */
  281. public function test_get_customer_order_with_invalid_id_format_returns_error(): void
  282. {
  283. $testData = $this->createTestData();
  284. $query = <<<'GQL'
  285. query getCustomerOrder {
  286. customerOrder(id: "invalid-id") {
  287. _id
  288. }
  289. }
  290. GQL;
  291. $response = $this->authenticatedGraphQL($testData['customer'], $query);
  292. $response->assertOk();
  293. $errors = $response->json('errors');
  294. $message = $errors[0]['message'] ?? '';
  295. expect($errors)->not()->toBeEmpty();
  296. expect($message)->toContain('Invalid ID format');
  297. }
  298. /**
  299. * Test: Query order returns correct financial data
  300. */
  301. public function test_order_returns_financial_data(): void
  302. {
  303. $testData = $this->createTestData();
  304. $orderId = "/api/shop/customer-orders/{$testData['order1']->id}";
  305. $query = <<<GQL
  306. query getCustomerOrder {
  307. customerOrder(id: "{$orderId}") {
  308. _id
  309. grandTotal
  310. baseGrandTotal
  311. subTotal
  312. baseSubTotal
  313. taxAmount
  314. shippingAmount
  315. discountAmount
  316. }
  317. }
  318. GQL;
  319. $response = $this->authenticatedGraphQL($testData['customer'], $query);
  320. $response->assertOk();
  321. $data = $response->json('data.customerOrder');
  322. expect($data)->toHaveKeys([
  323. '_id',
  324. 'grandTotal',
  325. 'baseGrandTotal',
  326. 'subTotal',
  327. 'baseSubTotal',
  328. 'taxAmount',
  329. 'shippingAmount',
  330. 'discountAmount',
  331. ]);
  332. }
  333. /**
  334. * Test: Query non-existent order returns error
  335. */
  336. public function test_get_nonexistent_order_returns_error(): void
  337. {
  338. $this->seedRequiredData();
  339. $query = <<<'GQL'
  340. query getCustomerOrder {
  341. customerOrder(id: "/api/shop/customer-orders/99999") {
  342. _id
  343. status
  344. }
  345. }
  346. GQL;
  347. $response = $this->graphQL($query);
  348. $response->assertOk();
  349. $errors = $response->json('errors');
  350. expect($errors)->not()->toBeEmpty();
  351. }
  352. // ── Pagination ────────────────────────────────────────────
  353. /**
  354. * Test: Pagination with first parameter
  355. */
  356. public function test_pagination_with_first_parameter(): void
  357. {
  358. $testData = $this->createTestData();
  359. $query = <<<'GQL'
  360. query getCustomerOrders {
  361. customerOrders(first: 1) {
  362. edges {
  363. cursor
  364. node {
  365. _id
  366. }
  367. }
  368. pageInfo {
  369. endCursor
  370. hasNextPage
  371. }
  372. totalCount
  373. }
  374. }
  375. GQL;
  376. $response = $this->authenticatedGraphQL($testData['customer'], $query);
  377. $response->assertOk();
  378. $data = $response->json('data.customerOrders');
  379. expect(count($data['edges']))->toBe(1);
  380. expect($data['totalCount'])->toBeGreaterThanOrEqual(2);
  381. }
  382. /**
  383. * Test: Forward pagination with after cursor
  384. */
  385. public function test_pagination_with_after_cursor(): void
  386. {
  387. $testData = $this->createTestData();
  388. /** First page */
  389. $query = <<<'GQL'
  390. query getCustomerOrders {
  391. customerOrders(first: 1) {
  392. edges {
  393. cursor
  394. node {
  395. _id
  396. }
  397. }
  398. pageInfo {
  399. endCursor
  400. hasNextPage
  401. }
  402. }
  403. }
  404. GQL;
  405. $response = $this->authenticatedGraphQL($testData['customer'], $query);
  406. $response->assertOk();
  407. $firstPageData = $response->json('data.customerOrders');
  408. $endCursor = $firstPageData['pageInfo']['endCursor'];
  409. $firstOrderId = $firstPageData['edges'][0]['node']['_id'];
  410. /** Second page */
  411. $query2 = <<<GQL
  412. query getCustomerOrders {
  413. customerOrders(first: 1, after: "{$endCursor}") {
  414. edges {
  415. node {
  416. _id
  417. }
  418. }
  419. }
  420. }
  421. GQL;
  422. $response2 = $this->authenticatedGraphQL($testData['customer'], $query2);
  423. $response2->assertOk();
  424. $secondPageData = $response2->json('data.customerOrders');
  425. $secondOrderId = $secondPageData['edges'][0]['node']['_id'] ?? null;
  426. /** Second page should have a different order */
  427. expect($secondOrderId)->not()->toBe($firstOrderId);
  428. }
  429. // ── Schema Introspection ──────────────────────────────────
  430. /**
  431. * Test: CustomerOrder type has expected fields in schema
  432. */
  433. public function test_customer_order_schema_has_expected_fields(): void
  434. {
  435. $query = <<<'GQL'
  436. {
  437. __type(name: "CustomerOrder") {
  438. name
  439. fields {
  440. name
  441. }
  442. }
  443. }
  444. GQL;
  445. $response = $this->graphQL($query);
  446. $response->assertSuccessful();
  447. $type = $response->json('data.__type');
  448. expect($type)->not->toBeNull()
  449. ->and($type['name'])->toBe('CustomerOrder');
  450. $fieldNames = array_column($type['fields'], 'name');
  451. expect($fieldNames)
  452. ->toContain('_id')
  453. ->toContain('incrementId')
  454. ->toContain('status')
  455. ->toContain('customerEmail')
  456. ->toContain('customerFirstName')
  457. ->toContain('customerLastName')
  458. ->toContain('grandTotal')
  459. ->toContain('subTotal')
  460. ->toContain('shippingMethod')
  461. ->toContain('shippingTitle')
  462. ->toContain('totalItemCount')
  463. ->toContain('totalQtyOrdered')
  464. ->toContain('baseCurrencyCode')
  465. ->toContain('orderCurrencyCode')
  466. ->toContain('createdAt');
  467. }
  468. }