47 lines
4.6 KiB
TypeScript
47 lines
4.6 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { buckets, expired, inventoryExpired, parseDashboardDrilldown, previousDate } from './dashboardNavigation'
|
|
import type { QueryContext } from './types'
|
|
|
|
const context: QueryContext = { salesRunId: '11', inventoryRunId: '12', salesScopeHash: 'sales', inventoryScopeHash: 'stock', queryAsOf: '2026-10-13T00:00:00Z', queryExpiresAt: '2026-10-13T00:30:00Z', businessTimezone: 'Asia/Shanghai', anchorDate: '2026-10-13', rangeStartDate: '2026-09-29', rangeEndDateExclusive: '2026-10-14', inventoryDrilldownExpiresAt: '2026-10-13T00:10:00Z' }
|
|
const base = { from: 'overview', warehouseCode: 'WH-A', context: JSON.stringify(context), overviewReturn: JSON.stringify({ rangePreset: 'LAST_15_DAYS', scroll: 650 }) }
|
|
describe('dashboard exact drilldown contract', () => {
|
|
it.each(buckets)('maps $code to exact server bucket rather than an attention approximation', ({ code }) => {
|
|
const parsed = parseDashboardDrilldown({ ...base, coverageBucket: code, bucketRuleVersion: 'COVERAGE_DISTRIBUTION_V1' }, 'inventory')!
|
|
expect(parsed.inventory).toMatchObject({ coverageBucket: code, bucketRuleVersion: 'COVERAGE_DISTRIBUTION_V1', drilldownFrom: 'DASHBOARD', attention: 'ALL' })
|
|
expect(parsed.context).toEqual(context)
|
|
expect(parsed).toMatchObject({ rangePreset: 'CUSTOM', startDate: '2026-09-29', endDate: '2026-10-13' })
|
|
expect(parsed.returnTo).toEqual({ rangePreset: 'LAST_15_DAYS', scroll: 650 })
|
|
})
|
|
it('preserves exact owner/SKU, maps hot enum and the independent finite candidate selector', () => {
|
|
expect(parseDashboardDrilldown({ ...base, analysis: 'HOT', rangePreset: 'CUSTOM', startDate: '2026-09-13', endDate: '2026-10-12', customerCode: 'OWNER-B', sku: 'SKU-X' }, 'sales')).toMatchObject({ customerCode: 'OWNER-B', analysis: 'hot' })
|
|
expect(parseDashboardDrilldown({ ...base, coverageSelection: 'FINITE_POSITIVE', sortBy: 'COVERAGE_DAYS_DESC' }, 'inventory')?.inventory).toMatchObject({ coverageSelection: 'FINITE_POSITIVE', sortBy: 'COVERAGE_DAYS_DESC' })
|
|
expect(parseDashboardDrilldown({ ...base, analysis: 'SKU', rangePreset: 'CUSTOM', startDate: '2026-10-12', endDate: '2026-10-12' }, 'sales')).toMatchObject({ analysis: 'summary', startDate: '2026-10-12', endDate: '2026-10-12' })
|
|
})
|
|
it.each([
|
|
{ coverageBucket: 'BAD', bucketRuleVersion: 'COVERAGE_DISTRIBUTION_V1' },
|
|
{ coverageBucket: 'DAYS_GT_90', bucketRuleVersion: 'BAD' },
|
|
{ bucketRuleVersion: 'COVERAGE_DISTRIBUTION_V1' },
|
|
{ coverageBucket: 'DAYS_GT_90', bucketRuleVersion: 'COVERAGE_DISTRIBUTION_V1', attention: 'NO_OUTBOUND_IN_WINDOW' },
|
|
{ coverageSelection: 'FINITE_POSITIVE', attention: 'NO_OUTBOUND_IN_WINDOW' },
|
|
{ coverageSelection: 'FINITE_POSITIVE', coverageBucket: 'UNKNOWN', bucketRuleVersion: 'COVERAGE_DISTRIBUTION_V1' },
|
|
{ context: '{}' }, { context: JSON.stringify({ ...context, rangeStartDate: undefined }) }, { context: JSON.stringify({ ...context, rangeEndDateExclusive: '2026-09-01' }) }, { warehouseCode: '' }, { drilldownFrom: 'OTHER' },
|
|
])('rejects malformed/conflicting parameters without widening to the whole warehouse: %j', invalid => {
|
|
expect(() => parseDashboardDrilldown(Object.fromEntries(Object.entries({ ...base, ...invalid }).filter(([, value]) => value !== undefined)) as Record<string, string>, 'inventory')).toThrow()
|
|
})
|
|
it('rejects invalid date/analysis/source and keeps warehouse selection mandatory', () => {
|
|
expect(parseDashboardDrilldown({ ...base, analysis: 'HOT', rangePreset: 'CUSTOM', startDate: '2026-09-01', endDate: '2026-10-13' }, 'sales')).toMatchObject({ analysis: 'hot', rangePreset: 'CUSTOM', startDate: '2026-09-01', endDate: '2026-10-13' })
|
|
expect(() => parseDashboardDrilldown({ ...base, analysis: 'HOT', windowDays: '30' }, 'sales')).toThrow()
|
|
expect(() => parseDashboardDrilldown({ ...base, analysis: 'HOT', rangePreset: 'CUSTOM', startDate: '2026-10-13', endDate: '2026-10-12' }, 'sales')).toThrow()
|
|
expect(() => parseDashboardDrilldown({ ...base, analysis: 'ALL' }, 'sales')).toThrow()
|
|
expect(() => parseDashboardDrilldown({ ...base, analysis: 'SKU', rangePreset: 'CUSTOM', startDate: '2026-02-30', endDate: '2026-02-30' }, 'sales')).toThrow()
|
|
expect(() => parseDashboardDrilldown({ ...base, from: 'https://example.com' }, 'sales')).toThrow()
|
|
expect(() => parseDashboardDrilldown({ coverageBucket: 'UNKNOWN' }, 'inventory')).toThrow()
|
|
})
|
|
it('expires inventory earlier without expiring sales and subtracts date boundaries without local timezone drift', () => {
|
|
const now = Date.parse('2026-10-13T00:15:00Z')
|
|
expect(inventoryExpired(context, now)).toBe(true)
|
|
expect(expired(context, now)).toBe(false)
|
|
expect(previousDate('2026-03-01')).toBe('2026-02-28')
|
|
})
|
|
})
|