From b932f8cecd64e9b7a465203df321a8bc4da4c423 Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Thu, 8 Oct 2026 14:35:03 +0800 Subject: [PATCH] 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 --- .../data-base/src/hero/__test__/items.test.ts | 21 +++++++ .../src/map/__test__/mapLifecycle.test.ts | 5 +- .../data-base/src/map/__test__/mover.test.ts | 21 ++++++- .../src/replay/__test__/sandbox.test.ts | 60 ++++++++++++------- .../src/store/__test__/tileStore.test.ts | 48 ++++++++++++++- 5 files changed, 127 insertions(+), 28 deletions(-) diff --git a/packages-user/data-base/src/hero/__test__/items.test.ts b/packages-user/data-base/src/hero/__test__/items.test.ts index 9608450..4cb8d80 100644 --- a/packages-user/data-base/src/hero/__test__/items.test.ts +++ b/packages-user/data-base/src/hero/__test__/items.test.ts @@ -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', () => { diff --git a/packages-user/data-base/src/map/__test__/mapLifecycle.test.ts b/packages-user/data-base/src/map/__test__/mapLifecycle.test.ts index b650700..f4a9b0f 100644 --- a/packages-user/data-base/src/map/__test__/mapLifecycle.test.ts +++ b/packages-user/data-base/src/map/__test__/mapLifecycle.test.ts @@ -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( diff --git a/packages-user/data-base/src/map/__test__/mover.test.ts b/packages-user/data-base/src/map/__test__/mover.test.ts index fc53d08..91286a0 100644 --- a/packages-user/data-base/src/map/__test__/mover.test.ts +++ b/packages-user/data-base/src/map/__test__/mover.test.ts @@ -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(); diff --git a/packages-user/data-common/src/replay/__test__/sandbox.test.ts b/packages-user/data-common/src/replay/__test__/sandbox.test.ts index 3f52598..27f53c7 100644 --- a/packages-user/data-common/src/replay/__test__/sandbox.test.ts +++ b/packages-user/data-common/src/replay/__test__/sandbox.test.ts @@ -23,11 +23,6 @@ afterAll(() => { vi.unstubAllGlobals(); }); -interface IManualReplaySandbox extends IReplaySandbox { - playing: boolean; - pausing: boolean; -} - // 构造一个可注入执行与收尾行为的主动录像命令 function createCommand( execute: (step: IReplayStepHandler) => Promise, @@ -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', () => { diff --git a/packages-user/data-common/src/store/__test__/tileStore.test.ts b/packages-user/data-common/src/store/__test__/tileStore.test.ts index 4785a93..3eefd91 100644 --- a/packages-user/data-common/src/store/__test__/tileStore.test.ts +++ b/packages-user/data-common/src/store/__test__/tileStore.test.ts @@ -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(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(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); + }); });