import { test } from 'node:test' import assert from 'node:assert/strict' import { createApiFieldCompatChecker } from '../src/shared/api-field-compat.ts' const batchSchema = { taskId: { type: 'number', required: true }, status: { type: 'string', required: true }, progress: { type: 'number' }, message: { type: 'string' }, items: { type: 'array' }, } test('test_task_096_api_compat_normal_default_path', () => { const checker = createApiFieldCompatChecker({ schema: batchSchema }) const violations = checker.check({ taskId: 1, status: 'SUCCESS', progress: 50 }) assert.deepEqual(violations, [], '全部必填字段存在且类型正确') assert.equal(checker.lastResult().violations.length, 0) assert.equal(checker.lastResult().checkedCount, 1) assert.equal(checker.lastResult().passed, true) assert.deepEqual(checker.checkedFields(), ['taskId', 'status', 'progress', 'message', 'items']) }) test('test_task_096_api_compat_normal_multiple_items', () => { const checker = createApiFieldCompatChecker({ schema: batchSchema }) const rows = [ { taskId: 1, status: 'RUNNING' }, { taskId: 2, status: 'SUCCESS', progress: 100 }, { taskId: 3, status: 'FAILED', message: 'err' }, ] for (const row of rows) { assert.deepEqual(checker.check(row), []) } assert.equal(checker.stats().checkedCount, 3) assert.equal(checker.stats().violationCount, 0) // 顺序稳定:检查记录按输入顺序 assert.deepEqual(checker.stats().checkedIds, [1, 2, 3]) }) test('test_task_096_api_compat_normal_repeated_operation_is_idempotent', () => { const checker = createApiFieldCompatChecker({ schema: batchSchema }) const row = { taskId: 1, status: 'SUCCESS' } assert.deepEqual(checker.check(row), []) assert.deepEqual(checker.check(row), [], '同一输入重复检查结果一致') assert.equal(checker.stats().checkedCount, 2) assert.equal(checker.stats().violationCount, 0) // 输入对象不被修改 assert.equal('items' in row, false) // 无 schema 的纯遍历模式 const free = createApiFieldCompatChecker({ schema: {} }) assert.deepEqual(free.check({ anything: 1, other: 'x' }), []) }) test('test_task_096_api_compat_boundary_empty_input', () => { const checker = createApiFieldCompatChecker({ schema: batchSchema }) // 空对象:必填字段缺失 const violations = checker.check({}) assert.equal(violations.length, 2) assert.ok(violations.some((v) => v.field === 'taskId' && v.kind === 'missing')) assert.ok(violations.some((v) => v.field === 'status' && v.kind === 'missing')) // 空数组响应:无字段可查,跳过 const checker2 = createApiFieldCompatChecker({ schema: batchSchema }) checker2.checkBatch([]) assert.equal(checker2.stats().checkedCount, 0) assert.equal(checker2.stats().violationCount, 0) assert.equal(checker2.lastResult().passed, true) }) test('test_task_096_api_compat_boundary_single_item', () => { const checker = createApiFieldCompatChecker({ schema: batchSchema }) const violations = checker.check({ taskId: 7, status: 'SUCCESS' }) assert.deepEqual(violations, [], '单条最小合法记录通过') assert.equal(checker.checkedFields().includes('progress'), true, '可选字段也纳入检查清单') assert.equal(checker.lastResult().checkedCount, 1) }) test('test_task_096_api_compat_boundary_limit_and_overflow', () => { const checker = createApiFieldCompatChecker({ schema: batchSchema, maxChecked: 2 }) checker.check({ taskId: 1, status: 'a' }) checker.check({ taskId: 2, status: 'b' }) checker.check({ taskId: 3, status: 'c' }) assert.equal(checker.stats().checkedCount, 2, '超过 maxChecked 后不再检查') assert.equal(checker.stats().skippedCount, 1) assert.deepEqual(checker.stats().checkedIds, [1, 2]) // 类型错误累计 const checker2 = createApiFieldCompatChecker({ schema: batchSchema }) checker2.check({ taskId: 'x' as never, status: 5 as never }) assert.equal(checker2.stats().violationCount, 2) }) test('test_task_096_api_compat_invalid_input_rejected', () => { assert.throws(() => createApiFieldCompatChecker({} as never), /schema 必须是对象/) assert.throws(() => createApiFieldCompatChecker({ schema: null as never }), /schema 必须是对象/) assert.throws(() => createApiFieldCompatChecker({ schema: [] as never }), /schema 必须是对象/) assert.throws( () => createApiFieldCompatChecker({ schema: { a: { type: 'unknown' } } }), /不支持的字段类型: unknown/, ) assert.throws( () => createApiFieldCompatChecker({ schema: batchSchema, maxChecked: 0 }), /maxChecked 必须为正数/, ) const checker = createApiFieldCompatChecker({ schema: batchSchema }) assert.throws(() => checker.check(null as never), /对象必须是普通对象/) assert.throws(() => checker.check([1, 2] as never), /对象必须是普通对象/) assert.throws(() => checker.checkBatch('x' as never), /对象数组必须是数组/) }) test('test_task_096_api_compat_dependency_failure_releases_resources', () => { // 检查对象读取失败:check 抛错但状态不被污染,恢复后可用 let broken = false const checker = createApiFieldCompatChecker({ schema: batchSchema }) const poisoned = new Proxy({ taskId: 1, status: 'SUCCESS' }, { get(target, prop, receiver) { if (broken && prop === 'taskId') throw new Error('row getter down') return Reflect.get(target, prop, receiver) }, }) broken = true assert.throws(() => checker.check(poisoned), /row getter down/) assert.equal(checker.stats().checkedCount, 0, '失败不产生检查记录') broken = false assert.deepEqual(checker.check(poisoned), [], '依赖恢复后同一检查器继续可用') assert.equal(checker.stats().checkedCount, 1) })