test(06-14): strengthen stream assertions with types, index progression and expiry (G-06-04-C/B)

- rewrite expectHeterogeneousRead to assert per-param typeof + value, stream index i+1, terminal null and stream.index === length
- add per-param typeof/value and stream.index progression to the sequence and expand stream cases
- add 'expires a read stream after the heterogeneous route is mutated': add() expires the old stream (warn 155) and a fresh stream reads the new 7-step order
This commit is contained in:
unanmed 2026-09-15 11:47:25 +08:00
parent 8ae4588ba4
commit 2cffe3ef6c

View File

@ -136,7 +136,7 @@ function createComplexArray(): ReplayArray {
return array; return array;
} }
// 逐条比较读流与按索引读回:两者的 index 语义不同(读流为位置 + 1、get 从 0 起),故只比较指令与参数 // 逐条比较读流与按索引读回:读流索引为位置 + 1、get 从 0 起,故只按指令与参数对应并对每参数断言类型
function expectHeterogeneousRead( function expectHeterogeneousRead(
array: ReplayArray, array: ReplayArray,
expectedSteps: ReadonlyArray<readonly [number, ReplayParamValue[]]> expectedSteps: ReadonlyArray<readonly [number, ReplayParamValue[]]>
@ -144,9 +144,13 @@ function expectHeterogeneousRead(
const stream = array.createReadStream(0); const stream = array.createReadStream(0);
expect(stream.length).toBe(array.length); expect(stream.length).toBe(array.length);
expectedSteps.forEach(([command, params], i) => { expectedSteps.forEach(([command, params], i) => {
expect(stream.read()).toMatchObject({ command, params }); expectStepTyped(stream.read()!, command, params, i + 1);
expect(array.get(i)).toMatchObject({ command, params }); const step = array.get(i);
expect(step.command).toBe(command);
params.forEach((param, j) => expectParamTyped(step.params[j], param));
expect(step.index).toBe(i);
}); });
expect(stream.index).toBe(expectedSteps.length);
expect(stream.read()).toBeNull(); expect(stream.read()).toBeNull();
} }
@ -373,7 +377,7 @@ describe('ReplayArray param codec', () => {
}); });
describe('ReplayArray stream and buffer combination', () => { describe('ReplayArray stream and buffer combination', () => {
// 验证 createReadStream 从起始索引顺序读回多步并在末尾返回 null // 验证 createReadStream 从起始索引顺序读回多步,每参数为 number 且流索引逐次递进,末尾返回 null
it('reads a sequence of steps through a read stream', () => { it('reads a sequence of steps through a read stream', () => {
const array = createArray(); const array = createArray();
array.add(1, [10]); array.add(1, [10]);
@ -383,9 +387,17 @@ describe('ReplayArray stream and buffer combination', () => {
const stream = array.createReadStream(0); const stream = array.createReadStream(0);
expect(stream.index).toBe(0); expect(stream.index).toBe(0);
expect(stream.length).toBe(3); expect(stream.length).toBe(3);
expect(stream.read()).toEqual({ command: 1, params: [10], index: 1 }); for (let i = 0; i < 3; i++) {
expect(stream.read()).toEqual({ command: 2, params: [20], index: 2 }); const step = stream.read()!;
expect(stream.read()).toEqual({ command: 3, params: [30], index: 3 }); expect(step).toEqual({
command: i + 1,
params: [(i + 1) * 10],
index: i + 1
});
expect(typeof step.params[0]).toBe('number');
expect(step.params[0]).toBe((i + 1) * 10);
expect(stream.index).toBe(i + 1);
}
expect(stream.read()).toBeNull(); expect(stream.read()).toBeNull();
}); });
@ -614,7 +626,31 @@ describe('ReplayArray expand and width warnings', () => {
expect(info.map(v => v.code)).toContain(155); expect(info.map(v => v.code)).toContain(155);
}); });
// 验证初始容量不足时自动扩容且扩容后仍能顺序读回每一步 // 验证复杂序列 add 变更后既有读流过期并告警 155,新建读流按新次序逐条类型化读回 7 步
it('expires a read stream after the heterogeneous route is mutated', () => {
const array = createHeterogeneousArray();
const stream = array.createReadStream(0);
expectStepTyped(stream.read()!, 1, [10], 1);
expectStepTyped(stream.read()!, 2, [true, 'x'], 2);
array.add(7, ['z']);
expect(stream.expired).toBe(true);
const { info } = logger.catch(() => stream.read());
expect(info.map(v => v.code)).toContain(155);
const expectedSteps: ReadonlyArray<
readonly [number, ReplayParamValue[]]
> = [...heterogeneousSteps, [7, ['z']]];
const fresh = array.createReadStream(0);
expectedSteps.forEach(([command, params], i) => {
expectStepTyped(fresh.read()!, command, params, i + 1);
});
expect(fresh.read()).toBeNull();
expect(fresh.index).toBe(7);
});
// 验证初始容量不足时自动扩容,扩容后仍能顺序读回每一步且流索引逐次递进
it('expands buffers and still reads every step back', () => { it('expands buffers and still reads every step back', () => {
const array = createArray({ const array = createArray({
initCommandLength: 2, initCommandLength: 2,
@ -627,11 +663,15 @@ describe('ReplayArray expand and width warnings', () => {
expect(array.length).toBe(15); expect(array.length).toBe(15);
const stream = array.createReadStream(0); const stream = array.createReadStream(0);
for (let i = 0; i < 15; i++) { for (let i = 0; i < 15; i++) {
expect(stream.read()).toEqual({ const step = stream.read()!;
expect(step).toEqual({
command: i, command: i,
params: [i], params: [i],
index: i + 1 index: i + 1
}); });
expect(typeof step.params[0]).toBe('number');
expect(step.params[0]).toBe(i);
expect(stream.index).toBe(i + 1);
} }
expect(stream.read()).toBeNull(); expect(stream.read()).toBeNull();
}); });