import { describe, expect, it } from 'vitest' import { MAJORS, MAJOR_ORDER, MAJOR_NAMES, describeRealm, realmTier, compareRealm, nextRealm, maxRealmExp, basePower, breakthroughBaseChance, realmDeathChance, TECHNIQUE_GRADE_NAMES } from '../src/renderer/game/data/realms' import { ELEMENT_LIST, ROOT_GRADES, ROOT_GRADE_NAMES, describeRoots } from '../src/renderer/game/data/elements' 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, missionById } from '../src/renderer/game/data/secrets' import { NPCS, npcById } from '../src/renderer/game/data/npcs' import { POSTS, POST_ORDER } from '../src/renderer/game/data/posts' import { EVENTS } from '../src/renderer/game/data/events' import { MAJOR_RATE, masteryRateOfMajor } from '../src/renderer/game/data/pacing' import { SURNAME_POOL, MALE_GIVEN, FEMALE_GIVEN } from '../src/renderer/game/engine/kernel/names' import { TRAITS } from '../src/renderer/game/data/traits' describe('realms 境界表', () => { it('每大境界结构完整(名称/层数/寿元/修为递增进)', () => { for (const major of MAJOR_ORDER) { const def = MAJORS[major] expect(def.name).toBeTruthy() expect(def.minorLayers).toBeGreaterThanOrEqual(0) expect(def.lifespan).toBeGreaterThan(0) expect(def.expBase).toBeGreaterThanOrEqual(0) expect(def.expGrowth).toBeGreaterThanOrEqual(1) } expect(MAJOR_NAMES.mortal).toBe('凡人') expect(MAJOR_NAMES.spirit).toBe('化神') }) it('大境界寿元单调递增', () => { for (let i = 1; i < MAJOR_ORDER.length; i++) { expect(MAJORS[MAJOR_ORDER[i]].lifespan).toBeGreaterThan(MAJORS[MAJOR_ORDER[i - 1]].lifespan) } }) it('describeRealm 映射正确', () => { expect(describeRealm({ major: 'mortal', minor: 0 })).toBe('凡人') expect(describeRealm({ major: 'qi', minor: 0 })).toBe('炼气1层') expect(describeRealm({ major: 'qi', minor: 8 })).toBe('炼气9层') expect(describeRealm({ major: 'spirit', minor: 2 })).toBe('化神3层') }) it('realmTier/compareRealm 正确排序', () => { expect(realmTier({ major: 'mortal', minor: 0 })).toBe(0) expect(realmTier({ major: 'qi', minor: 0 })).toBe(11) expect(realmTier({ major: 'core', minor: 0 })).toBe(31) expect(compareRealm({ major: 'qi', minor: 3 }, { major: 'foundation', minor: 0 })).toBeLessThan(0) expect(compareRealm({ major: 'qi', minor: 9 }, { major: 'qi', minor: 5 })).toBeGreaterThan(0) }) it('nextRealm 链完整走完直至化神壁', () => { let r = { major: 'mortal', minor: 0 } as { major: (typeof MAJOR_ORDER)[number]; minor: number } r = nextRealm(r)! expect(r).toEqual({ major: 'qi', minor: 0 }) r = nextRealm(r)! expect(r).toEqual({ major: 'qi', minor: 1 }) r = { major: 'qi', minor: 8 } r = nextRealm(r)! expect(r).toEqual({ major: 'foundation', minor: 0 }) r = { major: 'foundation', minor: 2 } r = nextRealm(r)! expect(r).toEqual({ major: 'core', minor: 0 }) r = { major: 'spirit', minor: 2 } expect(nextRealm(r)).toBeNull() }) it('maxRealmExp 随层数严格增长', () => { for (const major of ['qi', 'foundation', 'core', 'nascent'] as const) { for (let i = 0; i < MAJORS[major].minorLayers - 1; i++) { expect(maxRealmExp(major, i + 1)).toBeGreaterThan(maxRealmExp(major, i)) } } }) it('basePower 战力随境界单调递增', () => { const seq: { major: (typeof MAJOR_ORDER)[number]; minor: number }[] = [ { major: 'mortal', minor: 0 }, { major: 'qi', minor: 0 }, { major: 'qi', minor: 8 }, { major: 'foundation', minor: 0 }, { major: 'core', minor: 0 }, { major: 'nascent', minor: 0 }, { major: 'spirit', minor: 0 } ] for (let i = 1; i < seq.length; i++) { expect(basePower(seq[i])).toBeGreaterThan(basePower(seq[i - 1])) } }) it('突破基础概率与死亡风险在合法区间且递减/递增', () => { const majors = ['mortal', 'qi', 'foundation', 'core', 'nascent', 'spirit'] for (const major of majors) { const b = breakthroughBaseChance({ major: major as never, minor: 0 }) expect(b).toBeGreaterThan(0) expect(b).toBeLessThanOrEqual(0.9) } for (let i = 1; i < majors.length; i++) { expect(breakthroughBaseChance({ major: majors[i] as never, minor: 0 })).toBeLessThan( breakthroughBaseChance({ major: majors[i - 1] as never, minor: 0 }) ) } for (const major of majors) { expect(realmDeathChance({ major: major as never, minor: 0 }, 5)).toBeGreaterThanOrEqual(0) expect(realmDeathChance({ major: major as never, minor: 0 }, 5)).toBeLessThan(0.06) } }) it('品阶名数量一致(含凡阶哨兵)', () => { expect(TECHNIQUE_GRADE_NAMES.length).toBe(6) }) }) describe('elements 灵根', () => { it('五行与品阶定义齐全', () => { expect(ELEMENT_LIST.length).toBe(5) for (const g of [0, 1, 2, 3, 4, 5]) { expect(ROOT_GRADES[g]).toBeTruthy() expect(ROOT_GRADES[g].drawWeight).toBeGreaterThan(0) expect(ROOT_GRADES[g].expBonus).toBeGreaterThan(0) } expect(ROOT_GRADE_NAMES.length).toBe(6) }) it('品阶修为加成单调', () => { for (let g = 1; g <= 5; g++) { expect(ROOT_GRADES[g].expBonus).toBeGreaterThan(ROOT_GRADES[g - 1].expBonus) } }) it('describeRoots 包含主属性与品阶', () => { const label = describeRoots({ grade: 3, primary: '火', secondary: ['金', '水'] }) expect(label).toContain('火') expect(label).toContain('真品灵根') }) }) describe('items 物品表', () => { it('物品 id 唯一且价格为正', () => { const ids = new Set() for (const [id, item] of Object.entries(ITEMS)) { expect(ids.has(id)).toBe(false) ids.add(id) expect(item.basePrice).toBeGreaterThan(0) expect(item.name).toBeTruthy() } }) it('法宝战力表与法器条目一一对应', () => { const artifacts = Object.entries(ITEMS).filter(([, v]) => v.kind === 'artifact') for (const [id] of artifacts) { expect(ARTIFACT_POWER[id]).toBeGreaterThan(0) } expect(Object.keys(ARTIFACT_POWER).length).toBe(artifacts.length) }) }) describe('techniques 功法表', () => { it('字段在法律区间且元素合法', () => { for (const t of TECHNIQUES) { expect(t.grade).toBeGreaterThanOrEqual(0) expect(t.grade).toBeLessThanOrEqual(4) expect(t.expBonus).toBeGreaterThan(0) expect(t.powerBonus).toBeGreaterThan(0) expect(ELEMENT_LIST).toContain(t.element) expect(['剑修', '体修', '丹修', '阵修', '符修', '御灵']).toContain(t.path) } }) it('品阶越高综合面板越强(对比均值)', () => { const avg = (g: number) => { const pool = TECHNIQUES.filter((t) => t.grade === g) const exp = pool.reduce((a, t) => a + t.expBonus, 0) / pool.length const pow = pool.reduce((a, t) => a + t.powerBonus, 0) / pool.length return exp + pow } for (let g = 2; g <= 4; g++) { expect(avg(g)).toBeGreaterThan(avg(g - 1)) } }) }) describe('buildings 建筑表', () => { it('所有建筑定义完整、升级成本递增', () => { for (const [id, def] of Object.entries(BUILDINGS)) { expect(def.maxLevel).toBeGreaterThan(0) expect(def.name).toBeTruthy() for (let l = 1; l < def.maxLevel; l++) { const now = def.upgradeCost(l) const next = def.upgradeCost(l + 1) expect(next.stones).toBeGreaterThan(now.stones) expect(next.lingkuang).toBeGreaterThan(now.lingkuang) if (def.produceTable) { const p0 = def.produceTable(l) const p1 = def.produceTable(l + 1) const key = Object.keys(p0)[0] expect((p1[key] ?? 0)).toBeGreaterThanOrEqual((p0[key] ?? 0)) } } } }) it('id 无重复', () => { expect(new Set(Object.keys(BUILDINGS)).size).toBe(Object.keys(BUILDINGS).length) }) }) describe('secrets 秘境', () => { it('每个秘境阶段引用存在的敌人、奖励合法', () => { const enemyIds = new Set(ENEMIES.map((e) => e.id)) for (const m of MISSIONS) { expect(m.minMembers).toBeGreaterThan(0) expect(m.minMembers).toBeLessThanOrEqual(m.maxMembers) expect(m.risk).toBeGreaterThan(0) expect(m.risk).toBeLessThanOrEqual(1) expect(m.stages.length).toBeGreaterThan(0) for (const st of m.stages) { expect(['event', 'combat', 'resource', 'boss']).toContain(st.kind) expect(st.months).toBeGreaterThan(0) if (st.enemyId) expect(enemyIds.has(st.enemyId)).toBe(true) if (st.loot) { for (const r of Object.values(st.loot.resources)) { expect(r[0]).toBeGreaterThanOrEqual(0) expect(r[1]).toBeGreaterThanOrEqual(r[0]) } } } for (const r of Object.values(m.completionLoot.resources)) { expect(r[1]).toBeGreaterThan(r[0]) } } expect(missionById('m-anmoku').name).toBe('暗墨林') expect(() => missionById('nope')).toThrow() }) }) describe('npcs 势力表', () => { it('四家齐全、growth 为正、leader 境界合法', () => { expect(NPCS.length).toBe(4) for (const n of NPCS) { expect(n.powerGrowth[0]).toBeGreaterThan(0) expect(n.powerGrowth[1]).toBeGreaterThanOrEqual(n.powerGrowth[0]) expect(MAJOR_ORDER).toContain(n.leaderRealm) } // 0.1.23:npcById 双源返回 undefined(不抛)——缺失防御化 expect(npcById('n-nulei')?.name).toBe('怒雷祝氏') expect(npcById('nope')).toBeUndefined() }) }) describe('posts 职事', () => { it('职事完备且加成归一', () => { expect(POST_ORDER.length).toBe(5) expect(POSTS['head'].max).toBe(1) for (const id of POST_ORDER) { expect(POSTS[id].max).toBeGreaterThan(0) expect(POSTS[id].effect.value).toBeGreaterThan(0) } }) }) describe('events 事件池', () => { it('事件 id 唯一、结构合法、once 权重边界', () => { const ids = new Set() for (const e of EVENTS) { expect(ids.has(e.id)).toBe(false) ids.add(e.id) expect(e.weight).toBeGreaterThan(0) expect(['daily', 'major', 'fate']).toContain(e.category) expect(e.options.length).toBeGreaterThan(0) expect(e.text.length).toBeGreaterThan(5) for (const o of e.options) { expect(o.label.length).toBeGreaterThan(0) } } }) it('事件条件引用的建筑/资源存在', () => { for (const e of EVENTS) { const cond = e.cond if (cond?.minBuilding) { expect(BUILDINGS[cond.minBuilding.id]).toBeTruthy() } if (cond?.minResource) { const id = cond.minResource.id expect(id === 'stones' || ITEMS[id]).toBeTruthy() } if (cond?.relation) { expect(NPCS.some((n) => n.id === cond.relation!.npcId)).toBe(true) } } }) }) describe('pacing 节奏', () => { it('全部大境界有修炼速率', () => { for (const major of MAJOR_ORDER) { const rate = masteryRateOfMajor(major) expect(rate).toBeGreaterThan(0) // 0.1.10 提速后 qi=2.2 属设计,仅验证仍低于高境界速率 } // 高境界低于低境界 expect(MAJOR_RATE['spirit']).toBeLessThan(MAJOR_RATE['foundation']) expect(MAJOR_RATE['nascent']).toBeLessThan(MAJOR_RATE['core']) }) }) describe('names 人名池', () => { it('姓氏/名池非空且无重复', () => { expect(SURNAME_POOL.length).toBeGreaterThan(20) expect(MALE_GIVEN.length).toBeGreaterThan(20) expect(FEMALE_GIVEN.length).toBeGreaterThan(20) expect(new Set(MALE_GIVEN).size).toBe(MALE_GIVEN.length) expect(new Set(FEMALE_GIVEN).size).toBe(FEMALE_GIVEN.length) }) }) describe('traits 秉性', () => { it('全部特质定义完整、danger 在界', () => { for (const t of Object.values(TRAITS)) { expect(t.name).toBeTruthy() expect(t.danger).toBeGreaterThanOrEqual(0) expect(t.danger).toBeLessThanOrEqual(1) } }) })