test(telegram): cover topic-name cache reload

This commit is contained in:
Ayaan Zaidi
2026-04-13 23:49:04 +05:30
parent ad181b2361
commit 59afcf9922
2 changed files with 94 additions and 12 deletions

View File

@@ -1,4 +1,8 @@
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { resetTopicNameCacheForTest } from "./topic-name-cache.js";
const { recordInboundSessionMock } = vi.hoisted(() => ({
recordInboundSessionMock: vi.fn().mockResolvedValue(undefined),
}));
@@ -34,10 +38,12 @@ const { clearRuntimeConfigSnapshot, setRuntimeConfigSnapshot } =
beforeEach(() => {
clearRuntimeConfigSnapshot();
resetTopicNameCacheForTest();
});
afterEach(() => {
clearRuntimeConfigSnapshot();
resetTopicNameCacheForTest();
recordInboundSessionMock.mockClear();
});
@@ -161,6 +167,52 @@ describe("buildTelegramMessageContext group sessions without forum", () => {
expect(ctx).not.toBeNull();
expect(ctx?.ctxPayload?.TopicName).toBe("Deployments");
});
it("reloads topic name from disk after cache reset", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-telegram-topic-name-"));
const sessionStorePath = path.join(tempDir, "sessions.json");
const buildPersistedContext = async (message: Record<string, unknown>) =>
await buildTelegramMessageContextForTest({
message,
options: { forceWasMentioned: true },
resolveGroupActivation: () => true,
sessionRuntime: {
resolveStorePath: () => sessionStorePath,
},
});
try {
await buildPersistedContext({
message_id: 4,
chat: { id: -1001234567890, type: "supergroup", title: "Test Forum", is_forum: true },
date: 1700000003,
text: "@bot hello",
message_thread_id: 99,
from: { id: 42, first_name: "Alice" },
reply_to_message: {
message_id: 3,
forum_topic_created: { name: "Deployments", icon_color: 0x6fb9f0 },
},
});
resetTopicNameCacheForTest();
const ctx = await buildPersistedContext({
message_id: 5,
chat: { id: -1001234567890, type: "supergroup", title: "Test Forum", is_forum: true },
date: 1700000004,
text: "@bot again",
message_thread_id: 99,
from: { id: 42, first_name: "Alice" },
});
expect(ctx).not.toBeNull();
expect(ctx?.ctxPayload?.TopicName).toBe("Deployments");
} finally {
await fs.rm(tempDir, { recursive: true, force: true });
resetTopicNameCacheForTest();
}
});
});
describe("buildTelegramMessageContext direct peer routing", () => {

View File

@@ -1,20 +1,21 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import syncFs from "node:fs";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { beforeEach, describe, expect, it, vi } from "vitest";
import {
clearTopicNameCache,
getTopicEntry,
getTopicName,
resetTopicNameCacheForTest,
topicNameCacheSize,
updateTopicName,
} from "./topic-name-cache.js";
describe("topic-name-cache", () => {
beforeEach(() => {
vi.useRealTimers();
clearTopicNameCache();
});
afterEach(() => {
vi.useRealTimers();
resetTopicNameCacheForTest();
});
it("stores and retrieves a topic name", () => {
@@ -63,11 +64,9 @@ describe("topic-name-cache", () => {
});
it("updates timestamps on write", async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-04-13T22:00:00.000Z"));
updateTopicName(-100123, 42, { name: "A" });
const t1 = getTopicEntry(-100123, 42)?.updatedAt ?? 0;
await vi.advanceTimersByTimeAsync(10);
await new Promise((r) => setTimeout(r, 10));
updateTopicName(-100123, 42, { name: "B" });
const t2 = getTopicEntry(-100123, 42)?.updatedAt ?? 0;
expect(t2).toBeGreaterThan(t1);
@@ -88,10 +87,8 @@ describe("topic-name-cache", () => {
});
it("refreshes recency on read so active topics survive eviction", async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-04-13T22:00:00.000Z"));
updateTopicName(-100000, 1, { name: "Active" });
await vi.advanceTimersByTimeAsync(10);
await new Promise((r) => setTimeout(r, 10));
for (let i = 2; i <= 2048; i++) {
updateTopicName(-100000, i, { name: `Topic ${i}` });
}
@@ -100,4 +97,37 @@ describe("topic-name-cache", () => {
expect(getTopicName(-100000, 1)).toBe("Active");
expect(topicNameCacheSize()).toBe(2048);
});
it("reloads persisted entries from disk", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-topic-cache-"));
const persistedPath = path.join(tempDir, "topic-names.json");
try {
updateTopicName(-100123, 42, { name: "Deployments" }, persistedPath);
resetTopicNameCacheForTest();
expect(getTopicName(-100123, 42, persistedPath)).toBe("Deployments");
} finally {
await fs.rm(tempDir, { recursive: true, force: true });
resetTopicNameCacheForTest();
}
});
it("keeps separate in-memory stores for separate persisted paths", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-topic-cache-"));
const firstPath = path.join(tempDir, "first-topic-names.json");
const secondPath = path.join(tempDir, "second-topic-names.json");
try {
updateTopicName(-100123, 42, { name: "Deployments" }, firstPath);
updateTopicName(-200456, 84, { name: "Incidents" }, secondPath);
const readFileSpy = vi.spyOn(syncFs, "readFileSync");
expect(getTopicName(-100123, 42, firstPath)).toBe("Deployments");
expect(getTopicName(-200456, 84, secondPath)).toBe("Incidents");
expect(readFileSpy).not.toHaveBeenCalled();
} finally {
vi.restoreAllMocks();
await fs.rm(tempDir, { recursive: true, force: true });
resetTopicNameCacheForTest();
}
});
});