



























@@ -3,7 +3,8 @@
33 */
44import fs from "node:fs/promises";
55import path from "node:path";
6-import { describe, expect, it } from "vitest";
6+import { afterEach, describe, expect, it } from "vitest";
7+import { resetPluginStateStoreForTests } from "../plugin-state/plugin-state-store.js";
78import {
89appendMemoryHostEvent,
910readMemoryHostEvents,
@@ -18,11 +19,17 @@ function createDedupe(root: string, overrides?: { ttlMs?: number }) {
1819return createPersistentDedupe({
1920ttlMs: overrides?.ttlMs ?? 24 * 60 * 60 * 1000,
2021memoryMaxSize: 100,
21-fileMaxEntries: 1000,
22-resolveFilePath: (namespace) => path.join(root, `${namespace}.json`),
22+pluginId: "test-persistent-dedupe",
23+namespacePrefix: "test-dedupe",
24+stateMaxEntries: 1000,
25+env: { ...process.env, OPENCLAW_STATE_DIR: root },
2326});
2427}
252829+afterEach(() => {
30+resetPluginStateStoreForTests();
31+});
32+2633describe("memory host event journal helpers", () => {
2734it("appends and reads typed workspace events", async () => {
2835const workspaceDir = await createTempDir("memory-host-events-");
@@ -94,8 +101,10 @@ describe("createPersistentDedupe", () => {
94101const dedupe = createPersistentDedupe({
95102ttlMs: Number.NaN,
96103memoryMaxSize: Number.NaN,
97-fileMaxEntries: Number.NaN,
98-resolveFilePath: (namespace) => path.join(root, `${namespace}.json`),
104+pluginId: "test-persistent-dedupe",
105+namespacePrefix: "test-bounds",
106+stateMaxEntries: Number.NaN,
107+env: { ...process.env, OPENCLAW_STATE_DIR: root },
99108});
100109101110expect(await dedupe.checkAndRecord("m1", { namespace: "a", now: 100 })).toBe(true);
@@ -104,33 +113,32 @@ describe("createPersistentDedupe", () => {
104113expect(dedupe.memorySize()).toBe(0);
105114});
106115107-it("falls back to memory-only behavior on disk errors", async () => {
116+it("uses legacy JSON paths only as SQLite namespace identifiers", async () => {
117+const root = await createTempDir("openclaw-legacy-dedupe-");
118+const legacyPath = path.join(root, "legacy.json");
108119const dedupe = createPersistentDedupe({
109120ttlMs: 10_000,
110121memoryMaxSize: 100,
111122fileMaxEntries: 1000,
112-resolveFilePath: () => path.join("/dev/null", "dedupe.json"),
123+resolveFilePath: () => legacyPath,
124+env: { ...process.env, OPENCLAW_STATE_DIR: root },
113125});
114126115-expect(await dedupe.checkAndRecord("memory-only", { namespace: "x" })).toBe(true);
116-expect(await dedupe.checkAndRecord("memory-only", { namespace: "x" })).toBe(false);
127+expect(await dedupe.checkAndRecord("sqlite-only", { namespace: "x" })).toBe(true);
128+expect(await dedupe.checkAndRecord("sqlite-only", { namespace: "x" })).toBe(false);
129+await expect(fs.access(legacyPath)).rejects.toThrow();
117130});
118131119-it("warms empty namespaces and skips expired disk entries", async () => {
132+it("warms empty namespaces and ignores retired JSON cache files", async () => {
120133const root = await createTempDir("openclaw-dedupe-");
121134const emptyReader = createDedupe(root, { ttlMs: 10_000 });
122135expect(await emptyReader.warmup("nonexistent")).toBe(0);
123136124-const oldNow = Date.now() - 2000;
125-await fs.writeFile(
126-path.join(root, "acct.json"),
127-JSON.stringify({ "old-msg": oldNow, "new-msg": Date.now() }),
128-);
137+await fs.writeFile(path.join(root, "acct.json"), JSON.stringify({ "retired-msg": Date.now() }));
129138130139const reader = createDedupe(root, { ttlMs: 1000 });
131-expect(await reader.warmup("acct")).toBe(1);
132-expect(await reader.checkAndRecord("old-msg", { namespace: "acct" })).toBe(true);
133-expect(await reader.checkAndRecord("new-msg", { namespace: "acct" })).toBe(false);
140+expect(await reader.warmup("acct")).toBe(0);
141+expect(await reader.checkAndRecord("retired-msg", { namespace: "acct" })).toBe(true);
134142});
135143});
136144@@ -189,8 +197,10 @@ describe("createClaimableDedupe", () => {
189197const writer = createClaimableDedupe({
190198ttlMs: 10_000,
191199memoryMaxSize: 100,
192-fileMaxEntries: 1000,
193-resolveFilePath: (namespace) => path.join(root, `${namespace}.json`),
200+pluginId: "test-claimable-dedupe",
201+namespacePrefix: "test-claimable-dedupe",
202+stateMaxEntries: 1000,
203+env: { ...process.env, OPENCLAW_STATE_DIR: root },
194204});
195205196206await expect(writer.claim("m1", { namespace: "acct" })).resolves.toEqual({ kind: "claimed" });
@@ -199,8 +209,10 @@ describe("createClaimableDedupe", () => {
199209const reader = createClaimableDedupe({
200210ttlMs: 10_000,
201211memoryMaxSize: 100,
202-fileMaxEntries: 1000,
203-resolveFilePath: (namespace) => path.join(root, `${namespace}.json`),
212+pluginId: "test-claimable-dedupe",
213+namespacePrefix: "test-claimable-dedupe",
214+stateMaxEntries: 1000,
215+env: { ...process.env, OPENCLAW_STATE_DIR: root },
204216});
205217206218expect(await reader.hasRecent("m1", { namespace: "acct" })).toBe(true);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。