Files
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

121 lines
4.6 KiB
TypeScript

import { afterEach, beforeEach, expect, test, vi } from "vitest";
import { isReactive, watch } from "vue";
import { createPinia, setActivePinia } from "pinia";
import { formatLogValue, logCapacity, useLogStore } from "../src/stores/logs";
beforeEach(() => setActivePinia(createPinia()));
afterEach(() => {
vi.useRealTimers();
vi.unstubAllEnvs();
});
function backend(id: number, level = "INFO") {
return { id, timestamp: "2026-10-07T10:00:00Z", source: "Go", level, message: `entry ${id}` };
}
test("records while collapsed and filters exact selected levels", () => {
const logs = useLogStore();
logs.add("DEBUG", "test", "debug");
logs.add("INFO", "test", "info");
logs.add("WARN", "test", "warning");
logs.add("ERROR", "test", new Error("failure"));
expect(logs.expanded).toBe(false);
expect(logs.entries).toHaveLength(4);
expect(logs.filtered).toHaveLength(3);
expect(logs.errorCount).toBe(1);
logs.levels = ["ERROR", "DEBUG"];
expect(logs.filtered.map((entry) => entry.level)).toEqual(["DEBUG", "ERROR"]);
logs.levels = [];
expect(logs.filtered).toEqual([]);
});
test("history and live events are deduplicated and backend order is restored", () => {
const logs = useLogStore();
logs.ingest(backend(3));
logs.ingest([backend(1), backend(2), backend(3)]);
expect(logs.entries.map((entry) => entry.backendID)).toEqual([1, 2, 3]);
logs.ingest(backend(2));
expect(logs.entries).toHaveLength(3);
});
test("retains only the latest entries", () => {
const logs = useLogStore();
logs.ingest(Array.from({ length: logCapacity + 50 }, (_, i) => backend(i + 1)));
expect(logs.entries).toHaveLength(logCapacity);
expect(logs.entries[0].backendID).toBe(51);
logs.add("ERROR", "test", "latest");
expect(logs.entries).toHaveLength(logCapacity);
expect(logs.entries.at(-1)?.message).toBe("latest");
});
test("clear prevents delayed history replay from restoring cleared entries", () => {
const logs = useLogStore();
logs.ingest(backend(10));
logs.clear();
logs.ingest([backend(5), backend(10), backend(11)]);
expect(logs.entries.map((entry) => entry.backendID)).toEqual([11]);
logs.add("ERROR", "test", "new error");
expect(logs.errorCount).toBe(1);
logs.clear();
expect(logs.errorCount).toBe(0);
});
test("handles ERR, unknown levels and malformed events", () => {
const logs = useLogStore();
logs.ingest([null, {}, { ...backend(0) }, backend(1, "ERR"), backend(2, "unknown")]);
expect(logs.entries.map((entry) => entry.level)).toEqual(["ERROR", "INFO"]);
});
test("formats exceptions, objects and circular console arguments", () => {
expect(formatLogValue(new Error("failure"))).toContain("failure");
expect(formatLogValue({ state: "error" })).toBe('{"state":"error"}');
const circular: { self?: unknown } = {};
circular.self = circular;
expect(formatLogValue(circular)).toBe("[object Object]");
});
test("日志突发异步合并为一次界面更新,并保留容量内的顺序与错误", () => {
vi.useFakeTimers();
const logs = useLogStore();
let publications = 0;
const stop = watch(() => logs.entries, () => publications++, { flush: "sync" });
for (let id = 4000; id > 0; id--) logs.enqueueBackendLogs(backend(id, id === 4000 ? "ERROR" : "DEBUG"));
expect(logs.entries).toHaveLength(0);
vi.runAllTimers();
expect(publications).toBe(1);
expect(logs.entries).toHaveLength(logCapacity);
expect(logs.entries[0].backendID).toBe(2001);
expect(logs.entries.at(-1)?.backendID).toBe(4000);
expect(logs.errorCount).toBe(1);
expect(isReactive(logs.entries[0])).toBe(false);
const previous = logs.entries;
logs.add("INFO", "test", "new entry");
expect(previous.at(-1)?.backendID).toBe(4000);
stop();
});
test("清空时取消待发布日志,延迟历史不会重新出现", () => {
vi.useFakeTimers();
const logs = useLogStore();
logs.enqueueBackendLogs([backend(10), backend(11)]);
logs.clear();
logs.enqueueBackendLogs([backend(10), backend(11), backend(12)]);
vi.runAllTimers();
expect(logs.entries.map((entry) => entry.backendID)).toEqual([12]);
});
test("生产日志保留操作结果和异常,跳过 DEBUG 与 Go 运行时信息", () => {
vi.stubEnv("DEV", false);
vi.stubEnv("MODE", "production");
const logs = useLogStore();
const format = vi.fn(() => "debug details");
logs.add("DEBUG", "performance", { toJSON: format });
logs.ingest([
backend(1, "DEBUG"), backend(2, "INFO"), backend(3, "WARN"), backend(4, "ERROR"),
{ ...backend(5, "INFO"), source: "archive:saved" },
{ ...backend(6, "INFO"), source: undefined },
]);
expect(format).not.toHaveBeenCalled();
expect(logs.entries.map((entry) => entry.backendID)).toEqual([3, 4, 5]);
});