import { beforeEach, describe, expect, it, vi } from 'vitest' import { analyticsApi } from './analytics' const http = vi.hoisted(() => ({ get: vi.fn(), post: vi.fn() })) vi.mock('@/shared/api/http', () => ({ http, unwrapApiResponse: (value: { data: unknown }) => value.data })) beforeEach(() => { vi.clearAllMocks(); http.get.mockResolvedValue({ data: {} }); http.post.mockResolvedValue({ data: { taskNo: 'COL-001', status: 'QUEUED' } }) }) describe('self-operated platform transport contract', () => { it('forwards the inventory window, inclusive end and pinned page without renaming them', async () => { const params = { sourceAccount: 'lingxing-primary', warehouseCode: 'WH-A', salesWindowDays: 10 as const, salesWindowEndDate: '2026-10-12', current: 2, size: 20, salesRunId: '11', inventoryRunId: '12', queryAsOf: '2026-10-13T00:00:00Z' } await analyticsApi.inventory(params) expect(http.get).toHaveBeenCalledWith('/platform/v1/last-leg/self-operated-analytics/inventory-coverage', { params, signal: undefined }) }) it('keeps dashboard business aggregation separate from sync-overview runtime polling', async () => { const params = { sourceAccount: 'lingxing-primary', warehouseCode: 'WH-A', rangePreset: 'LAST_15_DAYS' as const, salesRunId: '11', inventoryRunId: '12', queryAsOf: '2026-10-13T00:00:00Z' } await analyticsApi.dashboard(params) expect(http.get).toHaveBeenCalledWith('/platform/v1/last-leg/self-operated-analytics/dashboard', { params, signal: undefined }) expect(params).not.toHaveProperty('customerCode') expect(params).not.toHaveProperty('current') }) it('uses local fixed-version owners, owner overview and inventory attention endpoints', async () => { const fixed = { sourceAccount: 'lingxing-primary', warehouseCode: 'WH-A', salesRunId: '11', inventoryRunId: '12', queryAsOf: '2026-10-12T01:00:00Z' } await analyticsApi.owners({ ...fixed, keyword: 'OWNER_', current: 2, size: 20 }) await analyticsApi.ownerOverview({ ...fixed, customerCode: 'OWNER_A', coverageDaysThreshold: 60 }) await analyticsApi.inventory({ ...fixed, customerCode: 'OWNER_A', attention: 'COVERAGE_GT_THRESHOLD', sortBy: 'PHYSICAL_DESC', coverageDaysThreshold: 60, current: 2, size: 20 }) expect(http.get.mock.calls.map(call => call[0])).toEqual(['owners', 'owner-overview', 'inventory-coverage'].map(path => `/platform/v1/last-leg/self-operated-analytics/${path}`)) expect(http.get.mock.calls[1]?.[1].params).toEqual({ ...fixed, customerCode: 'OWNER_A', coverageDaysThreshold: 60 }) expect(http.get.mock.calls[2]?.[1].params).toMatchObject({ attention: 'COVERAGE_GT_THRESHOLD', sortBy: 'PHYSICAL_DESC', current: 2 }) }) it('uses unified gateway domain with fixed context and abort signal, never supplier APIs', async () => { const abort = new AbortController() const query = { sourceAccount: 'lingxing-primary', warehouseCode: 'WH-A', current: 2, size: 20, salesRunId: '9007199254740999', inventoryRunId: '0', queryAsOf: '2026-10-12T01:00:00Z', salesScopeHash: 'hash' } await analyticsApi.summary(query, abort.signal) expect(http.get).toHaveBeenCalledWith('/platform/v1/last-leg/self-operated-analytics/sku-sales-summary', { params: query, signal: abort.signal }) }) it('reads task details by escaped taskNo with independent runtime list upper pair', async () => { await analyticsApi.task({ sourceAccount: 'lingxing-primary', warehouseCode: 'WH-A' }, 'COL/001') expect(http.get.mock.calls[0]?.[0]).toBe('/platform/v1/last-leg/self-operated-analytics/sync-tasks/COL%2F001') const params = { sourceAccount: 'lingxing-primary', warehouseCode: 'WH-A', current: 2, size: 20, listUpperCreatedAt: '2026-10-12T01:00:00', listUpperId: '9007199254740999' } await analyticsApi.tasks(params) expect(http.get.mock.calls[1]?.[1].params).toEqual(params) }) it('posts one RETRY command with business task number and exact idempotency key', async () => { const command = { action: 'RETRY' as const, taskNo: 'COL-001', warehouseCode: 'WH-A', requestKey: 'request-001', reason: '恢复原任务' } expect(await analyticsApi.retry(command)).toEqual({ taskNo: 'COL-001', status: 'QUEUED' }) expect(http.post).toHaveBeenCalledWith('/platform/v1/last-leg/self-operated-analytics/sync-tasks', command, { signal: undefined }) }) })