CustomerProfileTest.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. <?php
  2. namespace Webkul\BagistoApi\Tests\Feature\GraphQL;
  3. use Illuminate\Support\Facades\DB;
  4. use Webkul\BagistoApi\Tests\GraphQLTestCase;
  5. use Webkul\Customer\Models\Customer;
  6. class CustomerProfileTest extends GraphQLTestCase
  7. {
  8. // ── Read Profile (Query) ──────────────────────────────────
  9. /**
  10. * Test: Read authenticated customer profile returns all fields
  11. */
  12. public function test_read_customer_profile_returns_all_fields(): void
  13. {
  14. $customer = $this->createCustomer([
  15. 'first_name' => 'Alice',
  16. 'last_name' => 'Wonder',
  17. 'email' => 'alice@example.com',
  18. 'phone' => '555-1234',
  19. 'gender' => 'Female',
  20. 'date_of_birth' => '1995-06-15',
  21. 'status' => 1,
  22. 'subscribed_to_news_letter' => true,
  23. 'is_verified' => 1,
  24. ]);
  25. $query = <<<'GQL'
  26. query readProfile {
  27. readCustomerProfile {
  28. id
  29. firstName
  30. lastName
  31. email
  32. phone
  33. gender
  34. dateOfBirth
  35. status
  36. subscribedToNewsLetter
  37. isVerified
  38. isSuspended
  39. image
  40. }
  41. }
  42. GQL;
  43. $response = $this->authenticatedGraphQL($customer, $query, [
  44. 'id' => '/api/shop/customer-profiles/'.$customer->id,
  45. ]);
  46. $response->assertSuccessful();
  47. $data = $response->json('data.readCustomerProfile');
  48. expect($data)->not->toBeNull()
  49. ->and($data['firstName'])->toBe('Alice')
  50. ->and($data['lastName'])->toBe('Wonder')
  51. ->and($data['email'])->toBe('alice@example.com')
  52. ->and($data['phone'])->toBe('555-1234')
  53. ->and($data['gender'])->toBe('Female')
  54. ->and($data['dateOfBirth'])->toBe('1995-06-15')
  55. ->and($data['subscribedToNewsLetter'])->toBeTrue()
  56. ->and($data['isVerified'])->toBe('1');
  57. }
  58. /**
  59. * Test: Read profile without authentication returns error
  60. */
  61. public function test_read_profile_without_auth_returns_error(): void
  62. {
  63. $query = <<<'GQL'
  64. query readProfile($id: ID!) {
  65. readCustomerProfile(id: $id) {
  66. id
  67. firstName
  68. lastName
  69. email
  70. }
  71. }
  72. GQL;
  73. $response = $this->graphQL($query, [
  74. 'id' => '/api/shop/customer-profiles/1',
  75. ]);
  76. $response->assertSuccessful();
  77. $json = $response->json();
  78. expect($json)->toHaveKey('errors')
  79. ->and($json['errors'])->not->toBeEmpty();
  80. }
  81. /**
  82. * Test: Read profile returns correct id format
  83. */
  84. public function test_read_profile_returns_correct_id_format(): void
  85. {
  86. $customer = $this->createCustomer();
  87. $query = <<<'GQL'
  88. query readProfile {
  89. readCustomerProfile {
  90. id
  91. }
  92. }
  93. GQL;
  94. $response = $this->authenticatedGraphQL($customer, $query, [
  95. 'id' => '/api/shop/customer-profiles/'.$customer->id,
  96. ]);
  97. $response->assertSuccessful();
  98. $data = $response->json('data.readCustomerProfile');
  99. expect($data)->not->toBeNull()
  100. ->and($data['id'])->toContain('/api/shop/customer-profiles/');
  101. }
  102. /**
  103. * Test: Read profile with selective fields
  104. */
  105. public function test_read_profile_with_selective_fields(): void
  106. {
  107. $customer = $this->createCustomer([
  108. 'first_name' => 'Bob',
  109. 'email' => 'bob@example.com',
  110. ]);
  111. $query = <<<'GQL'
  112. query readProfile {
  113. readCustomerProfile {
  114. firstName
  115. email
  116. }
  117. }
  118. GQL;
  119. $response = $this->authenticatedGraphQL($customer, $query, [
  120. 'id' => '/api/shop/customer-profiles/'.$customer->id,
  121. ]);
  122. $response->assertSuccessful();
  123. $data = $response->json('data.readCustomerProfile');
  124. expect($data)->not->toBeNull()
  125. ->and($data['firstName'])->toBe('Bob')
  126. ->and($data['email'])->toBe('bob@example.com')
  127. ->and($data)->not->toHaveKey('lastName')
  128. ->and($data)->not->toHaveKey('phone');
  129. }
  130. // ── Update Profile (Mutation) ─────────────────────────────
  131. /**
  132. * Test: Update customer first name
  133. */
  134. public function test_update_customer_first_name(): void
  135. {
  136. $customer = $this->createCustomer([
  137. 'first_name' => 'Original',
  138. 'last_name' => 'Name',
  139. ]);
  140. $mutation = <<<'GQL'
  141. mutation updateProfile($input: createCustomerProfileUpdateInput!) {
  142. createCustomerProfileUpdate(input: $input) {
  143. clientMutationId
  144. }
  145. }
  146. GQL;
  147. $response = $this->authenticatedGraphQL($customer, $mutation, [
  148. 'input' => ['firstName' => 'Updated'],
  149. ]);
  150. $response->assertSuccessful();
  151. $response->assertJsonPath('data.createCustomerProfileUpdate.clientMutationId', null);
  152. $customer->refresh();
  153. expect($customer->first_name)->toBe('Updated')
  154. ->and($customer->last_name)->toBe('Name');
  155. }
  156. /**
  157. * Test: Update multiple profile fields
  158. */
  159. public function test_update_multiple_profile_fields(): void
  160. {
  161. $customer = $this->createCustomer([
  162. 'first_name' => 'Old',
  163. 'last_name' => 'User',
  164. 'phone' => '000-0000',
  165. ]);
  166. $mutation = <<<'GQL'
  167. mutation updateProfile($input: createCustomerProfileUpdateInput!) {
  168. createCustomerProfileUpdate(input: $input) {
  169. clientMutationId
  170. }
  171. }
  172. GQL;
  173. $response = $this->authenticatedGraphQL($customer, $mutation, [
  174. 'input' => [
  175. 'firstName' => 'NewFirst',
  176. 'lastName' => 'NewLast',
  177. 'phone' => '999-8888',
  178. 'gender' => 'Male',
  179. ],
  180. ]);
  181. $response->assertSuccessful();
  182. $customer->refresh();
  183. expect($customer->first_name)->toBe('NewFirst')
  184. ->and($customer->last_name)->toBe('NewLast')
  185. ->and($customer->phone)->toBe('999-8888')
  186. ->and($customer->gender)->toBe('Male');
  187. }
  188. /**
  189. * Test: Update profile without authentication returns error
  190. */
  191. public function test_update_profile_without_auth_returns_error(): void
  192. {
  193. $mutation = <<<'GQL'
  194. mutation updateProfile($input: createCustomerProfileUpdateInput!) {
  195. createCustomerProfileUpdate(input: $input) {
  196. clientMutationId
  197. }
  198. }
  199. GQL;
  200. $response = $this->graphQL($mutation, [
  201. 'input' => ['firstName' => 'Hacker'],
  202. ]);
  203. $response->assertSuccessful();
  204. $json = $response->json();
  205. expect($json)->toHaveKey('errors')
  206. ->and($json['errors'])->not->toBeEmpty();
  207. }
  208. /**
  209. * Test: Update customer email
  210. */
  211. public function test_update_customer_email(): void
  212. {
  213. $customer = $this->createCustomer([
  214. 'email' => 'old@example.com',
  215. ]);
  216. $mutation = <<<'GQL'
  217. mutation updateProfile($input: createCustomerProfileUpdateInput!) {
  218. createCustomerProfileUpdate(input: $input) {
  219. clientMutationId
  220. }
  221. }
  222. GQL;
  223. $response = $this->authenticatedGraphQL($customer, $mutation, [
  224. 'input' => ['email' => 'new@example.com'],
  225. ]);
  226. $response->assertSuccessful();
  227. $customer->refresh();
  228. expect($customer->email)->toBe('new@example.com');
  229. }
  230. /**
  231. * Test: Update customer date of birth
  232. */
  233. public function test_update_customer_date_of_birth(): void
  234. {
  235. $customer = $this->createCustomer([
  236. 'date_of_birth' => '1990-01-01',
  237. ]);
  238. $mutation = <<<'GQL'
  239. mutation updateProfile($input: createCustomerProfileUpdateInput!) {
  240. createCustomerProfileUpdate(input: $input) {
  241. clientMutationId
  242. }
  243. }
  244. GQL;
  245. $response = $this->authenticatedGraphQL($customer, $mutation, [
  246. 'input' => ['dateOfBirth' => '1985-12-25'],
  247. ]);
  248. $response->assertSuccessful();
  249. $customer->refresh();
  250. expect($customer->date_of_birth)->toBe('1985-12-25');
  251. }
  252. /**
  253. * Test: Update newsletter subscription
  254. */
  255. public function test_update_newsletter_subscription(): void
  256. {
  257. $customer = $this->createCustomer([
  258. 'subscribed_to_news_letter' => false,
  259. ]);
  260. $mutation = <<<'GQL'
  261. mutation updateProfile($input: createCustomerProfileUpdateInput!) {
  262. createCustomerProfileUpdate(input: $input) {
  263. clientMutationId
  264. }
  265. }
  266. GQL;
  267. $response = $this->authenticatedGraphQL($customer, $mutation, [
  268. 'input' => ['subscribedToNewsLetter' => true],
  269. ]);
  270. $response->assertSuccessful();
  271. $customer->refresh();
  272. expect((bool) $customer->subscribed_to_news_letter)->toBeTrue();
  273. }
  274. /**
  275. * Test: Update with empty input does not change data
  276. */
  277. public function test_update_with_empty_input_preserves_data(): void
  278. {
  279. $customer = $this->createCustomer([
  280. 'first_name' => 'Unchanged',
  281. 'last_name' => 'User',
  282. ]);
  283. $mutation = <<<'GQL'
  284. mutation updateProfile($input: createCustomerProfileUpdateInput!) {
  285. createCustomerProfileUpdate(input: $input) {
  286. clientMutationId
  287. }
  288. }
  289. GQL;
  290. $response = $this->authenticatedGraphQL($customer, $mutation, [
  291. 'input' => [],
  292. ]);
  293. $response->assertSuccessful();
  294. $customer->refresh();
  295. expect($customer->first_name)->toBe('Unchanged')
  296. ->and($customer->last_name)->toBe('User');
  297. }
  298. /**
  299. * Test: Verify update is confirmed by subsequent read
  300. */
  301. public function test_update_confirmed_by_read_query(): void
  302. {
  303. $customer = $this->createCustomer([
  304. 'first_name' => 'Before',
  305. 'last_name' => 'Update',
  306. ]);
  307. $mutation = <<<'GQL'
  308. mutation updateProfile($input: createCustomerProfileUpdateInput!) {
  309. createCustomerProfileUpdate(input: $input) {
  310. clientMutationId
  311. }
  312. }
  313. GQL;
  314. $this->authenticatedGraphQL($customer, $mutation, [
  315. 'input' => [
  316. 'firstName' => 'After',
  317. 'lastName' => 'Change',
  318. ],
  319. ]);
  320. $readQuery = <<<'GQL'
  321. query readProfile {
  322. readCustomerProfile {
  323. firstName
  324. lastName
  325. }
  326. }
  327. GQL;
  328. $response = $this->authenticatedGraphQL($customer, $readQuery, [
  329. 'id' => '/api/shop/customer-profiles/'.$customer->id,
  330. ]);
  331. $response->assertSuccessful();
  332. $data = $response->json('data.readCustomerProfile');
  333. expect($data['firstName'])->toBe('After')
  334. ->and($data['lastName'])->toBe('Change');
  335. }
  336. // ── Delete Profile (Mutation) ─────────────────────────────
  337. /**
  338. * Test: Delete customer profile removes the customer
  339. */
  340. public function test_delete_customer_profile(): void
  341. {
  342. $customer = $this->createCustomer([
  343. 'first_name' => 'ToDelete',
  344. 'email' => 'delete-me@example.com',
  345. ]);
  346. $customerId = $customer->id;
  347. $mutation = <<<'GQL'
  348. mutation deleteProfile($input: createCustomerProfileDeleteInput!) {
  349. createCustomerProfileDelete(input: $input) {
  350. clientMutationId
  351. }
  352. }
  353. GQL;
  354. $response = $this->authenticatedGraphQL($customer, $mutation, [
  355. 'input' => [],
  356. ]);
  357. $response->assertSuccessful();
  358. expect(Customer::find($customerId))->toBeNull();
  359. }
  360. /**
  361. * Test: Delete profile also removes access tokens
  362. */
  363. public function test_delete_profile_removes_tokens(): void
  364. {
  365. $customer = $this->createCustomer([
  366. 'email' => 'token-delete@example.com',
  367. ]);
  368. $customer->createToken('token-1');
  369. $customer->createToken('token-2');
  370. $tokenCount = DB::table('personal_access_tokens')
  371. ->where('tokenable_id', $customer->id)
  372. ->where('tokenable_type', Customer::class)
  373. ->count();
  374. expect($tokenCount)->toBeGreaterThanOrEqual(2);
  375. $mutation = <<<'GQL'
  376. mutation deleteProfile($input: createCustomerProfileDeleteInput!) {
  377. createCustomerProfileDelete(input: $input) {
  378. clientMutationId
  379. }
  380. }
  381. GQL;
  382. $this->authenticatedGraphQL($customer, $mutation, [
  383. 'input' => [],
  384. ]);
  385. $remainingTokens = DB::table('personal_access_tokens')
  386. ->where('tokenable_id', $customer->id)
  387. ->where('tokenable_type', Customer::class)
  388. ->count();
  389. expect($remainingTokens)->toBe(0);
  390. }
  391. /**
  392. * Test: Delete profile without authentication returns error
  393. */
  394. public function test_delete_profile_without_auth_returns_error(): void
  395. {
  396. $mutation = <<<'GQL'
  397. mutation deleteProfile($input: createCustomerProfileDeleteInput!) {
  398. createCustomerProfileDelete(input: $input) {
  399. clientMutationId
  400. }
  401. }
  402. GQL;
  403. $response = $this->graphQL($mutation, [
  404. 'input' => [],
  405. ]);
  406. $response->assertSuccessful();
  407. $json = $response->json();
  408. expect($json)->toHaveKey('errors')
  409. ->and($json['errors'])->not->toBeEmpty();
  410. }
  411. /**
  412. * Test: Read profile after delete returns error
  413. */
  414. public function test_read_profile_after_delete_returns_error(): void
  415. {
  416. $customer = $this->createCustomer([
  417. 'email' => 'ghost@example.com',
  418. ]);
  419. $customerId = $customer->id;
  420. $deleteMutation = <<<'GQL'
  421. mutation deleteProfile($input: createCustomerProfileDeleteInput!) {
  422. createCustomerProfileDelete(input: $input) {
  423. clientMutationId
  424. }
  425. }
  426. GQL;
  427. $this->authenticatedGraphQL($customer, $deleteMutation, [
  428. 'input' => [],
  429. ]);
  430. expect(Customer::find($customerId))->toBeNull();
  431. $readQuery = <<<'GQL'
  432. query readProfile($id: ID!) {
  433. readCustomerProfile(id: $id) {
  434. id
  435. firstName
  436. }
  437. }
  438. GQL;
  439. $response = $this->authenticatedGraphQL($customer, $readQuery, [
  440. 'id' => '/api/shop/customer-profiles/'.$customerId,
  441. ]);
  442. $json = $response->json();
  443. expect($json)->toHaveKey('errors')
  444. ->and($json['errors'])->not->toBeEmpty();
  445. }
  446. // ── Schema Introspection ──────────────────────────────────
  447. /**
  448. * Test: CustomerProfile type has expected fields in schema
  449. */
  450. public function test_customer_profile_schema_has_expected_fields(): void
  451. {
  452. $query = <<<'GQL'
  453. {
  454. __type(name: "CustomerProfile") {
  455. name
  456. fields {
  457. name
  458. }
  459. }
  460. }
  461. GQL;
  462. $response = $this->graphQL($query);
  463. $response->assertSuccessful();
  464. $type = $response->json('data.__type');
  465. expect($type)->not->toBeNull()
  466. ->and($type['name'])->toBe('CustomerProfile');
  467. $fieldNames = array_column($type['fields'], 'name');
  468. expect($fieldNames)
  469. ->toContain('id')
  470. ->toContain('firstName')
  471. ->toContain('lastName')
  472. ->toContain('email')
  473. ->toContain('phone')
  474. ->toContain('gender')
  475. ->toContain('dateOfBirth')
  476. ->toContain('status')
  477. ->toContain('subscribedToNewsLetter')
  478. ->toContain('isVerified')
  479. ->toContain('isSuspended')
  480. ->toContain('image');
  481. }
  482. /**
  483. * Test: Update mutation input has expected fields
  484. */
  485. public function test_update_mutation_input_has_expected_fields(): void
  486. {
  487. $query = <<<'GQL'
  488. {
  489. __type(name: "createCustomerProfileUpdateInput") {
  490. name
  491. inputFields {
  492. name
  493. }
  494. }
  495. }
  496. GQL;
  497. $response = $this->graphQL($query);
  498. $response->assertSuccessful();
  499. $type = $response->json('data.__type');
  500. expect($type)->not->toBeNull();
  501. $fieldNames = array_column($type['inputFields'], 'name');
  502. expect($fieldNames)
  503. ->toContain('firstName')
  504. ->toContain('lastName')
  505. ->toContain('email')
  506. ->toContain('phone')
  507. ->toContain('gender')
  508. ->toContain('dateOfBirth')
  509. ->toContain('password')
  510. ->toContain('confirmPassword');
  511. }
  512. /**
  513. * Test: Delete mutation exists in schema
  514. */
  515. public function test_delete_mutation_exists_in_schema(): void
  516. {
  517. $query = <<<'GQL'
  518. {
  519. __schema {
  520. mutationType {
  521. fields {
  522. name
  523. }
  524. }
  525. }
  526. }
  527. GQL;
  528. $response = $this->graphQL($query);
  529. $response->assertSuccessful();
  530. $mutationNames = array_column(
  531. $response->json('data.__schema.mutationType.fields'),
  532. 'name'
  533. );
  534. expect($mutationNames)
  535. ->toContain('createCustomerProfileUpdate')
  536. ->toContain('createCustomerProfileDelete');
  537. }
  538. }