test(06): fix lint and type errors in data-layer test files

This commit is contained in:
AncTe committed 2026-09-14 14:03:25 +08:00
1 parent 693b337e24
commit 136a9845d6
8 files changed
+284 -189

No files matched your search

@@ -139,9 +139,15 @@ class FakeScript implements ICombatScript<TestEnemyAttr, TestHeroAttr> {
}
}
type TestCombatFlow = import('./combat').CombatFlow<
TestEnemyAttr,
TestHeroAttr
>;
type TestHeroAttribute = import('@user/data-base').HeroAttribute<TestHeroAttr>;
interface CombatFixture {
/** 被测战斗流程对象 */
flow: InstanceType<TestModules['CombatFlow']>;
flow: TestCombatFlow;
/** 数据层状态假对象 */
state: IStateBase;
/** 怪物上下文假对象 */
@@ -149,7 +155,7 @@ interface CombatFixture {
/** 伤害上下文假对象 */
damage: IDamageContext<TestEnemyAttr, TestHeroAttr>;
/** 可修改勇士属性 */
hero: InstanceType<TestModules['HeroAttribute']>;
hero: TestHeroAttribute;
/** 怪物视图假对象 */
view: IEnemyView<TestEnemyAttr>;
/** 计算后怪物 */
@@ -4,19 +4,20 @@ import { type IRange } from '@motajs/common';
import {
type IEnemy,
type IReadonlyEnemy,
type IReadonlyHeroAttribute,
type IStateBase,
type ISpecial
} from '@user/data-base';
import {
type IAuraConverter,
type IAuraView,
type IEnemyAuraView,
type IEnemyCommonQueryEffect,
type IEnemyFinalEffect,
type IEnemyHandler,
type IEnemySpecialModifier,
type IEnemySpecialQueryEffect,
type IEnemyView,
type IReadonlyHeroAttribute
type IEnemyView
} from './types';
vi.hoisted(() => {
@@ -127,11 +128,17 @@ function createEnemy(
});
}
type TestEnemyContext = import('./context').EnemyContext<
TestEnemyAttr,
TestHeroAttr
>;
type TestHeroAttribute = import('@user/data-base').HeroAttribute<TestHeroAttr>;
interface ContextFixture {
/** 被测怪物上下文 */
context: InstanceType<TestModules['EnemyContext']>;
context: TestEnemyContext;
/** 可修改勇士属性 */
hero: InstanceType<TestModules['HeroAttribute']>;
hero: TestHeroAttribute;
/** 数据层状态假对象 */
state: IStateBase;
}
@@ -146,7 +153,9 @@ function createContextFixture(
height: number = 3
): ContextFixture {
const state = {} as IStateBase;
const context = new modules.EnemyContext<TestEnemyAttr, TestHeroAttr>(state);
const context = new modules.EnemyContext<TestEnemyAttr, TestHeroAttr>(
state
);
context.resize(width, height);
const hero = new modules.HeroAttribute<TestHeroAttr>({
hp: 100,
@@ -255,9 +264,18 @@ class FakeConverter implements IAuraConverter<TestEnemyAttr, TestHeroAttr> {
return this.codes.includes(special.code);
}
convert(special: ISpecial<any>): IAuraView<TestEnemyAttr> | null {
convert(special: ISpecial<any>): IEnemyAuraView<TestEnemyAttr, any, any> {
this.convertCalls.push(special.code);
return this.factory ? this.factory(special.code) : null;
if (!this.factory) {
// 接口约定 convert 必返回光环视图,但测试需覆盖“无工厂则跳过转换”的 null 分支
return null as unknown as IEnemyAuraView<TestEnemyAttr, any, any>;
}
// FakeAura 为最小测试替身,不携带 enemy/special/locator,无法完整满足 IEnemyAuraView
return this.factory(special.code) as unknown as IEnemyAuraView<
TestEnemyAttr,
any,
any
>;
}
}
@@ -353,9 +371,9 @@ describe('EnemyContext registry and lookups', () => {
y: 0
});
expect(fixture.context.getEnemyLocator(enemy)).toEqual({ x: 1, y: 0 });
expect(fixture.context.getViewByComputed(view!.getComputedEnemy())).toBe(
view
);
expect(
fixture.context.getViewByComputed(view!.getComputedEnemy())
).toBe(view);
});
// 验证未知定位符、未知坐标与未知怪物对象均返回 null
@@ -691,7 +709,10 @@ describe('EnemyContext single aura ranges', () => {
fixture.context.buildup();
expect(
fixture.context.getEnemyByLoc(0, 0)!.getComputedEnemy().hasSpecial(30)
fixture.context
.getEnemyByLoc(0, 0)!
.getComputedEnemy()
.hasSpecial(30)
).toBe(true);
});
});
@@ -902,7 +923,9 @@ describe('EnemyContext aura pipeline', () => {
fixture.context.buildup();
const computed = fixture.context.getEnemyByLoc(0, 0)!.getComputedEnemy();
const computed = fixture.context
.getEnemyByLoc(0, 0)!
.getComputedEnemy();
expect(converter.convertCalls).toContain(21);
expect(computed.hasSpecial(21)).toBe(true);
expect(computed.getAttribute('atk')).toBe(9);
@@ -957,12 +980,16 @@ describe('EnemyContext aura pipeline', () => {
enemy.addSpecial(createSpecial(20) as never);
fixture.context.setEnemyAt({ x: 0, y: 0 }, enemy);
fixture.context.registerAuraConverter(
new FakeConverter([20], () => new FakeAura({
priority: 10,
range: new modules.FullRange(),
param: undefined,
couldApplyBase: false
}))
new FakeConverter(
[20],
() =>
new FakeAura({
priority: 10,
range: new modules.FullRange(),
param: undefined,
couldApplyBase: false
})
)
);
fixture.context.registerSpecialQueryEffect({
priority: 10,
@@ -987,12 +1014,16 @@ describe('EnemyContext aura pipeline', () => {
enemy.addSpecial(createSpecial(20) as never);
fixture.context.setEnemyAt({ x: 0, y: 0 }, enemy);
fixture.context.registerAuraConverter(
new FakeConverter([20], () => new FakeAura({
priority: 10,
range: new modules.FullRange(),
param: undefined,
couldApplyBase: false
}))
new FakeConverter(
[20],
() =>
new FakeAura({
priority: 10,
range: new modules.FullRange(),
param: undefined,
couldApplyBase: false
})
)
);
fixture.context.registerSpecialQueryEffect({
priority: 10,
@@ -1019,7 +1050,13 @@ describe('EnemyContext effect stage ordering', () => {
enemy.addSpecial(createSpecial(20) as never);
fixture.context.setEnemyAt({ x: 0, y: 0 }, enemy);
const order: string[] = [];
const seen = { queryAtk: -1, queryDef: -1, queryHp: -1, finalAtk: -1, finalDef: -1 };
const seen = {
queryAtk: -1,
queryDef: -1,
queryHp: -1,
finalAtk: -1,
finalDef: -1
};
const aura = new FakeAura({
priority: 1,
range: new modules.FullRange(),
@@ -1034,7 +1071,9 @@ describe('EnemyContext effect stage ordering', () => {
return { add: () => [], delete: () => [], modify: () => false };
}
});
fixture.context.registerAuraConverter(new FakeConverter([20], () => aura));
fixture.context.registerAuraConverter(
new FakeConverter([20], () => aura)
);
fixture.context.registerCommonQueryEffect(20, {
priority: 1,
apply: handler => {
@@ -1196,8 +1235,12 @@ describe('EnemyContext refresh paths and DEV warnings', () => {
param: undefined,
onApply: handler => handler.enemy.addAttribute('atk', 50)
});
fixture.context.registerAuraConverter(new FakeConverter([20], createAura));
fixture.context.registerAuraConverter(new FakeConverter([20], createAura));
fixture.context.registerAuraConverter(
new FakeConverter([20], createAura)
);
fixture.context.registerAuraConverter(
new FakeConverter([20], createAura)
);
const result = modules.logger.catch(() => fixture.context.buildup());
@@ -1246,11 +1289,15 @@ describe('EnemyContext refresh paths and DEV warnings', () => {
fixture.context.bindHero(fixture.hero);
fixture.context.setEnemyAt({ x: 0, y: 0 }, createEnemy('e1'));
fixture.context.registerAuraConverter(
new FakeConverter([40], () => new FakeAura({
priority: 1,
range: new modules.FullRange(),
param: undefined
}))
new FakeConverter(
[40],
() =>
new FakeAura({
priority: 1,
range: new modules.FullRange(),
param: undefined
})
)
);
fixture.context.registerSpecialQueryEffect({
priority: 1,
@@ -1278,11 +1325,15 @@ describe('EnemyContext refresh paths and DEV warnings', () => {
fixture.context.bindHero(fixture.hero);
fixture.context.setEnemyAt({ x: 0, y: 0 }, createEnemy('e1'));
fixture.context.registerAuraConverter(
new FakeConverter([40], () => new FakeAura({
priority: 50,
range: new modules.FullRange(),
param: undefined
}))
new FakeConverter(
[40],
() =>
new FakeAura({
priority: 50,
range: new modules.FullRange(),
param: undefined
})
)
);
fixture.context.registerSpecialQueryEffect({
priority: 1,
@@ -1,14 +1,17 @@
// 测试战斗伤害上下文与伤害系统:结果展开、handler 身份、告警码 106/107、缓存与 with、临界生成、属性到伤害联动(D-26)
import { beforeAll, describe, expect, it, vi } from 'vitest';
import { type ITileLocator } from '@motajs/common';
import { type IEnemy, type IStateBase } from '@user/data-base';
import {
type IEnemy,
type IReadonlyEnemy,
type IStateBase
} from '@user/data-base';
import {
type CriticalableHeroStatus,
type IDamageCalculator,
type IEnemyContext,
type IEnemyDamageInfoBase,
type IEnemyView,
type IReadonlyEnemy,
type IReadonlyEnemyHandler
} from './types';
@@ -92,9 +95,7 @@ beforeAll(async () => {
/**
* 固定伤害计算器:每点攻击减免固定伤害,用于验证结果展开与临界生成
*/
class FakeCalculator
implements IDamageCalculator<TestEnemyAttr, TestHeroAttr>
{
class FakeCalculator implements IDamageCalculator<TestEnemyAttr, TestHeroAttr> {
/** 每点攻击减免的伤害值 */
readonly damagePerAtk: number;
/** calculate 调用次数 */
@@ -123,13 +124,19 @@ class FakeCalculator
}
}
type TestHeroAttribute = import('@user/data-base').HeroAttribute<TestHeroAttr>;
type TestEnemyContext = import('./context').EnemyContext<
TestEnemyAttr,
TestHeroAttr
>;
interface DamageFixture {
/** 怪物上下文假对象 */
context: IEnemyContext<TestEnemyAttr, TestHeroAttr>;
/** 固定伤害计算器 */
calculator: FakeCalculator;
/** 可修改勇士属性 */
hero: InstanceType<TestModules['HeroAttribute']>;
hero: TestHeroAttribute;
/** 怪物视图假对象 */
view: IEnemyView<TestEnemyAttr>;
/** 计算后怪物 */
@@ -234,9 +241,10 @@ function createSpecial(code: number): never {
/**
* 创建一个按怪物攻击力线性换算伤害的计算器,用于验证属性到伤害的联动
*/
class EnemyAttributeCalculator
implements IDamageCalculator<TestEnemyAttr, TestHeroAttr>
{
class EnemyAttributeCalculator implements IDamageCalculator<
TestEnemyAttr,
TestHeroAttr
> {
/** calculate 调用次数 */
calls: number = 0;
/** 每点怪物攻击力换算的伤害 */
@@ -289,9 +297,7 @@ interface FakeConverterShape {
* 创建一个可驱动光环流水线的真实怪物上下文
* @param converter 赋予上下文的测试用光环转换器
*/
function createEnemyContext(
converter: FakeConverterShape
): InstanceType<TestModules['EnemyContext']> {
function createEnemyContext(converter: FakeConverterShape): TestEnemyContext {
const context = new modules.EnemyContext<TestEnemyAttr, TestHeroAttr>(
{} as IStateBase
);
@@ -507,8 +513,9 @@ describe('DamageContext critical generation', () => {
fixture.hero
);
const first = context.calculateCritical(fixture.view, 'atk').next()
.value!;
const first = context
.calculateCritical(fixture.view, 'atk')
.next().value!;
expect(first.baseValue).toBe(0);
expect(first.nextValue).toBe(1);
@@ -577,8 +584,9 @@ describe('DamageContext critical generation', () => {
fixture.hero
);
const first = context.calculateCritical(fixture.view, 'atk').next()
.value!;
const first = context
.calculateCritical(fixture.view, 'atk')
.next().value!;
expect(first.nextValue).toBe(1);
expect(first.info.damage).toBe(90);
@@ -705,10 +713,7 @@ describe('DamageSystem attribute linkage', () => {
context.buildup();
expect(
context
.getEnemyByLoc(0, 0)!
.getComputedEnemy()
.getAttribute('atk')
context.getEnemyByLoc(0, 0)!.getComputedEnemy().getAttribute('atk')
).toBe(17);
});
});
@@ -165,9 +165,10 @@ class FakeView implements IMapDamageView<number> {
/**
* 固定输出伤害视图列表的测试转换器
*/
class FakeConverter
implements IMapDamageConverter<TestEnemyAttr, TestHeroAttr>
{
class FakeConverter implements IMapDamageConverter<
TestEnemyAttr,
TestHeroAttr
> {
/** 转换输出的伤害视图 */
readonly views: IMapDamageView<number>[];
/** convert 调用次数 */
@@ -190,9 +191,7 @@ class FakeReducer implements IMapDamageReducer {
/** reduce 调用次数 */
calls: number = 0;
reduce(
info: Iterable<Readonly<IMapDamageInfo>>
): Readonly<IMapDamageInfo> {
reduce(info: Iterable<Readonly<IMapDamageInfo>>): Readonly<IMapDamageInfo> {
this.calls++;
let damage = 0;
for (const item of info) {
@@ -249,10 +248,9 @@ function createFixture(): MapDamageFixture {
markDirty: () => {}
};
enemies.set(view, locator);
const damageView = new FakeView(
7,
[indexer.locToIndex(locator.x, locator.y)]
);
const damageView = new FakeView(7, [
indexer.locToIndex(locator.x, locator.y)
]);
const context = {
state,
indexer,
@@ -264,17 +262,13 @@ function createFixture(): MapDamageFixture {
getEnemyLocator: (target: IEnemy<TestEnemyAttr>) =>
target === origin ? locator : null,
getViewByComputed: () => view,
*iterateEnemy(): Iterable<
[ITileLocator, IEnemyView<TestEnemyAttr>]
> {
*iterateEnemy(): Iterable<[ITileLocator, IEnemyView<TestEnemyAttr>]> {
for (const [target, targetLocator] of enemies) {
yield [targetLocator, target];
}
}
} as never;
const damage = new modules.MapDamage<TestEnemyAttr, TestHeroAttr>(
context
);
const damage = new modules.MapDamage<TestEnemyAttr, TestHeroAttr>(context);
return {
damage,
context,
@@ -290,7 +284,9 @@ function createFixture(): MapDamageFixture {
/**
* 创建一个未注册到夹具中的怪物视图,用于验证未知定位符路径
*/
function createUnknownView(fixture: MapDamageFixture): IEnemyView<TestEnemyAttr> {
function createUnknownView(
fixture: MapDamageFixture
): IEnemyView<TestEnemyAttr> {
return {
context: {} as never,
reset: () => {},
@@ -309,14 +305,14 @@ describe('MapDamage sourceless damage', () => {
const info = createInfo(5);
fixture.damage.addMapDamage({ x: 0, y: 0 }, info);
expect(
[...fixture.damage.getSeparatedDamage({ x: 0, y: 0 })]
).toContain(info);
expect([
...fixture.damage.getSeparatedDamage({ x: 0, y: 0 })
]).toContain(info);
fixture.damage.deleteMapDamage({ x: 0, y: 0 }, info);
expect(
[...fixture.damage.getSeparatedDamage({ x: 0, y: 0 })]
).toHaveLength(0);
expect([
...fixture.damage.getSeparatedDamage({ x: 0, y: 0 })
]).toHaveLength(0);
});
// 验证删除不存在的无来源伤害是安全操作
@@ -326,9 +322,9 @@ describe('MapDamage sourceless damage', () => {
fixture.damage.deleteMapDamage({ x: 3, y: 2 }, createInfo(5));
expect(
[...fixture.damage.getSeparatedDamage({ x: 3, y: 2 })]
).toHaveLength(0);
expect([
...fixture.damage.getSeparatedDamage({ x: 3, y: 2 })
]).toHaveLength(0);
});
});
@@ -357,9 +353,9 @@ describe('MapDamage sourced conversion and reduction', () => {
fixture.damage.useConverter(fixture.converter);
fixture.damage.addMapDamage(fixture.locator, createInfo(3));
expect(
[...fixture.damage.getSeparatedDamage(fixture.locator)]
).toHaveLength(2);
expect([
...fixture.damage.getSeparatedDamage(fixture.locator)
]).toHaveLength(2);
const reduced = fixture.damage.getReducedDamage(fixture.locator);
expect(reduced?.damage).toBe(10);
@@ -445,15 +441,14 @@ describe('MapDamage sourced conversion and reduction', () => {
const fixture = createFixture();
fixture.damage.useReducer(fixture.reducer);
fixture.damage.useConverter(fixture.converter);
const before = [
...fixture.damage.getSeparatedDamage(fixture.locator)
].length;
const before = [...fixture.damage.getSeparatedDamage(fixture.locator)]
.length;
fixture.damage.deleteEnemy(createUnknownView(fixture));
expect(
[...fixture.damage.getSeparatedDamage(fixture.locator)]
).toHaveLength(before);
expect([
...fixture.damage.getSeparatedDamage(fixture.locator)
]).toHaveLength(before);
});
// 疑似 bug:deleteEnemy 应移除该怪物带来的有来源地图伤害,详见 06-TEST-FINDINGS.md #06-01-2,修复后取消 skip
@@ -461,14 +456,14 @@ describe('MapDamage sourced conversion and reduction', () => {
const fixture = createFixture();
fixture.damage.useReducer(fixture.reducer);
fixture.damage.useConverter(fixture.converter);
expect(
[...fixture.damage.getSeparatedDamage(fixture.locator)]
).toHaveLength(1);
expect([
...fixture.damage.getSeparatedDamage(fixture.locator)
]).toHaveLength(1);
fixture.damage.deleteEnemy(fixture.view);
expect(
[...fixture.damage.getSeparatedDamage(fixture.locator)]
).toHaveLength(0);
expect([
...fixture.damage.getSeparatedDamage(fixture.locator)
]).toHaveLength(0);
});
});