Files
ChronicleOfTheImmortalClan/tests/data-matrix.test.ts
T
thzxx 3195f67455 test: 测试矩阵扩至 861 项(参数化全覆盖)
新增 5 个矩阵套件:
- data-matrix 242:物品/功法/建筑/秘境/敌人/势力/事件选项/职事/秉性/灵根/境界/时节 逐行
- balance-edge 81:年龄/修为/气血/库存/金钱边界、传承谱系年轴、境界战力、突破矩阵、评分分档、难度
- world-matrix 21:多 seed 长跑、继承链多路径、gameOver 守卫、岁簿/大比/回声周期、确定性复跑
- rng-matrix 38:多种子性质、范围分桶、RngHub 回放/隔离、state 拷贝、pick/shuffle 保集
- api-matrix 32:act 全目录逐项、query 全 ref、subscribe 全事件矩阵
- event-state-matrix 193:逐事件逐选项应用不破、effect 引用、职事上限/寄读、志向、normalize 残缺矩阵、动态事件

引擎附带优化:月底 clampState(修为/气血/库存/金钱永不越界——负血量负库存负钱被矩阵钓出后修复)
总测试 256 → 861;金钟罩复验通过(clamp 零漂移)
2026-08-23 10:04:33 +08:00

160 lines
5.6 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { ITEMS, ARTIFACT_POWER } from '../src/renderer/game/data/items'
import { TECHNIQUES } from '../src/renderer/game/data/techniques'
import { BUILDINGS } from '../src/renderer/game/data/buildings'
import { MISSIONS, ENEMIES } from '../src/renderer/game/data/secrets'
import { NPCS } from '../src/renderer/game/data/npcs'
import { EVENTS } from '../src/renderer/game/data/events'
import { POSTS } from '../src/renderer/game/data/posts'
import { TRAITS } from '../src/renderer/game/data/traits'
import { ROOT_GRADES } from '../src/renderer/game/data/elements'
import { MAJORS, MAJOR_ORDER } from '../src/renderer/game/data/realms'
import { SEASON, seasonOf } from '../src/renderer/game/data/season'
describe('物品表逐行矩阵', () => {
it.each(Object.entries(ITEMS).map(([id, v]) => [id, v.name, v.basePrice, v.kind] as const))(
'物品 %s 名称/价格/类别合法',
(_id, name, price, kind) => {
expect(name.length).toBeGreaterThan(0)
expect(price).toBeGreaterThan(0)
expect(['resource', 'pill', 'artifact']).toContain(kind)
}
)
it.each(Object.entries(ITEMS).filter(([, v]) => v.kind === 'artifact').map(([id]) => [id] as const))(
'法器 %s 有战力修正',
(id) => {
expect(ARTIFACT_POWER[id]).toBeGreaterThan(0)
}
)
})
describe('功法表逐行矩阵', () => {
it.each(TECHNIQUES.map((t) => [t.id, t.grade, t.powerBonus, t.expBonus, t.path] as const))(
'功法 %s 参数板面合法',
(id, grade, pow, exp, path) => {
expect(id.startsWith('t-')).toBe(true)
expect(grade).toBeGreaterThanOrEqual(1)
expect(grade).toBeLessThanOrEqual(4)
expect(pow).toBeGreaterThan(0)
expect(exp).toBeGreaterThan(0)
expect(path.length).toBeGreaterThan(0)
}
)
it('功法 id 全局唯一', () => {
expect(new Set(TECHNIQUES.map((t) => t.id)).size).toBe(TECHNIQUES.length)
})
})
describe('建筑表逐行矩阵', () => {
it.each(Object.entries(BUILDINGS).map(([id, v]) => [id, v.maxLevel, v.kind] as const))(
'建筑 %s 等级/类型合法',
(id, max, kind) => {
expect(id.length).toBeGreaterThan(1)
expect(max).toBeGreaterThanOrEqual(3)
expect(['produce', 'function']).toContain(kind)
}
)
it.each(Object.entries(BUILDINGS).map(([id, v]) => [id, v.upgradeCost(1), v.upgradeCost(v.maxLevel)] as const))(
'建筑 %s 升级成本随级递增',
(_id, c1, cMax) => {
expect(cMax.stones).toBeGreaterThan(c1.stones)
expect(cMax.lingkuang).toBeGreaterThan(c1.lingkuang)
}
)
})
describe('秘境表逐行矩阵', () => {
it.each(MISSIONS.map((m) => [m.id, m.name] as const))('秘境 %s 存在启程摘要', (id, name) => {
expect(id.startsWith('m-')).toBe(true)
expect(name.length).toBeGreaterThan(1)
})
it.each(
MISSIONS.flatMap((m) =>
m.stages.map(
(st, i) => [m.id, i, st.kind, st.months] as const
)
)
)('秘境 %s 第%i 阶段类型/月数合法', (_id, _i, kind, months) => {
expect(['event', 'combat', 'resource', 'boss']).toContain(kind)
expect(months).toBeGreaterThan(0)
})
it.each(ENEMIES.map((e) => [e.id, e.realm] as const))('敌人 %s 境界关联存在', (id, realm) => {
expect(id.startsWith('e-')).toBe(true)
expect(MAJOR_ORDER).toContain(realm)
})
})
describe('势力表逐行矩阵', () => {
it.each(NPCS.map((n) => [n.id, n.name] as const))('势力 %s 命名与风格', (id, name) => {
expect(id.startsWith('n-')).toBe(true)
expect(name).toContain('氏')
})
it.each(NPCS.map((n) => [n.id, n.initialPower] as const))('势力 %s 初始战力为正', (_id, p) => {
expect(p).toBeGreaterThan(0)
})
it.each(NPCS.map((n) => [n.id, n.sells ?? [], n.buys ?? []] as const))('势力 %s 交易品类引用合法', (_id, sells, buys) => {
for (const s of sells) {
expect(ITEMS[s] ?? TECHNIQUES.find((t) => t.id === s)).toBeTruthy()
}
void buys
})
})
describe('事件表逐选项矩阵', () => {
it.each(EVENTS.flatMap((e) => e.options.map((o, idx) => [e.id, idx, o.label.length] as const)))(
'事件 %s 选项%i 有文案',
(_id, _idx, len) => {
expect(len).toBeGreaterThan(0)
}
)
it.each(EVENTS.map((e) => [e.id, e.weight] as const))('事件 %s 权重为正', (_id, w) => {
expect(w).toBeGreaterThan(0)
})
it.each(EVENTS.filter((e) => e.cond?.minBuilding).map((e) => [e.id, e.cond!.minBuilding!.id] as const))(
'事件 %s 建筑条件引用存在',
(_id, b) => {
expect(BUILDINGS[b]).toBeTruthy()
}
)
})
describe('职事/秉性/灵根/境界矩阵', () => {
it.each(Object.values(POSTS).map((p) => [p.id, p.name, p.max] as const))('职事 %s %s 上限合法', (id, name, max) => {
expect(id.length).toBeGreaterThan(0)
expect(name.length).toBeGreaterThan(0)
expect(max).toBeGreaterThanOrEqual(1)
})
it.each(Object.entries(TRAITS).map(([id, t]) => [id, t.danger] as const))('秉性 %s 危险度在界', (_id, d) => {
expect(d).toBeGreaterThanOrEqual(0)
expect(d).toBeLessThanOrEqual(1)
})
it.each(Object.entries(ROOT_GRADES).map(([k, v]) => [Number(k), v.expBonus] as const))('灵根品阶%i 加成递增', (grade, exp) => {
expect(exp).toBeGreaterThan(0)
if (grade > 0) {
expect(exp).toBeGreaterThan(ROOT_GRADES[grade - 1].expBonus)
}
})
it.each(MAJOR_ORDER.map((m) => [m, MAJORS[m].lifespan] as const))('大境界 %s 寿元为正', (_m, l) => {
expect(l).toBeGreaterThan(0)
})
})
describe('时节数学矩阵', () => {
it.each([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map((m) => [m, seasonOf(m)] as const))('%i月 归季 %s', (m, s) => {
const mod = SEASON[s]
expect(mod.name.length).toBeGreaterThan(0)
})
})