import { normalizeFixedCustomerId } from './merchant-qualification-fixture.mjs' export const FIXED_RESOURCE_READONLY_MODULES = Object.freeze([ 'customers', 'system', 'master-data', 'notifications', 'pricing', 'phase4-overseas-master-data', ]) const IAM = '/api/platform/v1/iam' const MASTER = '/api/platform/v1/last-leg/master-data' const decimalId = value => /^[1-9]\d*$/.test(String(value ?? '')) const pageShape = value => Array.isArray(value?.records) && Number(value.page) === 1 && Number(value.size) === 20 && ['string', 'number'].includes(typeof value.total) && /^(0|[1-9]\d*)$/.test(String(value.total)) && Number.isSafeInteger(Number(value.total)) && Number(value.total) >= value.records.length && value.records.length <= 20 /** 固定资源只检查现有数据;生命周期缺口保留,不能以查询通过代替写入验收。 */ export async function runFixedResourceReadOnly(context, module) { if (!context.options.fixedCustomerId || !FIXED_RESOURCE_READONLY_MODULES.includes(module)) throw Error('固定资源只读检查必须指定已有客户及受支持模块') context.addCoverageGap(module, 'fixed-resource-lifecycle', '现有资源只读检查不覆盖创建、修改、发布及停用生命周期', '复用已有客户、账号、角色和主档;生命周期仍为未验收', 'not-implemented', { blocking: false }) const proof = { mode: 'READ_ONLY', lifecycle: 'NOT_TESTED', checks: [] } context.createdResources.fixedResourceReadOnly ??= {} context.createdResources.fixedResourceReadOnly[module] = proof async function get(label, path, validate) { const response = await context.request('GET', path) const value = context.apiSuccess(module, label, response) const passed = response?.http === 200 && String(response.body?.code) === '200' && validate(value) context.assert(module, `${label}结构及归属有效`, response, Boolean(passed), '', { deploymentBlocker: true }) proof.checks.push({ path, passed: Boolean(passed), ...(Array.isArray(value) ? { count: value.length } : Array.isArray(value?.records) ? { count: value.records.length, total: Number(value.total) } : {}) }) if (!passed) throw Error(`${module}现有资源只读核验失败,禁止补建或修改`) } const page = (label, path, validate = () => true) => get(label, `${path}?page=1&size=20`, value => pageShape(value) && value.records.every(validate)) switch (module) { case 'customers': { const customerId = normalizeFixedCustomerId(context.options.fixedCustomerId) await get('读取指定固定客户档案', `${IAM}/tenants/${customerId}`, value => String(value?.id) === customerId && value.status === 'ACTIVE') await get('读取指定固定客户尺寸初始化状态', `${IAM}/tenants/${customerId}/dimension-initialization`, value => value?.status === 'READY') break } case 'system': await get('读取既有权限目录', `${IAM}/permissions`, value => Array.isArray(value) && value.every(row => decimalId(row.id) && typeof row.permissionCode === 'string')) await get('读取既有角色目录', `${IAM}/roles`, value => Array.isArray(value) && value.every(row => decimalId(row.id) && typeof row.roleCode === 'string')) await page('读取既有平台账号分页', `${IAM}/users`, row => decimalId(row.membershipId) && String(row.tenantId) === '0') break case 'master-data': await page('读取既有公共仓库分页', '/api/platform/v1/warehouse-admin', row => decimalId(row.id) && String(row.tenantId) === '0') break case 'notifications': await page('读取既有通知模板分页', '/api/platform/v1/notification-templates') await page('读取既有通知任务分页', '/api/platform/v1/notification-tasks') break case 'pricing': await get('读取既有价格簿目录', '/api/platform/v1/fba-pricing/carrier-rates/price-books', Array.isArray) break case 'phase4-overseas-master-data': await page('读取既有海外报价分页', `${MASTER}/overseas-quotes`) await page('读取既有汇率分页', `${MASTER}/exchange-rates`) await page('读取既有海外车辆分页', `${MASTER}/vehicles`) break } }