98 lines
4.5 KiB
TypeScript
98 lines
4.5 KiB
TypeScript
import {
|
|
DING_WORKFLOW_ALLOWED_PROCESS_CODES,
|
|
DING_WORKFLOW_STARTED_AT_CUTOFF_ISO,
|
|
buildBrandCenterOngoingWorkflowWhere,
|
|
getDingWorkflowProjectType,
|
|
isAllowedDingWorkflowProcessCode,
|
|
isDingWorkflowStartedOnOrAfterCutoff,
|
|
isDingWorkflowOngoing,
|
|
normalizeDingWorkflowBrand,
|
|
normalizeDingWorkflowDepartment,
|
|
normalizeDingWorkflowProcessCode,
|
|
sourceWorkflowTimestampToIso,
|
|
} from '@/lib/ding-workflow-sync'
|
|
|
|
jest.mock('mssql', () => ({
|
|
__esModule: true,
|
|
default: {},
|
|
}))
|
|
|
|
jest.mock('@/lib/prisma', () => ({ prisma: {} }))
|
|
jest.mock('@/lib/brand-center-access', () => ({ resolveBrandCenterAccessForUser: jest.fn() }))
|
|
|
|
describe('ding workflow snapshot sync semantics', () => {
|
|
test.each([
|
|
['RUNNING', null, true],
|
|
['NEW', '', true],
|
|
['COMPLETED', null, false],
|
|
['TERMINATED', null, false],
|
|
['CANCELED', null, false],
|
|
['RUNNING', '2026-08-20 10:30:00', false],
|
|
['', null, false],
|
|
])('status %s and finish time %s resolves ongoing=%s', (status, finishTime, expected) => {
|
|
expect(isDingWorkflowOngoing(status, finishTime)).toBe(expected)
|
|
})
|
|
|
|
it('interprets upstream timestamps as Asia/Shanghai when no zone is present', () => {
|
|
expect(sourceWorkflowTimestampToIso('2026-08-20 10:30:00')).toBe('2026-08-20T02:30:00.000Z')
|
|
expect(sourceWorkflowTimestampToIso('2026-08-20T10:30:00Z')).toBe('2026-08-20T10:30:00.000Z')
|
|
expect(sourceWorkflowTimestampToIso('invalid')).toBeNull()
|
|
})
|
|
|
|
it('only includes workflows started from 2026-04-01 Asia/Shanghai', () => {
|
|
expect(DING_WORKFLOW_STARTED_AT_CUTOFF_ISO).toBe('2026-03-31T16:00:00.000Z')
|
|
expect(isDingWorkflowStartedOnOrAfterCutoff('2026-03-31 23:59:59')).toBe(false)
|
|
expect(isDingWorkflowStartedOnOrAfterCutoff('2026-04-01 00:00:00')).toBe(true)
|
|
expect(isDingWorkflowStartedOnOrAfterCutoff('2026-03-31T15:59:59.999Z')).toBe(false)
|
|
expect(isDingWorkflowStartedOnOrAfterCutoff('2026-03-31T16:00:00.000Z')).toBe(true)
|
|
expect(isDingWorkflowStartedOnOrAfterCutoff('invalid')).toBe(false)
|
|
})
|
|
|
|
it('only accepts the two approved process types', () => {
|
|
expect(DING_WORKFLOW_ALLOWED_PROCESS_CODES).toEqual([
|
|
'PROC-3571C915-0506-4490-A91A-8AA7EA33F838',
|
|
'PROC-AE9FF3E1-A442-4E6A-85CF-0F2B86409673',
|
|
])
|
|
expect(isAllowedDingWorkflowProcessCode(' proc-3571c915-0506-4490-a91a-8aa7ea33f838 ')).toBe(true)
|
|
expect(isAllowedDingWorkflowProcessCode('PROC-AE9FF3E1-A442-4E6A-85CF-0F2B86409673')).toBe(true)
|
|
expect(isAllowedDingWorkflowProcessCode('PROC-OTHER')).toBe(false)
|
|
expect(isAllowedDingWorkflowProcessCode(null)).toBe(false)
|
|
})
|
|
|
|
it('only queries ongoing workflows whose current assignee belongs to Brand Center', () => {
|
|
expect(buildBrandCenterOngoingWorkflowWhere()).toEqual({
|
|
isOngoing: true,
|
|
processCode: { in: [...DING_WORKFLOW_ALLOWED_PROCESS_CODES] },
|
|
sourceCreatedAtRaw: { gte: DING_WORKFLOW_STARTED_AT_CUTOFF_ISO },
|
|
activeTasks: { array_contains: [{ isBrandCenter: true }] },
|
|
})
|
|
})
|
|
|
|
it('normalizes process codes before persistence and comparison', () => {
|
|
expect(normalizeDingWorkflowProcessCode(' proc-ae9ff3e1-a442-4e6a-85cf-0f2b86409673 '))
|
|
.toBe('PROC-AE9FF3E1-A442-4E6A-85CF-0F2B86409673')
|
|
expect(normalizeDingWorkflowProcessCode('')).toBeNull()
|
|
})
|
|
|
|
it('maps the two process codes to the dashboard task types', () => {
|
|
expect(getDingWorkflowProjectType('PROC-3571C915-0506-4490-A91A-8AA7EA33F838')).toBe('美术设计流程')
|
|
expect(getDingWorkflowProjectType('PROC-AE9FF3E1-A442-4E6A-85CF-0F2B86409673')).toBe('内容视频和图片拍摄')
|
|
expect(getDingWorkflowProjectType('PROC-OTHER')).toBe('其他流程')
|
|
})
|
|
|
|
it('normalizes approved brands and groups unmatched values as other brands', () => {
|
|
expect(normalizeDingWorkflowBrand('自有品牌-亿家达')).toBe('亿家达')
|
|
expect(normalizeDingWorkflowBrand('索菲亚')).toBe('索菲亚')
|
|
expect(normalizeDingWorkflowBrand('新品牌')).toBe('其他品牌')
|
|
expect(normalizeDingWorkflowBrand(null)).toBe('其他品牌')
|
|
})
|
|
|
|
it('normalizes DingTalk department fields before grouping requester dimensions', () => {
|
|
expect(normalizeDingWorkflowDepartment('["运营中心E"]')).toBe('运营中心E')
|
|
expect(normalizeDingWorkflowDepartment('[{"name":"品牌中心"}]')).toBe('品牌中心')
|
|
expect(normalizeDingWorkflowDepartment(['运营中心E', '运营中心E'])).toBe('运营中心E')
|
|
expect(normalizeDingWorkflowDepartment('综合支持部')).toBe('综合支持部')
|
|
expect(normalizeDingWorkflowDepartment(null)).toBeNull()
|
|
})
|
|
})
|