| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495 |
- import assert from 'node:assert/strict';
- import test from 'node:test';
- import {
- COMPETITOR_LISTING_REFRESH_EXIT,
- runCompetitorListingRefreshCli,
- } from '../scripts/refresh-competitor-listings.js';
- import { ApiError } from '../src/http/api-error.js';
- import type { CompetitorListingRefreshRun } from '../src/modules/competitor-listing-monitor/domain.js';
- const runId = '11111111-1111-4111-8111-111111111111';
- const requestedAt = '2026-08-26T00:00:00.000Z';
- function run(status: CompetitorListingRefreshRun['status']): CompetitorListingRefreshRun {
- const terminal = ['completed', 'partial', 'failed'].includes(status);
- return {
- id: runId,
- workspaceId: 'demashi',
- platform: 'jd',
- trigger: 'scheduled',
- status,
- total: 3,
- completed: terminal ? 3 : 0,
- baseline: status === 'completed' ? 3 : 0,
- unchanged: 0,
- changed: 0,
- failed: status === 'partial' ? 1 : status === 'failed' ? 3 : 0,
- itemResults: [],
- requestedAt,
- startedAt: status === 'queued' ? null : '2026-08-26T00:00:01.000Z',
- completedAt: terminal ? '2026-08-26T00:00:02.000Z' : null,
- };
- }
- test('scheduled CLI waits for terminal status and maps completed, partial and failed exit codes', async (context) => {
- for (const scenario of [
- { status: 'completed' as const, exitCode: COMPETITOR_LISTING_REFRESH_EXIT.completed },
- { status: 'partial' as const, exitCode: COMPETITOR_LISTING_REFRESH_EXIT.partial },
- { status: 'failed' as const, exitCode: COMPETITOR_LISTING_REFRESH_EXIT.failed },
- ]) {
- await context.test(scenario.status, async () => {
- const outputs: string[] = [];
- const errors: string[] = [];
- let trigger = '';
- let polls = 0;
- const service = {
- async startRefresh(_workspaceId: string, _platform: 'jd', value: 'manual' | 'scheduled') {
- trigger = value;
- return run('queued');
- },
- async getRun() {
- polls += 1;
- return polls === 1 ? run('running') : run(scenario.status);
- },
- };
- const exitCode = await runCompetitorListingRefreshCli({
- service,
- workspaceId: 'demashi',
- pollMs: 1,
- timeoutMs: 1_000,
- sleep: async () => undefined,
- write: (line) => outputs.push(line),
- writeError: (line) => errors.push(line),
- });
- assert.equal(trigger, 'scheduled');
- assert.equal(exitCode, scenario.exitCode);
- assert.equal(polls, 2);
- const terminalLine = scenario.status === 'failed' ? errors.at(-1) : outputs.at(-1);
- assert.equal((JSON.parse(terminalLine ?? '{}') as { status?: string }).status, scenario.status);
- });
- }
- });
- test('scheduled CLI reports a distinct exit code when a manual refresh already owns the workspace lock', async () => {
- const errors: string[] = [];
- const service = {
- async startRefresh(): Promise<CompetitorListingRefreshRun> {
- throw new ApiError(409, 'competitor_listing_refresh_running');
- },
- async getRun(): Promise<CompetitorListingRefreshRun | null> {
- throw new Error('must not poll after a lock conflict');
- },
- };
- const exitCode = await runCompetitorListingRefreshCli({
- service,
- workspaceId: 'demashi',
- write: () => undefined,
- writeError: (line) => errors.push(line),
- });
- assert.equal(exitCode, COMPETITOR_LISTING_REFRESH_EXIT.alreadyRunning);
- assert.equal((JSON.parse(errors[0] ?? '{}') as { error?: string }).error, 'competitor_listing_refresh_running');
- });
|