Files
90CNServer/pvf/frontend/tests/logging.test.ts
T
hanfuit 645b64fe21 DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go):
- GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面
- GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新
- 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新)
- 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍
- 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0)
- 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算

启动器(Python/JS):
- 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭
- GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐)
- 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
2026-11-04 14:31:55 +08:00

80 lines
3.1 KiB
TypeScript

import { afterEach, beforeEach, expect, test, vi } from "vitest";
import { createApp } from "vue";
import { createPinia, setActivePinia } from "pinia";
const runtime = vi.hoisted(() => ({
handlers: new Map<string, (event: { data: unknown }) => void>(),
emit: vi.fn(),
}));
vi.mock("@wailsio/runtime", () => ({
Events: {
On: (name: string, handler: (event: { data: unknown }) => void) => runtime.handlers.set(name, handler),
Emit: runtime.emit,
},
}));
import { installLogging } from "../src/logging";
import { useLogStore } from "../src/stores/logs";
const listeners = new Map<string, (event: any) => void>();
beforeEach(() => {
vi.useFakeTimers();
setActivePinia(createPinia());
runtime.handlers.clear();
listeners.clear();
runtime.emit.mockReset().mockResolvedValue(undefined);
vi.stubGlobal("window", { addEventListener: (name: string, handler: (event: any) => void) => listeners.set(name, handler) });
vi.spyOn(console, "error").mockImplementation(() => {});
vi.spyOn(console, "warn").mockImplementation(() => {});
});
afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
vi.unstubAllGlobals();
vi.unstubAllEnvs();
});
test("subscribes before requesting history and receives backend logs", () => {
installLogging(createApp({}));
expect(runtime.emit).toHaveBeenCalledWith("app:logs-request");
runtime.handlers.get("app:logs")?.({ data: [{ id: 1, timestamp: "2026-10-07T12:00:00Z", level: "INFO", message: "startup" }] });
runtime.handlers.get("app:log")?.({ data: { id: 2, timestamp: "2026-10-07T12:00:01Z", level: "ERROR", message: "failed" } });
expect(useLogStore().entries).toHaveLength(0);
vi.runAllTimers();
expect(useLogStore().entries.map((entry) => entry.message)).toEqual(["startup", "failed"]);
});
test("captures console, browser and Vue exceptions", () => {
const app = createApp({});
const previous = vi.fn();
app.config.errorHandler = previous;
installLogging(app);
console.error("caught", new Error("console error"));
console.warn("warning");
listeners.get("error")?.({ error: new Error("browser error") });
listeners.get("unhandledrejection")?.({ reason: new Error("promise error") });
app.config.errorHandler?.(new Error("vue error"), null, "render");
const logs = useLogStore();
expect(logs.entries).toHaveLength(5);
expect(logs.errorCount).toBe(4);
expect(previous).toHaveBeenCalledOnce();
});
test("生产环境不写 Vue 开发警告,仍捕获业务警告与实际异常", () => {
vi.stubEnv("DEV", false);
vi.stubEnv("MODE", "production");
const warningOutput = console.warn;
const app = createApp({});
installLogging(app);
console.warn("[Vue warn]: development-only trace");
console.warn("业务警告");
console.error("业务错误");
app.config.errorHandler?.(new Error("Vue actual error"), null, "runtime trace");
const logs = useLogStore();
expect(warningOutput).toHaveBeenCalledOnce();
expect(logs.entries).toHaveLength(3);
expect(logs.errorCount).toBe(2);
expect(logs.entries.some((entry) => entry.message.includes("development-only") || entry.message.includes("runtime trace"))).toBe(false);
});