test(08-10): cover data-common/data-base gap codes and align baseline failures

- sandbox: drop obsolete playing=true bypass, align expired-step case, assert codes 73/194

- tileStore: assert codes 133/134 and add ItemStore/EnemyStore code 181 cases

- map/mover: assert code 192 when Dir8 handler missing

- hero/items: assert code 193 on missing item id registration

- mapLifecycle: drive resize via GameMap.resizeLayer so shared indexer width syncs
This commit is contained in:
unanmed 2026-10-08 14:35:03 +08:00
parent 339f72d541
commit b932f8cecd
5 changed files with 127 additions and 28 deletions

View File

@ -6,9 +6,11 @@ import {
type IItemRawData,
ItemCategory,
ItemStore,
SaveCompression,
TileStore,
TileType
} from '@user/data-common';
import { logger } from '@motajs/common';
import { HeroItems } from '../items';
vi.hoisted(() => {
@ -182,6 +184,25 @@ describe('HeroItems counting', () => {
expect(env.items.itemCount(99)).toBe(0);
expect(env.items.itemCount(77)).toBe(0);
});
// 验证读档时道具无图块 id 注册告警码 193 并跳过该道具
it('warns code 193 when a saved item has no tile id registration', () => {
const env = createEnv();
const warn = vi.spyOn(logger, 'warn');
// 仅向道具存储注册定义,不向图块存储注册 id
env.itemStore.addItem(
createItem(50, 'orphan', ItemCategory.Constant).item
);
const save = env.items.saveState(SaveCompression.NoCompression);
env.items.loadState(
{ items: [{ num: 50, count: 1 }], equipStore: save.equipStore },
SaveCompression.NoCompression
);
expect(warn).toHaveBeenCalledWith(193, '50');
expect(env.items.itemCount(50)).toBe(0);
});
});
describe('HeroItems useItem', () => {

View File

@ -290,12 +290,13 @@ describe('MapLayer point event lifecycle', () => {
// 验证改变图层宽度后,保留的点事件会使用新索引继续查询
it('reindexes preserved point events when the layer width changes', () => {
const { layer } = createMapState({
const { map, layer } = createMapState({
1: { 1: 'top-right' },
3: { 1: 'bottom-right' }
});
layer.resize(3, 2);
// 经 GameMap.resizeLayer 变更尺寸,使共享索引器宽度与图层同步后重建点事件索引
map.resizeLayer(3, 2, true);
expect(layer.getPointEvent(1, 0)).toEqual(new Map([[1, 'top-right']]));
expect(layer.getPointEvent(1, 1)).toEqual(

View File

@ -72,13 +72,15 @@ interface MoverFixture {
tile: DynamicTile;
}
/** 构造一个绑定动态图块的移动器宿主 */
function createMoverFixture(): MoverFixture {
/** 构造一个绑定动态图块的移动器宿主,可选择性跳过 Dir8 处理器注册 */
function createMoverFixture(registerDir8: boolean = true): MoverFixture {
const tileStore = new TileStore();
tileStore.addTile(createTileData(1, 'base'));
tileStore.addTile(createTileData(2, 'alternate'));
const faceManager = new FaceManager();
faceManager.register(FaceGroup.Dir8, new Dir8FaceHandler());
if (registerDir8) {
faceManager.register(FaceGroup.Dir8, new Dir8FaceHandler());
}
const state: IDataCommon = {
tileStore,
itemStore: {},
@ -159,6 +161,19 @@ describe('DynamicTileMover movement', () => {
expect(tile.y).toBe(0);
});
// 验证缺失 Dir8 朝向处理器时移动结束告警码 192 且图块停在原地
it('warns code 192 when the Dir8 face handler is missing', async () => {
const { tile } = createMoverFixture(false);
const warn = vi.spyOn(logger, 'warn');
tile.mover.step(FaceDirection.Right);
await tile.mover.start()!.onEnd;
expect(warn).toHaveBeenCalledWith(192);
expect(tile.x).toBe(0);
expect(tile.y).toBe(0);
});
// 验证移动开始、单步与结束钩子按生命周期顺序触发
it('fires the movement lifecycle hooks in order', async () => {
const { tile } = createMoverFixture();

View File

@ -23,11 +23,6 @@ afterAll(() => {
vi.unstubAllGlobals();
});
interface IManualReplaySandbox extends IReplaySandbox {
playing: boolean;
pausing: boolean;
}
// 构造一个可注入执行与收尾行为的主动录像命令
function createCommand(
execute: (step: IReplayStepHandler) => Promise<ReplayCommandResult>,
@ -38,18 +33,12 @@ function createCommand(
: { type: ReplayCommandType.Active, execute };
}
// 用录像系统构造一个可手动驱动的录像沙箱
function createSandbox(system: ReplaySystem): IManualReplaySandbox {
// 用录像系统构造一个可单步驱动的录像沙箱
function createSandbox(system: ReplaySystem): IReplaySandbox {
return system.createReplaySandbox({
route: system.array,
reseter: { reset: () => {} }
}) as IManualReplaySandbox;
}
// 将沙箱置为手动步进的播放状态
function start(sandbox: IManualReplaySandbox): void {
sandbox.playing = true;
sandbox.pausing = false;
});
}
// 有界等待播放结束,避免不终止的播放循环挂起测试
@ -84,7 +73,6 @@ describe('ReplaySandbox stepping', () => {
stepped.push(step);
}
});
start(sandbox);
await expect(sandbox.step()).resolves.toBe(true);
@ -116,7 +104,6 @@ describe('ReplaySandbox stepping', () => {
system.record(1);
system.record(2);
const sandbox = createSandbox(system);
start(sandbox);
await expect(sandbox.step()).resolves.toBe(false);
@ -131,7 +118,6 @@ describe('ReplaySandbox stepping', () => {
const system = new ReplaySystem();
system.record(9);
const sandbox = createSandbox(system);
start(sandbox);
await expect(sandbox.step()).resolves.toBe(false);
@ -165,7 +151,6 @@ describe('ReplaySandbox stepping', () => {
system.record(1);
system.record(2);
const sandbox = createSandbox(system);
start(sandbox);
await expect(sandbox.step()).resolves.toBe(true);
await expect(sandbox.step()).resolves.toBe(true);
@ -191,7 +176,6 @@ describe('ReplaySandbox stepping', () => {
system.record(1);
system.record(2);
const sandbox = createSandbox(system);
start(sandbox);
await expect(sandbox.step()).resolves.toBe(true);
await expect(sandbox.step()).resolves.toBe(false);
@ -209,7 +193,8 @@ describe('ReplaySandbox stepping', () => {
);
system.record(1);
const sandbox = createSandbox(system);
start(sandbox);
// 先自然步进一次使沙箱进入播放态,再使读取流过期
await expect(sandbox.step()).resolves.toBe(true);
system.array.add(1, []);
await expect(sandbox.step()).resolves.toBe(false);
@ -235,13 +220,44 @@ describe('ReplaySandbox stepping', () => {
route: system.array,
reseter: { reset: () => {} },
startIndex: 2
}) as IManualReplaySandbox;
start(sandbox);
});
expect(sandbox.getReplayed()).toBe(2);
await expect(sandbox.step()).resolves.toBe(true);
expect(executed).toEqual([3]);
});
// 验证无待播被动步时调用 getPassive 触发错误码 73 并返回 null
it('errors code 73 when getPassive is called with no pending passive step', () => {
const error = vi.spyOn(logger, 'error').mockImplementation(() => {});
const system = new ReplaySystem();
const sandbox = createSandbox(system);
expect(sandbox.getPassive()).toBeNull();
expect(error).toHaveBeenCalledWith(73);
});
// 验证主动步之后出现被动步时告警码 194 并继续忽略该被动步
it('warns code 194 when a passive step follows an active step', async () => {
const warn = vi.spyOn(logger, 'warn').mockImplementation(() => {});
const system = new ReplaySystem();
system.registerCommand(
1,
createCommand(async () => ReplayCommandResult.Success)
);
system.registerCommand(2, {
type: ReplayCommandType.Passive,
execute: async () => ReplayCommandResult.Success
});
system.record(1);
system.record(2);
const sandbox = createSandbox(system);
await expect(sandbox.step()).resolves.toBe(true);
await expect(sandbox.step()).resolves.toBe(true);
expect(warn).toHaveBeenCalledWith(194, '2', '1');
});
});
describe('ReplaySandbox playback control', () => {

View File

@ -1,5 +1,8 @@
import { describe, expect, it } from 'vitest';
import { describe, expect, it, vi } from 'vitest';
import { logger } from '@motajs/common';
import { TileStore } from '../tileStore';
import { ItemStore } from '../itemStore';
import { EnemyStore } from '../enemyStore';
import { ITileRawData, TileType } from '../types';
function createTile(
@ -85,4 +88,47 @@ describe('TileStore events-map contract', () => {
expect(store.getEvent(3)).toEqual(new Map([[4, 'original']]));
expect(store.getEvent(3)).not.toBe(events);
});
// 验证按数字或 id 重复注册时分别告警码 133 与 134
it('warns code 133 and 134 on number or id conflicts', () => {
const store = new TileStore();
const warn = vi.spyOn(logger, 'warn');
store.addTile(createTile(1, 'first', { 1: 'first-event' }));
store.addTile(createTile(1, 'second', { 2: 'second-event' }));
store.addTile(createTile(2, 'second', { 3: 'moved-event' }));
expect(warn).toHaveBeenCalledWith(133, '1', 'first');
expect(warn).toHaveBeenCalledWith(134, 'second', '1');
});
});
describe('ItemStore and EnemyStore duplicate registration', () => {
// 验证道具存储重复注册同一图块数字时告警码 181 并由后写入覆盖
it('warns code 181 when an item number is registered twice', () => {
const store = new ItemStore<never>(new TileStore());
const warn = vi.spyOn(logger, 'warn');
const first = { num: 5 } as never;
const second = { num: 5 } as never;
store.addItem(first);
store.addItem(second);
expect(warn).toHaveBeenCalledWith(181, '5', 'item');
expect(store.getData(5)).toBe(second);
});
// 验证怪物存储重复注册同一图块数字时告警码 181 并由后写入覆盖
it('warns code 181 when an enemy number is registered twice', () => {
const store = new EnemyStore<never>(new TileStore());
const warn = vi.spyOn(logger, 'warn');
const first = { num: 7, attribute: {}, special: {} } as never;
const second = { num: 7, attribute: {}, special: {} } as never;
store.addEnemy(first);
store.addEnemy(second);
expect(warn).toHaveBeenCalledWith(181, '7', 'enemy');
expect(store.getEnemy(7)).toBe(second);
});
});