import test from 'node:test' import assert from 'node:assert/strict' import { AcceptanceRun } from '../admin-test-env-acceptance.mjs' import { FIXED_RESOURCE_READONLY_MODULES } from './fixed-resource-readonly.mjs' function runner(mutate = value => value) { const context = new AcceptanceRun({ environment: 'test', fixedCustomerId: '123', quiet: true }) const calls = [] context.request = async (method, path) => { calls.push({ method, path }) assert.equal(method, 'GET', '固定资源只读检查不得写入或创建资源') let value = path.includes('?page=') ? { page: 1, size: 20, total: 0, records: [] } : [] if (path.endsWith('/tenants/123')) value = { id: '123', status: 'ACTIVE' } if (path.endsWith('/dimension-initialization')) value = { status: 'READY' } if (path.endsWith('/permissions')) value = [{ id: '1', permissionCode: 'platform:customer:list' }] if (path.endsWith('/roles')) value = [{ id: '2', roleCode: 'EXISTING_ROLE' }] if (path.includes('/iam/users?')) value.records = [{ membershipId: '3', tenantId: '0' }], value.total = 1 if (path.includes('/warehouse-admin?')) value.records = [{ id: '4', tenantId: '0' }], value.total = 1 return { method, path, http: 200, body: { code: '200', data: mutate(value, path) } } } return { context, calls } } for (const module of FIXED_RESOURCE_READONLY_MODULES) test(`${module}复用现有数据,只读通过且保留生命周期缺口`, async () => { const { context, calls } = runner() await context.executeModule(module, () => assert.fail('不得调用原生命周期处理器')) const result = context.modules[0] assert.equal(result.success, true) assert.ok(result.passed > 0) assert.equal(result.skipped, undefined) assert.equal(result.coverageMode, 'READ_ONLY') assert.equal(result.lifecycleSkipped, true) assert.ok(calls.length > 0) assert.ok(calls.every(row => row.method === 'GET')) assert.equal(context.coverageGaps.length, 1) assert.equal(context.coverageGaps[0].blocking, false) assert.equal(context.createdResources.fixedResourceReadOnly[module].lifecycle, 'NOT_TESTED') }) for (const [label, module, mutate] of [ ['客户不匹配', 'customers', (value, path) => path.endsWith('/123') ? { ...value, id: '999' } : value], ['客户被停用', 'customers', (value, path) => path.endsWith('/123') ? { ...value, status: 'DISABLED' } : value], ['初始化未就绪', 'customers', (value, path) => path.endsWith('/dimension-initialization') ? { status: 'PENDING' } : value], ['仓库非公共归属', 'master-data', value => ({ ...value, records: [{ id: '4', tenantId: '999' }], total: 1 })], ['平台账号非平台归属', 'system', (value, path) => path.includes('/users?') ? { ...value, records: [{ membershipId: '3', tenantId: '123' }] } : value], ['分页结构缺失', 'notifications', () => []], ['分页范围错误', 'notifications', value => ({ ...value, page: 2 })], ['价格目录结构错误', 'pricing', () => ({ records: [] })], ]) test(`${label}必须失败,禁止补建或执行原写入处理器`, async () => { const { context, calls } = runner(mutate) await context.executeModule(module, () => assert.fail('不能回退创建数据')) assert.equal(context.modules[0].success, false) assert.ok(context.modules[0].failed > 0) assert.ok(calls.every(row => row.method === 'GET')) }) test('权限错误保留为真实失败,不以空目录通过', async () => { const { context } = runner() context.request = async () => ({ http: 403, body: { code: 'A0403' } }) await context.executeModule('system', () => assert.fail('不能修改授权')) assert.equal(context.modules[0].success, false) })