fix(replay): enable recording guard; update tests for replay refactor

This commit is contained in:
unanmed 2026-09-14 20:14:01 +08:00
parent 17d7c8f46a
commit c08f3f8484
7 changed files with 58 additions and 70 deletions

View File

@ -103,8 +103,12 @@ function createItem(
function createEnv(): TestEnv {
const tileStore = new TileStore();
const itemStore = new ItemStore<IHeroAttr, unknown>();
// 录像系统桩,仅用于满足装备/卸下时的 route.add 记录
const replaySystem = { route: { add: vi.fn() } };
// 录像系统桩,用于满足装备/卸下时的 route.add 记录与临时禁用录像
const replaySystem = {
route: { add: vi.fn() },
disable: vi.fn(),
revert: vi.fn()
};
const state = { tileStore, itemStore, replaySystem } as never;
const attribute = new HeroAttribute<IHeroAttr>(createBaseAttr());
const store = new HeroEquipsStore<IHeroAttr>(state);

View File

@ -118,16 +118,16 @@ describe('HeroMover configuration', () => {
const mover = createMover();
expect(mover.getConfig()).toEqual({
noRoute: false,
ignoreTerrain: false,
autoSave: false,
allowOutBound: false
});
expect(mover.config({ noRoute: true, autoSave: true })).toBe(mover);
expect(mover.config({ ignoreTerrain: true, autoSave: true })).toBe(
mover
);
expect(mover.getConfig()).toEqual({
noRoute: true,
ignoreTerrain: false,
ignoreTerrain: true,
autoSave: true,
allowOutBound: false
});

View File

@ -76,7 +76,11 @@ function createState(): IDataCommon {
const tileStore = new TileStore();
const itemStore = new ItemStore<IHeroAttr, unknown>();
const route = { add: vi.fn() };
return { tileStore, itemStore, replaySystem: { route } } as never;
return {
tileStore,
itemStore,
replaySystem: { route, disable: vi.fn(), revert: vi.fn() }
} as never;
}
/** 构造一份合成的勇士基础属性 */

View File

@ -474,3 +474,20 @@ describe('ReplayArray expand and width warnings', () => {
expect(stream.read()).toBeNull();
});
});
describe('ReplayArray disable and revert', () => {
// 验证禁用后录像操作被忽略,恢复后录像继续记录
it('ignores recording while disabled and resumes after revert', () => {
const array = createArray();
array.add(1, [10]);
array.disable();
array.add(2, [20]);
expect(array.length).toBe(1);
array.revert();
array.add(3, [30]);
expect(array.length).toBe(2);
expect(array.get(1)).toEqual({ command: 3, params: [30], index: 1 });
});
});

View File

@ -390,7 +390,7 @@ export class ReplayArray implements IReplayArray {
}
add(command: number, params: ReplayParamValue[]): void {
if (this.disabled >= 0) return;
if (this.disabled > 0) return;
const normalized = this.normalizeParamList(params);
const length = this.calculateParamsLength(normalized);
this.checkBufferExpand(length);
@ -410,7 +410,7 @@ export class ReplayArray implements IReplayArray {
}
insert(index: number, command: number, params: ReplayParamValue[]): void {
if (this.disabled >= 0) return;
if (this.disabled > 0) return;
const normalized = this.normalizeParamList(params);
const length = this.calculateParamsLength(normalized);
this.checkBufferExpand(length);
@ -440,7 +440,7 @@ export class ReplayArray implements IReplayArray {
}
delete(index: number): void {
if (this.disabled >= 0) return;
if (this.disabled > 0) return;
const commandSize = this.getCommandSize();
const commandStart = index * commandSize;
const paramStart = this.indexArray[index];
@ -474,7 +474,7 @@ export class ReplayArray implements IReplayArray {
}
set(index: number, command: number, params: ReplayParamValue[]): void {
if (this.disabled >= 0) return;
if (this.disabled > 0) return;
const normalized = this.normalizeParamList(params);
const length = this.calculateParamsLength(normalized);
const paramStart = this.indexArray[index];

View File

@ -1,8 +1,6 @@
// 测试录像存读档:ReplayArray 与 ReplaySystem 同实例往返与多类型参数读回
// 测试录像存读档:ReplaySystem 同实例往返与多类型参数读回
import { afterAll, describe, expect, it, vi } from 'vitest';
import { ReplayArray } from './array';
import { ReplaySystem } from './system';
import { ReplayCommandWidth } from './types';
vi.hoisted(() => {
vi.stubGlobal('main', { replayChecking: true });
@ -24,67 +22,11 @@ afterAll(() => {
vi.unstubAllGlobals();
});
interface IArrayOverrides {
initCommandLength?: number;
initParamLength?: number;
commandExpandMultiplier?: number;
paramExpandMultiplier?: number;
commandWidth?: ReplayCommandWidth;
commandMaxLength?: number;
paramMaxLength?: number;
}
/** 与 system.ts 初始化保持一致的构造入口,默认给足初始容量避免无关扩容 */
function createArray(overrides: IArrayOverrides = {}): ReplayArray {
return new ReplayArray({
initCommandLength: 32,
initParamLength: 128,
commandExpandMultiplier: 2,
paramExpandMultiplier: 2,
commandWidth: ReplayCommandWidth.Uint8,
commandMaxLength: 64,
paramMaxLength: 512,
...overrides
});
}
/** 构造一个独立的录像系统实例 */
function createSystem(): ReplaySystem {
return new ReplaySystem();
}
describe('ReplayArray save and load round trips', () => {
// 验证指令位宽与首步命令及多类型参数在同实例上读回一致
it('restores the command width and reads back the first step on the same instance', () => {
const array = createArray();
array.add(3, [true, -128, 32768, 'hello', 5]);
const saved = array.saveState();
array.add(9, [1]);
array.loadState(saved);
expect(array.commandWidth).toBe(saved.metadata.commandWidth);
expect(array.get(0)).toEqual({
command: 3,
params: [true, -128, 32768, 'hello', 5],
index: 0
});
});
// 疑似 bug:loadState 未恢复录像长度与索引数组,详见 06-TEST-FINDINGS.md #06-09-4
it.skip('restores the recorded length on the same instance', () => {
const array = createArray();
array.add(1, [10]);
const saved = array.saveState();
array.add(2, [20]);
array.loadState(saved);
expect(array.length).toBe(1);
expect(array.get(0)).toEqual({ command: 1, params: [10], index: 0 });
});
});
describe('ReplaySystem save and load round trips', () => {
// 验证已注册指令与录像步在同实例上恢复到存档点
it('restores the route and registered commands on the same instance', () => {

View File

@ -158,3 +158,24 @@ describe('ReplaySystem sandbox lifecycle', () => {
expect(system.sandbox).toBeNull();
});
});
describe('ReplaySystem disable and revert', () => {
// 验证禁用后 record 被忽略,恢复后继续记录并委托给 route
it('delegates disable and revert to the route', () => {
const system = new ReplaySystem();
const disable = vi.spyOn(system.route, 'disable');
const revert = vi.spyOn(system.route, 'revert');
system.record(1);
system.disable();
system.record(2);
expect(disable).toHaveBeenCalledTimes(1);
expect(system.route.length).toBe(1);
system.revert();
system.record(3);
expect(revert).toHaveBeenCalledTimes(1);
expect(system.route.length).toBe(2);
expect(system.route.get(1).command).toBe(3);
});
});