feat(adapter-wp): WXR→UniversalRow 映射器(status 值域/悬空保留/unsupported 分流)
- mapItem 纯函数:WxrItem → MappedResult 三分支(row/unsupported/error), DEFAULT_TYPE_ROUTES/STATUS_MAP/META_KEYS 三冻结表导出 - Plan 1 移交落实:status 值域表驱动(未知→unknown+issue);property 断言 (isUniversalRow+refs 形状非空)测试钉死 - Task 3 审查移交:postId null→error(不产 row 不猜);-7 原样;-0 归一 '0' - taxonomyRefs 锚点=category @nicename(=term slug,Task 8 实数据核对后冻结) - 悬空 gallery 203/204 原样保留;wppop 解析失败→extras.phpRaw+issue(不崩不丢) - Task 2 审查移交补测四例:d: 键拒绝/NUL 往返/s:1:"0"→Map/1MB<50ms 烟雾 - tsconfig.build.json:src 首次引 @sf/contracts 后 build 经 contracts dist 声明解析(rootDir TS6059;typecheck/vitest/tsx 仍 src 直连,ADR-016 归口) - 142→173 测试全绿;typecheck/lint 绿 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -213,6 +213,40 @@ describe('错误信息上下文摘录(offset ±20 码元窗口、控制字符
|
||||
})
|
||||
})
|
||||
|
||||
// Task 2 审查移交补测(Plan 2 Task 4 落实):四例边界/形状/性能钉死
|
||||
describe('Task 2 审查移交补测:数组键类型、NUL、数字串键形状、1MB 性能烟雾', () => {
|
||||
it('d: 作数组键 → 拒绝(数组键只认 i:/s:,浮点键是 PHP 序列化产物中不存在的形状)', () => {
|
||||
const e = capture(() => phpUnserialize('a:1:{d:1.5;i:0;}')) as PhpParseError
|
||||
expect(e).toBeInstanceOf(PhpParseError)
|
||||
expect(e.message).toContain('数组键')
|
||||
})
|
||||
|
||||
it('s: 内容含 NUL(\\u0000)原样往返,不截断不报错(单指针扫描无 C 式终止符语义)', () => {
|
||||
expect(phpUnserialize('s:3:"a\0b";')).toBe('a\0b')
|
||||
// NUL 作键亦不炸:Map 保留该键
|
||||
expect(phpUnserialize('a:1:{s:3:"a\0b";i:1;}')).toStrictEqual(new Map([['a\0b', 1]]))
|
||||
})
|
||||
|
||||
it('数字串键 s:1:"0" → Map(密集数组判定用严格 ===,字符串键永不伪装整数键 0)', () => {
|
||||
expect(phpUnserialize('a:1:{s:1:"0";i:5;}')).toStrictEqual(new Map([['0', 5]]))
|
||||
expect(JSON.stringify(phpValueToJson(phpUnserialize('a:1:{s:1:"0";i:5;}')))).toBe('{"0":5}')
|
||||
})
|
||||
|
||||
it('1MB 输入性能烟雾:<50ms(防 O(n²) 回归——单指针扫描应线性)', () => {
|
||||
const parts: string[] = []
|
||||
for (let i = 0; i < 400; i++) parts.push(`i:${i};s:2500:"${'x'.repeat(2500)}";`)
|
||||
const input = `a:400:{${parts.join('')}}`
|
||||
expect(input.length).toBeGreaterThan(1_000_000) // 确证规模 ≥1MB(纯 ASCII:码元数=字节数)
|
||||
// 元素数 = 400 键 + 400 值 + 1 数组自身 = 801 ≤ 默认 phpMaxElements=1000(不经限额捷径)
|
||||
const t0 = performance.now()
|
||||
const value = phpUnserialize(input)
|
||||
const elapsed = performance.now() - t0
|
||||
expect(Array.isArray(value)).toBe(true)
|
||||
expect(value).toHaveLength(400)
|
||||
expect(elapsed).toBeLessThan(50)
|
||||
})
|
||||
})
|
||||
|
||||
describe('错误类型层级:PhpError 公共基类', () => {
|
||||
it('PhpParseError 与 PhpLimitError 均为 PhpError 子类,且互不为对方子类', () => {
|
||||
const parse = capture(() => phpUnserialize('x:1:irrelevant;')) as PhpParseError
|
||||
|
||||
Reference in New Issue
Block a user