





















@@ -1,8 +1,12 @@
1-import { mkdirSync, statSync } from "node:fs";
2-import { afterEach, describe, expect, it, vi } from "vitest";
1+import { mkdirSync, rmSync, statSync } from "node:fs";
2+import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
33import { requireNodeSqlite } from "../infra/node-sqlite.js";
4-import { withOpenClawTestState } from "../test-utils/openclaw-test-state.js";
54import {
5+createOpenClawTestState,
6+type OpenClawTestState,
7+} from "../test-utils/openclaw-test-state.js";
8+import {
9+clearPluginStateStoreForTests,
610closePluginStateSqliteStore,
711createCorePluginStateKeyedStore,
812createPluginStateKeyedStore,
@@ -14,11 +18,32 @@ import {
1418import { resolvePluginStateDir, resolvePluginStateSqlitePath } from "./plugin-state-store.paths.js";
1519import { seedPluginStateEntriesForTests } from "./plugin-state-store.test-helpers.js";
162021+let testState: OpenClawTestState | undefined;
22+23+beforeAll(async () => {
24+testState = await createOpenClawTestState({ label: "plugin-state-store" });
25+rmSync(resolvePluginStateDir(), { recursive: true, force: true });
26+});
27+28+beforeEach(() => {
29+testState?.applyEnv();
30+clearPluginStateStoreForTests();
31+});
32+1733afterEach(() => {
1834vi.useRealTimers();
35+resetPluginStateStoreForTests({ closeDatabase: false });
36+});
37+38+afterAll(async () => {
1939resetPluginStateStoreForTests();
40+await testState?.cleanup();
2041});
214243+async function withPluginStateTestState<T>(fn: () => Promise<T>): Promise<T> {
44+return await fn();
45+}
46+2247async function expectPluginStateStoreError(
2348promise: Promise<unknown>,
2449expected: { code: string; operation?: string },
@@ -38,7 +63,7 @@ async function expectPluginStateStoreError(
38633964describe("plugin state keyed store", () => {
4065it("registers and looks up values across store instances", async () => {
41-await withOpenClawTestState({ label: "plugin-state-roundtrip" }, async () => {
66+await withPluginStateTestState(async () => {
4267const store = createPluginStateKeyedStore<{ count: number }>("discord", {
4368namespace: "components",
4469maxEntries: 10,
@@ -54,7 +79,7 @@ describe("plugin state keyed store", () => {
5479});
55805681it("upserts values and refreshes deterministic entry ordering", async () => {
57-await withOpenClawTestState({ label: "plugin-state-upsert" }, async () => {
82+await withPluginStateTestState(async () => {
5883vi.useFakeTimers();
5984const store = createPluginStateKeyedStore<{ version: number }>("discord", {
6085namespace: "components",
@@ -76,7 +101,7 @@ describe("plugin state keyed store", () => {
76101});
7710278103it("registerIfAbsent inserts the first value and preserves live duplicates", async () => {
79-await withOpenClawTestState({ label: "plugin-state-register-if-absent-live" }, async () => {
104+await withPluginStateTestState(async () => {
80105vi.useFakeTimers();
81106const store = createPluginStateKeyedStore<{ version: number }>("discord", {
82107namespace: "claims",
@@ -100,7 +125,7 @@ describe("plugin state keyed store", () => {
100125});
101126102127it("registerIfAbsent replaces expired keys", async () => {
103-await withOpenClawTestState({ label: "plugin-state-register-if-absent-expired" }, async () => {
128+await withPluginStateTestState(async () => {
104129vi.useFakeTimers();
105130const store = createPluginStateKeyedStore<{ version: number }>("discord", {
106131namespace: "claims-expired",
@@ -122,40 +147,33 @@ describe("plugin state keyed store", () => {
122147});
123148124149it("registerIfAbsent keeps plugin and namespace claims isolated", async () => {
125-await withOpenClawTestState(
126-{ label: "plugin-state-register-if-absent-isolation" },
127-async () => {
128-const discordA = createPluginStateKeyedStore<{ owner: string }>("discord", {
129-namespace: "claims-a",
130-maxEntries: 10,
131-});
132-const discordB = createPluginStateKeyedStore<{ owner: string }>("discord", {
133-namespace: "claims-b",
134-maxEntries: 10,
135-});
136-const telegramA = createPluginStateKeyedStore<{ owner: string }>("telegram", {
137-namespace: "claims-a",
138-maxEntries: 10,
139-});
140-141-await expect(discordA.registerIfAbsent("same", { owner: "discord-a" })).resolves.toBe(true);
142-await expect(discordB.registerIfAbsent("same", { owner: "discord-b" })).resolves.toBe(true);
143-await expect(telegramA.registerIfAbsent("same", { owner: "telegram-a" })).resolves.toBe(
144-true,
145-);
146-await expect(discordA.registerIfAbsent("same", { owner: "overwrite" })).resolves.toBe(
147-false,
148-);
149-150-await expect(discordA.lookup("same")).resolves.toEqual({ owner: "discord-a" });
151-await expect(discordB.lookup("same")).resolves.toEqual({ owner: "discord-b" });
152-await expect(telegramA.lookup("same")).resolves.toEqual({ owner: "telegram-a" });
153-},
154-);
150+await withPluginStateTestState(async () => {
151+const discordA = createPluginStateKeyedStore<{ owner: string }>("discord", {
152+namespace: "claims-a",
153+maxEntries: 10,
154+});
155+const discordB = createPluginStateKeyedStore<{ owner: string }>("discord", {
156+namespace: "claims-b",
157+maxEntries: 10,
158+});
159+const telegramA = createPluginStateKeyedStore<{ owner: string }>("telegram", {
160+namespace: "claims-a",
161+maxEntries: 10,
162+});
163+164+await expect(discordA.registerIfAbsent("same", { owner: "discord-a" })).resolves.toBe(true);
165+await expect(discordB.registerIfAbsent("same", { owner: "discord-b" })).resolves.toBe(true);
166+await expect(telegramA.registerIfAbsent("same", { owner: "telegram-a" })).resolves.toBe(true);
167+await expect(discordA.registerIfAbsent("same", { owner: "overwrite" })).resolves.toBe(false);
168+169+await expect(discordA.lookup("same")).resolves.toEqual({ owner: "discord-a" });
170+await expect(discordB.lookup("same")).resolves.toEqual({ owner: "discord-b" });
171+await expect(telegramA.lookup("same")).resolves.toEqual({ owner: "telegram-a" });
172+});
155173});
156174157175it("registerIfAbsent only lets one parallel claimant win", async () => {
158-await withOpenClawTestState({ label: "plugin-state-register-if-absent-race" }, async () => {
176+await withPluginStateTestState(async () => {
159177const store = createPluginStateKeyedStore<{ claimant: number }>("discord", {
160178namespace: "claims-race",
161179maxEntries: 10,
@@ -177,7 +195,7 @@ describe("plugin state keyed store", () => {
177195});
178196179197it("registerIfAbsent preserves eviction and plugin row cap behavior", async () => {
180-await withOpenClawTestState({ label: "plugin-state-register-if-absent-limits" }, async () => {
198+await withPluginStateTestState(async () => {
181199vi.useFakeTimers();
182200const evicting = createPluginStateKeyedStore<number>("discord", {
183201namespace: "claims-evict",
@@ -217,7 +235,7 @@ describe("plugin state keyed store", () => {
217235});
218236219237it("returns undefined for missing lookups and consumes by deleting atomically", async () => {
220-await withOpenClawTestState({ label: "plugin-state-consume" }, async () => {
238+await withPluginStateTestState(async () => {
221239const store = createPluginStateKeyedStore<{ ok: boolean }>("discord", {
222240namespace: "components",
223241maxEntries: 10,
@@ -232,7 +250,7 @@ describe("plugin state keyed store", () => {
232250});
233251234252it("deletes and clears only the targeted namespace", async () => {
235-await withOpenClawTestState({ label: "plugin-state-clear" }, async () => {
253+await withPluginStateTestState(async () => {
236254const first = createPluginStateKeyedStore("discord", { namespace: "a", maxEntries: 10 });
237255const second = createPluginStateKeyedStore("discord", { namespace: "b", maxEntries: 10 });
238256await first.register("k1", { value: 1 });
@@ -249,7 +267,7 @@ describe("plugin state keyed store", () => {
249267});
250268251269it("excludes expired entries and sweeps them", async () => {
252-await withOpenClawTestState({ label: "plugin-state-expiry" }, async () => {
270+await withPluginStateTestState(async () => {
253271vi.useFakeTimers();
254272vi.setSystemTime(1000);
255273const store = createPluginStateKeyedStore("discord", {
@@ -269,7 +287,7 @@ describe("plugin state keyed store", () => {
269287});
270288271289it("evicts oldest live entries over maxEntries", async () => {
272-await withOpenClawTestState({ label: "plugin-state-eviction" }, async () => {
290+await withPluginStateTestState(async () => {
273291vi.useFakeTimers();
274292const store = createPluginStateKeyedStore("discord", { namespace: "evict", maxEntries: 2 });
275293vi.setSystemTime(1000);
@@ -284,7 +302,7 @@ describe("plugin state keyed store", () => {
284302});
285303286304it("keeps the just-registered key when namespace eviction timestamps tie", async () => {
287-await withOpenClawTestState({ label: "plugin-state-eviction-tie-register" }, async () => {
305+await withPluginStateTestState(async () => {
288306vi.useFakeTimers();
289307vi.setSystemTime(1000);
290308const store = createPluginStateKeyedStore<number>("discord", {
@@ -301,7 +319,7 @@ describe("plugin state keyed store", () => {
301319});
302320303321it("keeps a same-millisecond registerIfAbsent claim during namespace eviction", async () => {
304-await withOpenClawTestState({ label: "plugin-state-eviction-tie-claim" }, async () => {
322+await withPluginStateTestState(async () => {
305323vi.useFakeTimers();
306324vi.setSystemTime(1000);
307325const store = createPluginStateKeyedStore<number>("discord", {
@@ -318,7 +336,7 @@ describe("plugin state keyed store", () => {
318336});
319337320338it("rejects when the per-plugin live row ceiling would be exceeded without evicting siblings", async () => {
321-await withOpenClawTestState({ label: "plugin-state-plugin-limit" }, async () => {
339+await withPluginStateTestState(async () => {
322340seedPluginStateEntriesForTests([
323341 ...Array.from({ length: 999 }, (_, entryIndex) => ({
324342pluginId: "discord",
@@ -355,7 +373,7 @@ describe("plugin state keyed store", () => {
355373});
356374357375it("segregates plugins sharing a namespace and key", async () => {
358-await withOpenClawTestState({ label: "plugin-state-segregation" }, async () => {
376+await withPluginStateTestState(async () => {
359377const discord = createPluginStateKeyedStore("discord", { namespace: "same", maxEntries: 10 });
360378const telegram = createPluginStateKeyedStore("telegram", {
361379namespace: "same",
@@ -371,7 +389,7 @@ describe("plugin state keyed store", () => {
371389});
372390373391it("validates namespaces, keys, options, and JSON values before writes", async () => {
374-await withOpenClawTestState({ label: "plugin-state-validation" }, async () => {
392+await withPluginStateTestState(async () => {
375393expect(() =>
376394createPluginStateKeyedStore("discord", { namespace: "../bad", maxEntries: 10 }),
377395).toThrow(PluginStateStoreError);
@@ -436,7 +454,7 @@ describe("plugin state keyed store", () => {
436454});
437455438456it("rejects reopening the same namespace with incompatible options", async () => {
439-await withOpenClawTestState({ label: "plugin-state-option-consistency" }, async () => {
457+await withPluginStateTestState(async () => {
440458createPluginStateKeyedStore("discord", { namespace: "same", maxEntries: 10 });
441459expect(() =>
442460createPluginStateKeyedStore("discord", { namespace: "same", maxEntries: 11 }),
@@ -445,7 +463,7 @@ describe("plugin state keyed store", () => {
445463});
446464447465it("allows core owners and reserves core-prefixed plugin ids", async () => {
448-await withOpenClawTestState({ label: "plugin-state-core" }, async () => {
466+await withPluginStateTestState(async () => {
449467const store = createCorePluginStateKeyedStore<{ stopped: boolean }>({
450468ownerId: "core:channel-intent",
451469namespace: "stopped",
@@ -460,7 +478,7 @@ describe("plugin state keyed store", () => {
460478});
461479462480it("closes the cached DB handle and reopens cleanly", async () => {
463-await withOpenClawTestState({ label: "plugin-state-close" }, async () => {
481+await withPluginStateTestState(async () => {
464482const store = createPluginStateKeyedStore("discord", { namespace: "close", maxEntries: 10 });
465483await store.register("k", { ok: true });
466484closePluginStateSqliteStore();
@@ -469,7 +487,7 @@ describe("plugin state keyed store", () => {
469487});
470488471489it.runIf(process.platform !== "win32")("hardens DB directory and file permissions", async () => {
472-await withOpenClawTestState({ label: "plugin-state-permissions" }, async () => {
490+await withPluginStateTestState(async () => {
473491const store = createPluginStateKeyedStore("discord", { namespace: "perms", maxEntries: 10 });
474492await store.register("k", { ok: true });
475493@@ -479,7 +497,7 @@ describe("plugin state keyed store", () => {
479497});
480498481499it("reports healthy diagnostics without stored values", async () => {
482-await withOpenClawTestState({ label: "plugin-state-probe" }, async () => {
500+await withPluginStateTestState(async () => {
483501const result = probePluginStateStore();
484502expect(result.ok).toBe(true);
485503const failedSteps = result.steps.filter((step) => !step.ok);
@@ -489,7 +507,8 @@ describe("plugin state keyed store", () => {
489507});
490508491509it("throws on unsupported future schema versions", async () => {
492-await withOpenClawTestState({ label: "plugin-state-schema" }, async () => {
510+await withPluginStateTestState(async () => {
511+closePluginStateSqliteStore();
493512mkdirSync(resolvePluginStateDir(), { recursive: true });
494513const { DatabaseSync } = requireNodeSqlite();
495514const db = new DatabaseSync(resolvePluginStateSqlitePath());
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。