fix(adapter-wp): phpValueToJson 辅助与 JSON 陷阱警示;错误上下文摘录;PhpError 公共基类
复审修复:新增 phpValueToJson(Map→字符串键普通对象,递归;bigint 分支
冻结为抛 TypeError 不静默转换),PhpValue JSDoc 警示 Map 直接
JSON.stringify 会静默变 {};两类错误 message 附 offset ±20 码元原文
摘录(控制字符转义、120 字符封顶);PhpError 公共抽象基类,两子类
互不 instanceof 的既有断言保持通过。
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
phpUnserialize,
|
||||
phpValueToJson,
|
||||
PhpError,
|
||||
PhpLimitError,
|
||||
PhpParseError,
|
||||
type PhpValue,
|
||||
@@ -171,3 +173,53 @@ describe('phpUnserialize:限额(默认取 DEFAULT_WXR_LIMITS:phpMaxDepth=8
|
||||
expect(phpUnserialize('a:2:{i:0;s:1:"a";i:1;s:1:"b";}', { maxElements: 5 })).toStrictEqual(['a', 'b'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('phpValueToJson:JSON 陷阱辅助(Map 不可直接 JSON.stringify)', () => {
|
||||
it('Map → 字符串键普通对象(并佐证直接 stringify Map 会静默变 {} 的陷阱)', () => {
|
||||
const v = phpUnserialize('a:1:{s:1:"k";i:1;}')
|
||||
expect(JSON.stringify(v)).toBe('{}') // 陷阱:Map 被 JSON.stringify 静默丢弃全部内容
|
||||
expect(JSON.stringify(phpValueToJson(v))).toBe('{"k":1}')
|
||||
})
|
||||
|
||||
it('嵌套 Map-in-array 递归往返', () => {
|
||||
const v = phpUnserialize('a:2:{i:0;a:1:{s:3:"foo";s:3:"bar";}i:1;b:0;}')
|
||||
expect(JSON.stringify(phpValueToJson(v))).toBe('[{"foo":"bar"},false]')
|
||||
})
|
||||
|
||||
it('bigint 分支冻结:抛 TypeError(当前解析器不产出 bigint,拒绝静默转换)', () => {
|
||||
expect(() => phpValueToJson(42n)).toThrow(TypeError)
|
||||
})
|
||||
})
|
||||
|
||||
describe('错误信息上下文摘录(offset ±20 码元窗口、控制字符转义、总长封顶)', () => {
|
||||
it('message 含 offset 附近原文摘录', () => {
|
||||
const e = capture(() => phpUnserialize('i:1;junk')) as PhpParseError
|
||||
expect(e.message).toContain('摘录')
|
||||
expect(e.message).toContain('i:1;junk')
|
||||
})
|
||||
|
||||
it('控制字符转义为可见形式,message 不含裸控制字符', () => {
|
||||
const e = capture(() => phpUnserialize('s:7:"a\nb\nc";')) as PhpParseError
|
||||
expect(e.message).toContain('a\\nb\\nc')
|
||||
expect(e.message).not.toContain('\n')
|
||||
})
|
||||
|
||||
it('长输入窗口截断(… 标记)且摘录总长封顶', () => {
|
||||
const e = capture(() => phpUnserialize(`i:1;${'\u0001'.repeat(60)}`)) as PhpParseError
|
||||
expect(e.message).toContain('…')
|
||||
const excerpt = e.message.match(/「([^」]*)」/)?.[1]
|
||||
expect(excerpt).toBeDefined()
|
||||
expect((excerpt ?? '').length).toBeLessThanOrEqual(123) // 120 字符封顶 + 截断/窗口省略号
|
||||
})
|
||||
})
|
||||
|
||||
describe('错误类型层级:PhpError 公共基类', () => {
|
||||
it('PhpParseError 与 PhpLimitError 均为 PhpError 子类,且互不为对方子类', () => {
|
||||
const parse = capture(() => phpUnserialize('x:1:irrelevant;')) as PhpParseError
|
||||
const limit = capture(() => phpUnserialize('a:1001:{}')) as PhpLimitError
|
||||
expect(parse).toBeInstanceOf(PhpError)
|
||||
expect(limit).toBeInstanceOf(PhpError)
|
||||
expect(parse).not.toBeInstanceOf(PhpLimitError)
|
||||
expect(limit).not.toBeInstanceOf(PhpParseError)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user