parse-rest-competitor-sync.test.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. import assert from 'node:assert/strict';
  2. import test from 'node:test';
  3. import type { ParseRestClient } from '../src/db/parse-rest.client.js';
  4. import { ParseRestCompetitorSyncService } from '../src/modules/domestic-voc/services/parse-rest-competitor-sync.service.js';
  5. import type { GatewayRequestClient } from '../src/modules/domestic-voc/services/jd-sync.service.js';
  6. import type { ParseRestVocRepository } from '../src/modules/saas-platform/parse-rest-voc.repository.js';
  7. test('competitor sync creates mapped placeholders and persists returned products as competitors', async () => {
  8. const placeholders: string[] = [];
  9. const products: Array<{ productId: string; role: string }> = [];
  10. const reviewProducts: Array<{ productId: string; count: number }> = [];
  11. const client = {
  12. async findAll() {
  13. return [
  14. { competitorProductId: 'c1', competitorBrand: 'Brand A', category: 'Category' },
  15. { competitorProductId: 'c1', competitorBrand: 'Brand A', category: 'Category' },
  16. { competitorProductId: 'c2', competitorBrand: 'Brand B', category: 'Category' },
  17. ];
  18. },
  19. async findOne() { return null; },
  20. async count() { return 0; },
  21. } as unknown as ParseRestClient;
  22. const repository = {
  23. async ensureCompetitorProduct(input: { productId: string }) { placeholders.push(input.productId); },
  24. async upsertProduct(_workspaceId: string, product: { productId: string; role: string }) {
  25. products.push({ productId: product.productId, role: product.role });
  26. },
  27. async upsertReviews(_workspaceId: string, _platform: string, productId: string, reviews: unknown[]) {
  28. reviewProducts.push({ productId, count: reviews.length });
  29. return reviews.length;
  30. },
  31. } as unknown as ParseRestVocRepository;
  32. const gateway: GatewayRequestClient = {
  33. async request(path, init) {
  34. const productId = String(init?.params?.itemId);
  35. if (path.includes('comments')) {
  36. return { data: { data: { data: [{
  37. commentId: `review-${productId}`,
  38. commentData: `Review ${productId}`,
  39. commentScore: '5',
  40. commentDate: '2026-07-24 10:00:00',
  41. }], pageInfo: { maxPage: 1, nowPage: 1 } } } } as never;
  42. }
  43. return { code: 200, data: { code: 200, data: { item: { itemId: productId, itemName: `Product ${productId}` } } } } as never;
  44. },
  45. };
  46. const result = await new ParseRestCompetitorSyncService(client, repository, gateway).sync({
  47. workspaceId: 'demashi',
  48. platform: 'jd',
  49. concurrency: 2,
  50. includeReviews: true,
  51. });
  52. assert.deepEqual(placeholders.sort(), ['c1', 'c2']);
  53. assert.deepEqual(products.sort((left, right) => left.productId.localeCompare(right.productId)), [
  54. { productId: 'c1', role: 'competitor' },
  55. { productId: 'c2', role: 'competitor' },
  56. ]);
  57. assert.deepEqual(reviewProducts.sort((left, right) => left.productId.localeCompare(right.productId)), [
  58. { productId: 'c1', count: 1 },
  59. { productId: 'c2', count: 1 },
  60. ]);
  61. assert.deepEqual(result, {
  62. total: 2,
  63. placeholders: 2,
  64. succeeded: 2,
  65. skipped: 0,
  66. failed: 0,
  67. failedProductIds: [],
  68. reviewProductsRequested: 2,
  69. reviewProductsSucceeded: 2,
  70. reviewProductsSkipped: 0,
  71. reviewProductsFailed: 0,
  72. reviewsUpserted: 2,
  73. failedReviewProductIds: [],
  74. });
  75. });