CompareItemTest.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. <?php
  2. namespace Webkul\BagistoApi\Tests\Feature\GraphQL;
  3. use Webkul\BagistoApi\Tests\GraphQLTestCase;
  4. use Webkul\Customer\Models\CompareItem;
  5. use Webkul\Product\Models\Product;
  6. class CompareItemTest extends GraphQLTestCase
  7. {
  8. /**
  9. * Create test data - customer, products and compare items
  10. */
  11. private function createTestData(): array
  12. {
  13. $this->seedRequiredData();
  14. $customer = $this->createCustomer();
  15. $product1 = Product::factory()->create();
  16. $product2 = Product::factory()->create();
  17. $compareItem1 = CompareItem::factory()->create([
  18. 'customer_id' => $customer->id,
  19. 'product_id' => $product1->id,
  20. ]);
  21. $compareItem2 = CompareItem::factory()->create([
  22. 'customer_id' => $customer->id,
  23. 'product_id' => $product2->id,
  24. ]);
  25. return compact('customer', 'product1', 'product2', 'compareItem1', 'compareItem2');
  26. }
  27. /**
  28. * Test: Query all compare items collection
  29. */
  30. public function test_get_compare_items_collection(): void
  31. {
  32. $testData = $this->createTestData();
  33. $query = <<<'GQL'
  34. query getCompareItems {
  35. compareItems {
  36. edges {
  37. cursor
  38. node {
  39. id
  40. _id
  41. product {
  42. id
  43. }
  44. customer {
  45. id
  46. }
  47. createdAt
  48. updatedAt
  49. }
  50. }
  51. pageInfo {
  52. endCursor
  53. startCursor
  54. hasNextPage
  55. hasPreviousPage
  56. }
  57. totalCount
  58. }
  59. }
  60. GQL;
  61. $response = $this->authenticatedGraphQL($testData['customer'], $query);
  62. $response->assertOk();
  63. $data = $response->json('data.compareItems');
  64. expect($data['totalCount'])->toBeGreaterThanOrEqual(2);
  65. expect($data['edges'])->not()->toBeEmpty();
  66. }
  67. /**
  68. * Test: Query single compare item by ID
  69. */
  70. public function test_get_compare_item_by_id(): void
  71. {
  72. $testData = $this->createTestData();
  73. $compareItemId = "/api/shop/compare-items/{$testData['compareItem1']->id}";
  74. $query = <<<GQL
  75. query getCompareItem {
  76. compareItem(id: "{$compareItemId}") {
  77. id
  78. _id
  79. product {
  80. id
  81. }
  82. customer {
  83. id
  84. }
  85. createdAt
  86. updatedAt
  87. }
  88. }
  89. GQL;
  90. $response = $this->graphQL($query);
  91. $response->assertOk();
  92. $data = $response->json('data.compareItem');
  93. expect($data['_id'])->toBe($testData['compareItem1']->id);
  94. expect($data['product'])->toHaveKey('id');
  95. expect($data['customer'])->toHaveKey('id');
  96. }
  97. /**
  98. * Test: Timestamps are returned in ISO8601 format
  99. */
  100. public function test_compare_item_timestamps_are_iso8601_format(): void
  101. {
  102. $testData = $this->createTestData();
  103. $query = <<<'GQL'
  104. query getCompareItems {
  105. compareItems(first: 1) {
  106. edges {
  107. node {
  108. _id
  109. createdAt
  110. updatedAt
  111. }
  112. }
  113. }
  114. }
  115. GQL;
  116. $response = $this->authenticatedGraphQL($testData['customer'], $query);
  117. $response->assertOk();
  118. $compareItem = $response->json('data.compareItems.edges.0.node');
  119. expect($compareItem['createdAt'])->toMatch('/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/');
  120. expect($compareItem['updatedAt'])->toMatch('/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/');
  121. }
  122. /**
  123. * Test: Query compare items with pagination (first)
  124. */
  125. public function test_compare_items_pagination_first(): void
  126. {
  127. $testData = $this->createTestData();
  128. $query = <<<'GQL'
  129. query getCompareItems {
  130. compareItems(first: 1) {
  131. edges {
  132. node {
  133. _id
  134. }
  135. }
  136. pageInfo {
  137. hasNextPage
  138. }
  139. }
  140. }
  141. GQL;
  142. $response = $this->authenticatedGraphQL($testData['customer'], $query);
  143. $response->assertOk();
  144. $data = $response->json('data.compareItems');
  145. expect($data['edges'])->toHaveCount(1);
  146. }
  147. /**
  148. * Test: Query compare items with product relationship
  149. */
  150. public function test_query_compare_items_with_product(): void
  151. {
  152. $testData = $this->createTestData();
  153. $query = <<<'GQL'
  154. query getCompareItems {
  155. compareItems(first: 1) {
  156. edges {
  157. node {
  158. id
  159. product {
  160. id
  161. }
  162. }
  163. }
  164. }
  165. }
  166. GQL;
  167. $response = $this->authenticatedGraphQL($testData['customer'], $query);
  168. $response->assertOk();
  169. $compareItem = $response->json('data.compareItems.edges.0.node');
  170. expect($compareItem)->toHaveKey('product');
  171. }
  172. /**
  173. * Test: Query all fields of compare item
  174. */
  175. public function test_query_all_compare_item_fields(): void
  176. {
  177. $testData = $this->createTestData();
  178. $query = <<<'GQL'
  179. query getCompareItems {
  180. compareItems(first: 1) {
  181. edges {
  182. node {
  183. id
  184. _id
  185. product {
  186. id
  187. }
  188. customer {
  189. id
  190. }
  191. createdAt
  192. updatedAt
  193. }
  194. }
  195. }
  196. }
  197. GQL;
  198. $response = $this->authenticatedGraphQL($testData['customer'], $query);
  199. $response->assertOk();
  200. $node = $response->json('data.compareItems.edges.0.node');
  201. expect($node)->toHaveKeys(['id', '_id', 'product', 'customer', 'createdAt', 'updatedAt']);
  202. }
  203. /**
  204. * Test: Query returns appropriate error for invalid ID
  205. */
  206. public function test_invalid_compare_item_id_returns_error(): void
  207. {
  208. $query = <<<'GQL'
  209. query getCompareItem {
  210. compareItem(id: "/api/shop/compare-items/99999") {
  211. id
  212. }
  213. }
  214. GQL;
  215. $response = $this->graphQL($query);
  216. $response->assertOk();
  217. expect($response->json('data.compareItem'))->toBeNull();
  218. }
  219. /**
  220. * Test: Pagination with cursor
  221. */
  222. public function test_compare_items_pagination_with_cursor(): void
  223. {
  224. $testData = $this->createTestData();
  225. $firstQuery = <<<'GQL'
  226. query getCompareItems {
  227. compareItems(first: 1) {
  228. edges {
  229. cursor
  230. }
  231. }
  232. }
  233. GQL;
  234. $firstResponse = $this->authenticatedGraphQL($testData['customer'], $firstQuery);
  235. $cursor = $firstResponse->json('data.compareItems.edges.0.cursor');
  236. $secondQuery = <<<GQL
  237. query getCompareItems {
  238. compareItems(first: 1, after: "{$cursor}") {
  239. edges {
  240. node {
  241. _id
  242. }
  243. }
  244. }
  245. }
  246. GQL;
  247. $secondResponse = $this->authenticatedGraphQL($testData['customer'], $secondQuery);
  248. $secondResponse->assertOk();
  249. }
  250. /**
  251. * Test: Numeric ID is an integer
  252. */
  253. public function test_compare_item_numeric_id_is_integer(): void
  254. {
  255. $testData = $this->createTestData();
  256. $query = <<<'GQL'
  257. query getCompareItems {
  258. compareItems(first: 1) {
  259. edges {
  260. node {
  261. _id
  262. }
  263. }
  264. }
  265. }
  266. GQL;
  267. $response = $this->authenticatedGraphQL($testData['customer'], $query);
  268. $response->assertOk();
  269. $compareItem = $response->json('data.compareItems.edges.0.node');
  270. expect($compareItem['_id'])->toBeInt();
  271. }
  272. /**
  273. * Test: Multiple compare items can be queried
  274. */
  275. public function test_query_multiple_compare_items(): void
  276. {
  277. $testData = $this->createTestData();
  278. $query = <<<'GQL'
  279. query getCompareItems {
  280. compareItems(first: 5) {
  281. edges {
  282. node {
  283. id
  284. _id
  285. }
  286. }
  287. totalCount
  288. }
  289. }
  290. GQL;
  291. $response = $this->authenticatedGraphQL($testData['customer'], $query);
  292. $response->assertOk();
  293. $data = $response->json('data.compareItems');
  294. expect($data['totalCount'])->toBeGreaterThanOrEqual(2);
  295. expect($data['edges'])->not()->toBeEmpty();
  296. }
  297. /**
  298. * Test: Schema introspection for CompareItem
  299. */
  300. public function test_compare_item_introspection_query(): void
  301. {
  302. $query = <<<'GQL'
  303. {
  304. __type(name: "CompareItem") {
  305. name
  306. kind
  307. fields {
  308. name
  309. }
  310. }
  311. }
  312. GQL;
  313. $response = $this->graphQL($query);
  314. $response->assertOk();
  315. $type = $response->json('data.__type');
  316. expect($type['name'])->toBe('CompareItem');
  317. expect($type['kind'])->toBe('OBJECT');
  318. $fieldNames = collect($type['fields'])->pluck('name')->toArray();
  319. expect($fieldNames)->toContain('id', '_id', 'product', 'customer', 'createdAt', 'updatedAt');
  320. }
  321. /**
  322. * Test: Compare items are properly sorted by creation date
  323. */
  324. public function test_compare_items_sorted_by_created_at(): void
  325. {
  326. $testData = $this->createTestData();
  327. $query = <<<'GQL'
  328. query getCompareItems {
  329. compareItems(first: 10) {
  330. edges {
  331. node {
  332. _id
  333. createdAt
  334. }
  335. }
  336. }
  337. }
  338. GQL;
  339. $response = $this->authenticatedGraphQL($testData['customer'], $query);
  340. $response->assertOk();
  341. $edges = $response->json('data.compareItems.edges');
  342. // Verify we have items
  343. expect($edges)->not()->toBeEmpty();
  344. }
  345. /**
  346. * Test: Create compare item via mutation
  347. */
  348. public function test_create_compare_item_mutation(): void
  349. {
  350. $customer = $this->createCustomer();
  351. $product = Product::factory()->create();
  352. $mutation = <<<'GQL'
  353. mutation CreateCompareItem($productId: Int!) {
  354. createCompareItem(input: {productId: $productId}) {
  355. compareItem {
  356. id
  357. _id
  358. createdAt
  359. updatedAt
  360. product {
  361. id
  362. _id
  363. sku
  364. type
  365. }
  366. customer {
  367. id
  368. }
  369. }
  370. }
  371. }
  372. GQL;
  373. $response = $this->authenticatedGraphQL($customer, $mutation, ['productId' => $product->id]);
  374. $response->assertOk();
  375. $errors = $response->json('errors');
  376. if (! empty($errors)) {
  377. $this->fail('GraphQL errors: '.json_encode($errors));
  378. }
  379. $compareItem = $response->json('data.createCompareItem.compareItem');
  380. expect($compareItem)->not()->toBeNull();
  381. expect($compareItem['_id'])->toBeInt();
  382. expect($compareItem['product']['_id'])->toBe($product->id);
  383. expect($compareItem['createdAt'])->not()->toBeNull();
  384. expect($compareItem['updatedAt'])->not()->toBeNull();
  385. }
  386. /**
  387. * Test: Delete compare item via mutation
  388. */
  389. public function test_delete_compare_item_mutation(): void
  390. {
  391. $customer = $this->createCustomer();
  392. $product = Product::factory()->create();
  393. $compareItem = CompareItem::factory()->create([
  394. 'customer_id' => $customer->id,
  395. 'product_id' => $product->id,
  396. ]);
  397. $mutation = <<<'GQL'
  398. mutation DeleteCompareItem($id: ID!) {
  399. deleteCompareItem(input: {id: $id}) {
  400. compareItem {
  401. id
  402. _id
  403. }
  404. }
  405. }
  406. GQL;
  407. $response = $this->authenticatedGraphQL($customer, $mutation, [
  408. 'id' => "/api/shop/compare-items/{$compareItem->id}",
  409. ]);
  410. $response->assertOk();
  411. $deletedItem = $response->json('data.deleteCompareItem.compareItem');
  412. expect($deletedItem)->not()->toBeNull();
  413. expect($deletedItem['_id'])->toBe($compareItem->id);
  414. expect(CompareItem::find($compareItem->id))->toBeNull();
  415. }
  416. /**
  417. * Test: Create compare item mutation with duplicate product
  418. */
  419. public function test_create_duplicate_compare_item_mutation_fails(): void
  420. {
  421. $customer = $this->createCustomer();
  422. $product1 = Product::factory()->create();
  423. CompareItem::factory()->create([
  424. 'customer_id' => $customer->id,
  425. 'product_id' => $product1->id,
  426. ]);
  427. $mutation = <<<'GQL'
  428. mutation CreateCompareItem($productId: Int!) {
  429. createCompareItem(input: {productId: $productId}) {
  430. compareItem {
  431. id
  432. }
  433. }
  434. }
  435. GQL;
  436. $response = $this->authenticatedGraphQL($customer, $mutation, ['productId' => $product1->id]);
  437. $response->assertOk();
  438. $errors = $response->json('errors');
  439. expect($errors)->not()->toBeEmpty();
  440. if (isset($errors[0]['extensions']['code'])) {
  441. expect($errors[0]['extensions']['code'])->toBe('INVALID_INPUT');
  442. } else {
  443. expect(implode(' ', array_column($errors, 'message')))->toContain('already');
  444. }
  445. }
  446. /**
  447. * Test: Delete all compare items successfully
  448. */
  449. public function test_delete_all_compare_items(): void
  450. {
  451. $testData = $this->createTestData();
  452. $mutation = <<<'GQL'
  453. mutation DeleteAllCompareItems {
  454. createDeleteAllCompareItems(input: {}) {
  455. deleteAllCompareItems {
  456. message
  457. deletedCount
  458. }
  459. }
  460. }
  461. GQL;
  462. $response = $this->authenticatedGraphQL($testData['customer'], $mutation);
  463. $response->assertOk();
  464. $response->assertJsonMissingPath('errors');
  465. $data = $response->json('data.createDeleteAllCompareItems.deleteAllCompareItems');
  466. expect($data)->not()->toBeNull();
  467. expect($data['deletedCount'])->toBe(2);
  468. expect($data['message'])->toContain('removed');
  469. expect(CompareItem::where('customer_id', $testData['customer']->id)->count())->toBe(0);
  470. }
  471. /**
  472. * Test: Delete all compare items requires authentication
  473. */
  474. public function test_delete_all_compare_items_requires_authentication(): void
  475. {
  476. $mutation = <<<'GQL'
  477. mutation DeleteAllCompareItems {
  478. createDeleteAllCompareItems(input: {}) {
  479. deleteAllCompareItems {
  480. message
  481. deletedCount
  482. }
  483. }
  484. }
  485. GQL;
  486. $response = $this->graphQL($mutation);
  487. $response->assertOk();
  488. $errors = $response->json('errors');
  489. expect($errors)->not()->toBeEmpty();
  490. }
  491. /**
  492. * Test: Delete all compare items with no items returns zero count
  493. */
  494. public function test_delete_all_compare_items_with_no_items(): void
  495. {
  496. $this->seedRequiredData();
  497. $customer = $this->createCustomer();
  498. $mutation = <<<'GQL'
  499. mutation DeleteAllCompareItems {
  500. createDeleteAllCompareItems(input: {}) {
  501. deleteAllCompareItems {
  502. message
  503. deletedCount
  504. }
  505. }
  506. }
  507. GQL;
  508. $response = $this->authenticatedGraphQL($customer, $mutation);
  509. $response->assertOk();
  510. $response->assertJsonMissingPath('errors');
  511. $data = $response->json('data.createDeleteAllCompareItems.deleteAllCompareItems');
  512. expect($data)->not()->toBeNull();
  513. expect($data['deletedCount'])->toBe(0);
  514. }
  515. }