Files
baiyue-firstleg/scripts/admin-test-env-modules/appointment-work-contract.test.mjs
T

93 lines
4.3 KiB
JavaScript
Raw Normal View History

2026-10-20 14:22:55 +08:00
import assert from 'node:assert/strict'
import test from 'node:test'
import { readFileSync } from 'node:fs'
import { run } from './appointment-work.mjs'
const entry = readFileSync(new URL('../admin-test-env-acceptance.mjs', import.meta.url), 'utf8')
const moduleSource = readFileSync(new URL('./appointment-work.mjs', import.meta.url), 'utf8')
function fakeContext(overrides = {}) {
const calls = []
const gaps = []
const steps = []
const context = {
marker: 'ADMIN-API-OFFLINE',
calls,
gaps,
steps,
async request(method, path, body) {
calls.push({ method, path, body })
const url = new URL(path, 'https://offline.invalid')
if (url.pathname.endsWith('/calendar')) {
const from = url.searchParams.get('from')
const to = url.searchParams.get('to')
return { http: 200, body: { code: '200', data: { from, to, total: 2, days: [
{ date: from, total: 1, typeCounts: { DELIVERY: 1 } },
{ date: to, total: 1, typeCounts: { PICKUP: 1 } },
] } } }
}
if (url.pathname.endsWith('/summary')) {
return { http: 200, body: { code: '200', data: { all: 2, followToday: 1, overdue: 0, undated: 1, review: 0 } } }
}
if (url.pathname.endsWith('/items')) {
return { http: 200, body: { code: '200', data: overrides.emptyPage
? { records: [], page: 1, size: 20, total: 0, pages: 0 }
: { records: [{ workItemKey: 'WORK-1', type: 'DELIVERY', flow: 'TRANSFER' }], page: 1, size: 20, total: 1, pages: 1 } } }
}
if (url.pathname.includes('/items/WORK-1')) {
return { http: 200, body: { code: '200', data: {
item: { workItemKey: 'WORK-1', type: 'DELIVERY', flow: 'TRANSFER', containers: [{ containerBookingId: 'C-1' }], allowedActions: [] },
references: [], history: [],
} } }
}
if (url.pathname.endsWith('/commands')) {
return { http: 200, body: { code: 'A0404', message: '工作事项不存在' } }
}
throw new Error(`unexpected offline request ${method} ${path}`)
},
apiSuccess(module, name, response) {
assert.equal(response.http, 200)
assert.equal(String(response.body?.code), '200')
steps.push({ module, name, passed: true })
return response.body.data
},
assert(module, name, response, condition, evidence) {
assert.equal(Boolean(condition), true, `${name}: ${evidence}`)
steps.push({ module, name, passed: true })
return true
},
addCoverageGap(module, scenario, name, reason, category, metadata) {
gaps.push({ module, scenario, name, reason, category, metadata })
},
}
return context
}
test('预约工作模块在默认全量中注册且只读查询官方工作事项接口', () => {
assert.match(entry, /const CONFIGURED_MODULES = \[[^\n]*'appointment-work'/)
assert.match(moduleSource, /\/api\/platform\/v1\/last-leg\/appointment-work/)
for (const path of ['/calendar', '/summary', '/items', '/commands']) assert.match(moduleSource, new RegExp(path.replace('/', '\\/')))
assert.match(moduleSource, /command: 'SAVE_FOLLOWUP'/)
assert.doesNotMatch(moduleSource, /this\.addStep|writeFileSync|execSync/)
})
test('预约工作只读链按日历、汇总、分页、详情顺序调用,唯一写入是不存在事项的拒绝命令', async () => {
const context = fakeContext()
await run(context)
assert.deepEqual(context.calls.map((row) => row.method), ['GET', 'GET', 'GET', 'GET', 'POST'])
assert.ok(context.calls[0].path.includes('/calendar?'))
assert.ok(context.calls[1].path.endsWith('/summary'))
assert.ok(context.calls[2].path.includes('/items?view=ALL&page=1&size=20'))
assert.ok(context.calls[3].path.includes('/items/WORK-1'))
assert.ok(context.calls[4].path.includes('/items/FULL-ACCEPTANCE-MISSING-WORK-ITEM/commands'))
assert.equal(context.calls[4].body.command, 'SAVE_FOLLOWUP')
assert.ok(context.gaps.some((gap) => gap.name === '预约写命令正向链路' && gap.metadata.blocking === false))
})
test('无本轮工作事项时记录非阻断覆盖缺口而不是虚构详情或命令结果', async () => {
const context = fakeContext({ emptyPage: true })
await run(context)
assert.equal(context.calls.length, 3)
assert.ok(context.gaps.some((gap) => gap.name === '预约工作项详情与命令' && gap.metadata.blocking === false))
})