import { describe, expect, it } from "vitest"; import { buildRedisKeyTree, createKeyCommand, isDangerousRedis, quoteRedisArg, redisCmd, redisLeafLabel, removeRedisKeyFromTree, splitRedisCommands, tryPrettyJson, tsLabel, } from "./redis"; describe("buildRedisKeyTree", () => { it("按 : 编文件夹,叶子保留全名", () => { const tree = buildRedisKeyTree( [ { name: "CEX:FOLLOW:FOLLOWERS:FUTURE:97865651", type: "hash", ttl: -1 }, { name: "CEX:FOLLOW:TRADERS:1", type: "hash", ttl: -1 }, { name: "LANG", type: "string", ttl: 12 }, ], ":", ); expect(tree.map((n) => n.name)).toEqual(["CEX", "LANG"]); const cex = tree[0]; expect(cex.kind).toBe("folder"); expect(cex.count).toBe(2); expect(cex.fullName).toBe("CEX:"); const follow = cex.children?.[0]; expect(follow?.name).toBe("FOLLOW"); const leaves = (follow?.children ?? []).flatMap((n) => n.kind === "folder" ? n.children ?? [] : [n], ); expect(leaves.some((n) => n.name === "CEX:FOLLOW:TRADERS:1")).toBe(true); }); it("无分隔符时保持扁平", () => { const tree = buildRedisKeyTree( [ { name: "alpha", type: "string", ttl: -1 }, { name: "beta", type: "hash", ttl: 3 }, ], ":", ); expect(tree.every((n) => n.kind === "key")).toBe(true); expect(tree.map((n) => n.name)).toEqual(["alpha", "beta"]); }); it("删除键后收起空文件夹", () => { const tree = buildRedisKeyTree( [ { name: "a:b:c", type: "string", ttl: -1 }, { name: "a:x", type: "string", ttl: -1 }, ], ":", ); const next = removeRedisKeyFromTree(tree, "a:b:c"); expect(next).toHaveLength(1); expect(next[0].kind).toBe("folder"); expect(next[0].count).toBe(1); expect(next[0].children?.[0].name).toBe("a:x"); }); }); describe("quote / cmd", () => { it("空白与引号转义", () => { expect(quoteRedisArg("abc")).toBe("abc"); expect(quoteRedisArg("a b")).toBe('"a b"'); expect(quoteRedisArg('say "hi"')).toBe('"say \\"hi\\""'); expect(redisCmd("SET", "k", "v w")).toBe('SET k "v w"'); }); it("新建键命令", () => { expect(createKeyCommand("string", "demo", "x")).toBe("SET demo x"); expect(createKeyCommand("hash", "h", "v")).toBe("HSET h field v"); }); }); describe("splitRedisCommands", () => { it("按行切,跳过空行和 # 注释", () => { const parts = splitRedisCommands("GET a\n\n# skip\nHGETALL b"); expect(parts.map((p) => p.text)).toEqual(["GET a", "HGETALL b"]); }); it("不把分号当分隔", () => { const parts = splitRedisCommands('SET k "a;b"'); expect(parts).toHaveLength(1); expect(parts[0].text).toBe('SET k "a;b"'); }); }); describe("misc", () => { it("危险命令", () => { expect(isDangerousRedis("FLUSHALL")).toBe(true); expect(isDangerousRedis("GET k")).toBe(false); expect(isDangerousRedis("keys *")).toBe(true); }); it("JSON 美化", () => { expect(tryPrettyJson('{"a":1}')).toBe('{\n "a": 1\n}'); expect(tryPrettyJson("not-json")).toBeNull(); }); it("leaf label", () => { expect(redisLeafLabel("a:b:c", ":")).toBe("c"); expect(redisLeafLabel("plain", ":")).toBe("plain"); }); }); describe("tsLabel", () => { it("毫秒时间戳给可读时间", () => { expect(tsLabel(1782230400000)).toMatch(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/); }); it("秒时间戳给可读时间", () => { expect(tsLabel(1782230400)).toMatch(/^\d{4}-/); }); it("普通数字/非法值不误判", () => { expect(tsLabel(10)).toBeNull(); expect(tsLabel(77777777777777)).toBeNull(); expect(tsLabel(Number.NaN)).toBeNull(); }); });