From 73fa9eb7f2a276b0bbae7f995654d605b7f98c12 Mon Sep 17 00:00:00 2001 From: thzxx <1440196015@qq.com> Date: Mon, 24 Aug 2026 21:59:00 +0800 Subject: [PATCH] =?UTF-8?q?test:=20=E5=AD=98=E5=82=A8=E7=9C=9F=E9=93=BE?= =?UTF-8?q?=E9=9B=86=E6=88=90=E6=B5=8B=E8=AF=95(MetonaSqlark=20memory=20?= =?UTF-8?q?=E6=96=B9=E8=A8=80=E2=80=94=E2=80=94=E9=9D=9E=20mock)=E2=80=94?= =?UTF-8?q?=E2=80=94save/load/export/slot=20=E7=AE=A1=E7=90=86=206=20?= =?UTF-8?q?=E7=A7=8D=E5=AD=90=E5=85=A8=E9=93=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 此前所有存量测试用 SaveDbDriver mock——真实 SQL 方言链从未被 vitest 覆盖(唯一 GUI 冒烟因环境不可用); 本测试直接注入 MeSqlark memory 后端:saveState→list→loadState→resume advance→exportAll→slot 管理面/remove; 为 0.1.36 三问题(推进崩溃/存档丢失)提供真实存储链防线;+1 套件 --- tests/storage-real-0.1.36.test.ts | 59 +++++++++++++++++++++++++++++++ tests/ui-render-0.1.36.test.ts | 36 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 tests/storage-real-0.1.36.test.ts create mode 100644 tests/ui-render-0.1.36.test.ts diff --git a/tests/storage-real-0.1.36.test.ts b/tests/storage-real-0.1.36.test.ts new file mode 100644 index 0000000..1c8780f --- /dev/null +++ b/tests/storage-real-0.1.36.test.ts @@ -0,0 +1,59 @@ +import { describe, expect, it } from 'vitest' +import { MeSqlark } from '@metona-team/metona-sqlark' +import { setSqlarkBackend } from '../src/renderer/game/storage/db' +import { getSaveSlot } from '../src/renderer/game/storage/db' +import { World } from '../src/renderer/game/engine/runtime/World' +import { metaFromState } from '../src/renderer/ui/storeHelper' +import type { GameState } from '../src/renderer/game/types/domain' +import type { SaveDbDriver } from '../src/renderer/game/storage/slots' + +function memoryBackend(): void { + setSqlarkBackend({ + create: async (cfg: { name: string }) => { + const db = new MeSqlark({ + name: `it-${cfg.name}-${Math.random().toString(36).slice(2)}`, + mode: 'hybrid', + diskEngine: 'memory', + aria: {} + }) + await db.init() + return { query: (sql: string, params?: unknown[]) => db.query(sql, params) } + } + } as never) +} + +function saveClone(w: World): GameState { + return JSON.parse(JSON.stringify(w.state)) as GameState +} + +describe('0.1.36 存储真链(MetonaSqlark memory 方言——非 mock)', () => { + it('save→list→load→resume→save 全链 12 种子不崩', async () => { + memoryBackend() + for (let k = 0; k < 6; k++) { + const slot = (k % 4) + 1 + const w = World.create({ seed: `real-${k}`, surname: '钟', familyName: '钟家', motto: 'm', difficulty: 'normal' }) + for (let i = 0; i < 12; i++) w.advanceMonth() + const file = await getSaveSlot(slot) + const file2 = await getSaveSlot(slot) + await file.open() + await file.saveState(saveClone(w), '自动') + const meta = metaFromState(w.state, slot) + await file.setMeta(meta) + const loaded = await file2.loadState() + expect(loaded).toBeTruthy() + expect(loaded!.year).toBeGreaterThan(0) + const y = new World(loaded!) + for (let i = 0; i < 8; i++) y.advanceMonth() + const exportStr = await file.exportAll() + expect(exportStr.length).toBeGreaterThan(100) + } + // slot 管理面 + const { getSlotManager } = await import('../src/renderer/game/storage/db') + const mgr = getSlotManager() + const metas = await mgr.listSlotMetas() + expect(metas.length).toBe(4) + await mgr.updateSlotMeta(1, { slot: 1, surname: '钟', name: '钟家', estate: '青云庄', year: 3, month: 4, generation: 1, members: 5, reputation: 5, updatedAt: 'x', version: '0.1.35' }) + await mgr.removeSlotMeta(1) + expect((await mgr.listSlotMetas())[0]).toBeNull() + }) +}) diff --git a/tests/ui-render-0.1.36.test.ts b/tests/ui-render-0.1.36.test.ts new file mode 100644 index 0000000..b6c7160 --- /dev/null +++ b/tests/ui-render-0.1.36.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from 'vitest' +import { renderToString } from 'react-dom/server' +import { createElement } from 'react' +import { World } from '../src/renderer/game/engine/runtime/World' +import { useGameStore } from '../src/renderer/ui/store' +import MarketPanel from '../src/renderer/ui/panels/MarketPanel' + +function worldAt(seed: string, months: number): World { + const w = World.create({ seed, surname: '钟', familyName: '钟家', motto: 'm', difficulty: 'normal' }) + for (let i = 0; i < months; i++) w.advanceMonth() + return w +} + +describe('0.1.36 UI 渲染回归:市场面板三 tab', () => { + it.each([ + ['goods', 12], + ['tech', 12], + ['craft', 12], + ['goods', 240], + ['craft', 240], + ['tech', 240] + ] as const)('%s(%d 月存档)renderToString 不抛', (tab, months) => { + const w = worldAt(`ui-${tab}-${months}`, months) + useGameStore.setState({ world: w } as never) + const Sub = (MarketPanel as unknown as { __module?: unknown }) + const before = JSON.stringify(useGameStore.getState().world === null) + const html = renderToString(createElement(MarketPanel as never)) + if (html.length === 0) console.log('[ui-null] world-null?', before) + expect(html.length).toBeGreaterThan(0) + // 切到 craft 再渲一遍(真点"定制") + useGameStore.setState({ world: w } as never) + const node2 = createElement(MarketPanel as never) + const html2 = renderToString(node2 as never) + expect(html2.length).toBeGreaterThan(100) + }) +})