优化
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { run as runPdaReleases } from './pda-releases.mjs'
|
||||
import { run as runAudit } from './audit.mjs'
|
||||
import { run as runSailingCalendar } from './sailing-calendar.mjs'
|
||||
|
||||
const entry = readFileSync(new URL('../admin-test-env-acceptance.mjs', import.meta.url), 'utf8')
|
||||
|
||||
function context(handler) {
|
||||
const calls = []
|
||||
const gaps = []
|
||||
return {
|
||||
marker: 'ADMIN-API-OFFLINE',
|
||||
calls,
|
||||
gaps,
|
||||
async request(method, path, body) {
|
||||
calls.push({ method, path, body })
|
||||
return handler(method, path, body)
|
||||
},
|
||||
async requestMultipart(method, path, form) {
|
||||
calls.push({ method, path, multipart: form })
|
||||
return handler(method, path, form)
|
||||
},
|
||||
apiSuccess(_module, _name, response) {
|
||||
assert.equal(response.http, 200)
|
||||
assert.equal(String(response.body?.code), '200')
|
||||
return response.body.data
|
||||
},
|
||||
assert(_module, name, _response, condition, evidence) {
|
||||
assert.equal(Boolean(condition), true, `${name}: ${evidence}`)
|
||||
return true
|
||||
},
|
||||
addCoverageGap(module, scenario, name, reason, category, metadata) {
|
||||
gaps.push({ module, scenario, name, reason, category, metadata })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
test('PDA 版本模块注册为默认模块并只读核对双端发布列表', async () => {
|
||||
assert.match(entry, /const CONFIGURED_MODULES = \[[^\n]*'pda-releases'/)
|
||||
const releases = (app) => [{
|
||||
id: '1', app, appid: `__UNI__${app}`, packageType: 'apk', versionName: '1.0.0', versionCode: 1,
|
||||
minAppVersionCode: 1, mandatory: false, releaseNotes: 'offline', status: 'PUBLISHED', current: true,
|
||||
sha256: 'a'.repeat(64), sizeBytes: 1024, downloadUrl: '/download', createdAt: '2026-10-01T00:00:00Z',
|
||||
publishedAt: '2026-10-01T00:00:00Z',
|
||||
}]
|
||||
const ctx = context((method, path) => {
|
||||
if (method === 'GET') return { http: 200, body: { code: '200', data: releases(path.endsWith('/domestic') ? 'domestic' : 'overseas') } }
|
||||
if (method === 'POST' && path.includes('/publish')) return { http: 200, body: { code: 'A0404', message: '不存在' } }
|
||||
if (method === 'POST' && path.includes('/withdraw')) return { http: 200, body: { code: 'A0404', message: '不存在' } }
|
||||
return { http: 400, body: { code: 'A0400', message: '文件不能为空' } }
|
||||
})
|
||||
await runPdaReleases(ctx)
|
||||
assert.deepEqual(ctx.calls.map((row) => row.method), ['GET', 'GET', 'POST', 'POST', 'POST'])
|
||||
assert.ok(ctx.calls[0].path.endsWith('/domestic'))
|
||||
assert.ok(ctx.calls[1].path.endsWith('/overseas'))
|
||||
assert.ok(ctx.gaps.some((gap) => gap.name === '真实 APK/WGT 上传、发布、停发与下载' && gap.metadata.blocking === false))
|
||||
})
|
||||
|
||||
test('平台审计模块注册为默认模块,列表不泄漏快照且详情身份一致', async () => {
|
||||
assert.match(entry, /const CONFIGURED_MODULES = \[[^\n]*'audit'/)
|
||||
const row = { id: '7', eventId: 'e-7', eventType: 'ORDER_SUBMIT', resourceType: 'ORDER', resourceId: '1',
|
||||
accountId: '0', operatorTenantId: '0', userId: '1', occurredAt: '2026-10-01T00:00:00Z', success: true }
|
||||
const detailCtx = context((method, path) => {
|
||||
if (path.endsWith('/999999999999999')) return { http: 200, body: { code: 'A0404', message: '不存在' } }
|
||||
if (path.endsWith('/7')) return { http: 200, body: { code: '200', data: { ...row, httpMethod: 'POST' } } }
|
||||
return { http: 200, body: { code: '200', data: { records: [row], page: 1, size: 20, total: 1, pages: 1 } } }
|
||||
})
|
||||
const withSnapshot = context((method, path) => {
|
||||
if (path.includes('/audit/logs/')) return { http: 200, body: { code: 'A0404', message: '不存在' } }
|
||||
return { http: 200, body: { code: '200', data: { records: [{ ...row, beforeData: '{}' }], page: 1, size: 20, total: 1, pages: 1 } } }
|
||||
})
|
||||
await runAudit(detailCtx)
|
||||
assert.equal(detailCtx.calls[0].method, 'GET')
|
||||
assert.ok(detailCtx.calls[0].path.includes('/audit/logs?page=1&size=20'))
|
||||
assert.equal(detailCtx.calls[1].path, '/api/platform/v1/audit/logs/7')
|
||||
assert.ok(detailCtx.calls[2].path.endsWith('/999999999999999'))
|
||||
await assert.rejects(runAudit(withSnapshot), /不返回变更前后快照/)
|
||||
})
|
||||
|
||||
test('船期日历模块注册为默认模块并核对月历、渠道、模板与异常分配', async () => {
|
||||
assert.match(entry, /const CONFIGURED_MODULES = \[[^\n]*'sailing-calendar'/)
|
||||
const summary = {
|
||||
actualReceivedCbm: 1, catchableForecastCbm: 2, riskUnmatchedCbm: 0, bookedContainerCount: 1,
|
||||
recommendedContainerCount: 1, pendingContainerCount: 0, lateBatchCount: 0, overCapacity: false,
|
||||
planningCapacityCbm: 10, bookedPlanningCapacityCbm: 5, catchableReceivedCbm: 1, lateReceivedCbm: 0,
|
||||
snapshotAt: null, sailingCount: 1,
|
||||
}
|
||||
const sailing = {
|
||||
id: '11', sailingNo: 'SAIL-11', warehouseId: '1', channelCode: 'SEA', channelName: '海运', templateId: null,
|
||||
channelId: null, etaAt: '2026-10-20T00:00:00', sourceType: 'MANUAL', plannedCapacityCbm: 10,
|
||||
volumeSummary: summary, version: 0,
|
||||
}
|
||||
const ctx = context((method, path) => {
|
||||
if (path.includes('/calendar')) return { http: 200, body: { code: '200', data: {
|
||||
records: [sailing], summary, filterOptions: { warehouses: [], channels: [], departurePorts: [], destinationPorts: [] } } } }
|
||||
if (path.includes('/sailing-channels')) return { http: 200, body: { code: '200', data: [{ id: '1', channelCode: 'SEA', defaultCutoffDays: 3, version: 0 }] } }
|
||||
if (path.includes('/sailing-templates')) return { http: 200, body: { code: '200', data: { records: [{ id: '1', channelId: '1', warehouseId: '1', weekdays: [1, 2], cutoffDays: 3, version: 0 }] } } }
|
||||
if (path.includes('/sailing-assignments/exceptions')) return { http: 200, body: { code: '200', data: { records: [{ assignmentId: '1', volumeCbm: 1 }], total: 1 } } }
|
||||
if (path.endsWith('/11')) return { http: 200, body: { code: '200', data: {
|
||||
sailing: { ...sailing, volumeSummary: summary }, histories: [] } } }
|
||||
if (path.endsWith('/999999999999999')) return { http: 200, body: { code: 'A0404', message: '不存在' } }
|
||||
throw new Error(`unexpected ${method} ${path}`)
|
||||
})
|
||||
await runSailingCalendar(ctx)
|
||||
assert.deepEqual(ctx.calls.map((row) => row.method), ['GET', 'GET', 'GET', 'GET', 'GET', 'GET'])
|
||||
assert.ok(ctx.calls[0].path.includes('/sailings/calendar?month='))
|
||||
assert.ok(ctx.calls[1].path.endsWith('/sailing-channels'))
|
||||
assert.ok(ctx.calls[2].path.includes('/sailing-templates?page=1&size=200'))
|
||||
assert.ok(ctx.calls[3].path.includes('/sailing-assignments/exceptions'))
|
||||
assert.ok(ctx.calls[4].path.endsWith('/11'))
|
||||
assert.ok(ctx.calls[5].path.endsWith('/999999999999999'))
|
||||
})
|
||||
Reference in New Issue
Block a user