Files
remoteconn-gitea/apps/miniprogram/utils/storage.test.ts
2026-03-21 18:57:10 +08:00

368 lines
11 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
type StorageValue = string | number | boolean | null | undefined | Record<string, unknown> | unknown[];
function createWxStorage(initial: Record<string, StorageValue>) {
const store = new Map<string, StorageValue>(Object.entries(initial));
return {
store,
getStorageSync(key: string) {
return store.get(key);
},
setStorageSync(key: string, value: StorageValue) {
store.set(key, value);
},
getStorageInfoSync() {
return { keys: Array.from(store.keys()) };
}
};
}
function loadStorageModule() {
const modulePath = require.resolve("./storage.js");
delete require.cache[modulePath];
return require("./storage.js");
}
describe("miniprogram storage bootstrap", () => {
beforeEach(() => {
vi.restoreAllMocks();
});
it("当前正式 key 缺失时可从稳定备份恢复服务器列表", () => {
const backupServers = [
{
id: "srv-backup-1",
name: "backup",
host: "127.0.0.1",
port: 22,
username: "root",
authType: "password",
projectPath: "~/workspace",
timeoutSeconds: 20,
heartbeatSeconds: 15,
transportMode: "gateway",
tags: ["prod"],
jumpHost: {},
sortOrder: 1,
lastConnectedAt: ""
}
];
const wxStorage = createWxStorage({
"remoteconn.backup.servers": backupServers
});
(global as typeof globalThis & { wx: typeof wxStorage }).wx = wxStorage;
const storage = loadStorageModule();
const rows = storage.listServers();
expect(rows).toHaveLength(1);
expect(rows[0].id).toBe("srv-backup-1");
expect(wxStorage.store.get("remoteconn.servers.v2")).toEqual(backupServers);
});
it("当前正式 key 与备份都缺失时可从历史 key 迁移记录数据", () => {
const legacyRecords = [
{
id: "rec-legacy-1",
content: "legacy note",
serverId: "srv-legacy-1",
createdAt: "2026-03-09T10:00:00.000Z"
}
];
const wxStorage = createWxStorage({
"remoteconn.records.v1": legacyRecords
});
(global as typeof globalThis & { wx: typeof wxStorage }).wx = wxStorage;
const storage = loadStorageModule();
const rows = storage.listRecords();
expect(rows).toHaveLength(1);
expect(rows[0]).toMatchObject({
id: "rec-legacy-1",
content: "legacy note",
serverId: "srv-legacy-1"
});
expect(wxStorage.store.get("remoteconn.records.v2")).toEqual(rows);
expect(wxStorage.store.get("remoteconn.backup.records")).toEqual(rows);
});
it("闪念处理状态会归一化并按互斥规则稳定写回", () => {
const wxStorage = createWxStorage({
"remoteconn.records.v2": [
{
id: "rec-1",
content: "待处理事项",
category: "问题",
createdAt: "2026-03-10T10:00:00.000Z",
updatedAt: "2026-03-10T10:00:00.000Z"
}
]
});
(global as typeof globalThis & { wx: typeof wxStorage }).wx = wxStorage;
const storage = loadStorageModule();
const normalized = storage.listRecords();
expect(normalized[0]?.processed).toBe(false);
expect(normalized[0]?.discarded).toBe(false);
const processed = storage.updateRecord({
id: "rec-1",
content: "待处理事项",
category: "问题",
processed: true
});
expect(processed?.processed).toBe(true);
expect(processed?.discarded).toBe(false);
const discarded = storage.updateRecord({
id: "rec-1",
content: "待处理事项",
category: "问题",
discarded: true
});
expect(discarded?.processed).toBe(false);
expect(discarded?.discarded).toBe(true);
expect(storage.listRecords()[0]?.processed).toBe(false);
expect(storage.listRecords()[0]?.discarded).toBe(true);
expect(wxStorage.store.get("remoteconn.records.v2")).toEqual(storage.listRecords());
});
it("写入正式 key 时会同步刷新稳定备份", () => {
const wxStorage = createWxStorage({});
(global as typeof globalThis & { wx: typeof wxStorage }).wx = wxStorage;
const storage = loadStorageModule();
storage.saveSettings({
uiThemeMode: "light",
uiBgColor: "#f4f1de",
uiTextColor: "#222222",
uiBtnColor: "#111111",
uiAccentColor: "#123456"
});
expect(wxStorage.store.get("remoteconn.settings.v2")).toEqual(
wxStorage.store.get("remoteconn.backup.settings")
);
});
it("syncConfigEnabled 关闭后可稳定读回", () => {
const wxStorage = createWxStorage({});
(global as typeof globalThis & { wx: typeof wxStorage }).wx = wxStorage;
const storage = loadStorageModule();
storage.saveSettings({
syncConfigEnabled: false
});
expect(storage.getSettings().syncConfigEnabled).toBe(false);
});
it("showVoiceInputButton 关闭后可稳定读回", () => {
const wxStorage = createWxStorage({});
(global as typeof globalThis & { wx: typeof wxStorage }).wx = wxStorage;
const storage = loadStorageModule();
expect(storage.getSettings().showVoiceInputButton).toBe(true);
storage.saveSettings({
showVoiceInputButton: false
});
expect(storage.getSettings().showVoiceInputButton).toBe(false);
});
it("ttsSpeakableMaxChars 会按边界归一化并稳定读回", () => {
const wxStorage = createWxStorage({});
(global as typeof globalThis & { wx: typeof wxStorage }).wx = wxStorage;
const storage = loadStorageModule();
expect(storage.getSettings().ttsSpeakableMaxChars).toBe(500);
expect(storage.getSettings().ttsSegmentMaxChars).toBe(80);
storage.saveSettings({
ttsSpeakableMaxChars: 50
});
expect(storage.getSettings().ttsSpeakableMaxChars).toBe(120);
storage.saveSettings({
ttsSpeakableMaxChars: 500
});
expect(storage.getSettings().ttsSpeakableMaxChars).toBe(500);
storage.saveSettings({
ttsSpeakableMaxChars: 5000
});
expect(storage.getSettings().ttsSpeakableMaxChars).toBe(1200);
storage.saveSettings({
ttsSegmentMaxChars: 10
});
expect(storage.getSettings().ttsSegmentMaxChars).toBe(40);
storage.saveSettings({
ttsSegmentMaxChars: 80
});
expect(storage.getSettings().ttsSegmentMaxChars).toBe(80);
storage.saveSettings({
ttsSegmentMaxChars: 1000
});
expect(storage.getSettings().ttsSegmentMaxChars).toBe(200);
});
it("AI 连接默认模式会稳定读回,非法值会回退到默认档位", () => {
const wxStorage = createWxStorage({});
(global as typeof globalThis & { wx: typeof wxStorage }).wx = wxStorage;
const storage = loadStorageModule();
expect(storage.getSettings().aiDefaultProvider).toBe("codex");
expect(storage.getSettings().aiCodexSandboxMode).toBe("workspace-write");
expect(storage.getSettings().aiCopilotPermissionMode).toBe("default");
storage.saveSettings({
aiDefaultProvider: "copilot",
aiCodexSandboxMode: "read-only",
aiCopilotPermissionMode: "experimental"
});
expect(storage.getSettings().aiDefaultProvider).toBe("copilot");
expect(storage.getSettings().aiCodexSandboxMode).toBe("read-only");
expect(storage.getSettings().aiCopilotPermissionMode).toBe("experimental");
storage.saveSettings({
aiDefaultProvider: "invalid",
aiCodexSandboxMode: "invalid",
aiCopilotPermissionMode: "invalid"
});
expect(storage.getSettings().aiDefaultProvider).toBe("codex");
expect(storage.getSettings().aiCodexSandboxMode).toBe("workspace-write");
expect(storage.getSettings().aiCopilotPermissionMode).toBe("default");
});
it("续接快照行数会按边界归一化后稳定读回", () => {
const wxStorage = createWxStorage({});
(global as typeof globalThis & { wx: typeof wxStorage }).wx = wxStorage;
const storage = loadStorageModule();
expect(storage.getSettings().shellBufferSnapshotMaxLines).toBe(
storage.DEFAULT_TERMINAL_BUFFER_SNAPSHOT_MAX_LINES
);
storage.saveSettings({
shellBufferSnapshotMaxLines: storage.MIN_TERMINAL_BUFFER_SNAPSHOT_MAX_LINES - 1
});
expect(storage.getSettings().shellBufferSnapshotMaxLines).toBe(
storage.MIN_TERMINAL_BUFFER_SNAPSHOT_MAX_LINES
);
storage.saveSettings({
shellBufferSnapshotMaxLines: storage.MAX_TERMINAL_BUFFER_SNAPSHOT_MAX_LINES + 1
});
expect(storage.getSettings().shellBufferSnapshotMaxLines).toBe(
storage.MAX_TERMINAL_BUFFER_SNAPSHOT_MAX_LINES
);
});
it("uiLanguage 会回退到合法值并稳定读回", () => {
const wxStorage = createWxStorage({});
(global as typeof globalThis & { wx: typeof wxStorage }).wx = wxStorage;
const storage = loadStorageModule();
expect(storage.getSettings().uiLanguage).toBe("zh-Hans");
storage.saveSettings({
uiLanguage: "ja"
});
expect(storage.getSettings().uiLanguage).toBe("ja");
storage.saveSettings({
uiLanguage: "ko"
});
expect(storage.getSettings().uiLanguage).toBe("ko");
storage.saveSettings({
uiLanguage: "invalid-language"
});
expect(storage.getSettings().uiLanguage).toBe("zh-Hans");
});
it("历史服务器脏数据会先归一化,再参与同步上传", () => {
const wxStorage = createWxStorage({
"remoteconn.servers.v2": [
{
id: "srv-legacy-1",
name: null,
tags: ["prod", "", "prod"],
host: " 10.0.0.8 ",
port: "22",
username: null,
authType: "key",
password: null,
privateKey: null,
passphrase: null,
certificate: null,
projectPath: null,
timeoutSeconds: "30",
heartbeatSeconds: "12",
transportMode: null,
jumpHost: {
enabled: true,
host: " jump.local ",
port: "2222",
username: null,
authType: "key"
},
jumpPassword: null,
jumpPrivateKey: null,
jumpPassphrase: null,
jumpCertificate: null,
sortOrder: "9",
lastConnectedAt: null,
updatedAt: ""
}
]
});
(global as typeof globalThis & { wx: typeof wxStorage }).wx = wxStorage;
const storage = loadStorageModule();
const rows = storage.listServers();
expect(rows).toHaveLength(1);
expect(rows[0]).toMatchObject({
id: "srv-legacy-1",
name: "",
tags: ["prod"],
host: "10.0.0.8",
port: 22,
username: "",
authType: "password",
password: "",
privateKey: "",
passphrase: "",
certificate: "",
projectPath: "",
timeoutSeconds: 30,
heartbeatSeconds: 12,
transportMode: "gateway",
jumpHost: {
enabled: true,
host: "jump.local",
port: 2222,
username: "",
authType: "password"
},
jumpPassword: "",
jumpPrivateKey: "",
jumpPassphrase: "",
jumpCertificate: "",
sortOrder: 9,
lastConnectedAt: ""
});
expect(typeof rows[0].updatedAt).toBe("string");
expect(rows[0].updatedAt.length).toBeGreaterThan(0);
});
});