mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
Matrix: harden timer-driven tests
This commit is contained in:
@@ -79,6 +79,37 @@ describe("matrix monitor handler pairing account scope", () => {
|
||||
expect(readAllowFromStore).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("refreshes the account-scoped allowFrom cache after its ttl expires", async () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-03-01T10:00:00.000Z"));
|
||||
try {
|
||||
const readAllowFromStore = vi.fn(async () => [] as string[]);
|
||||
const { handler } = createMatrixHandlerTestHarness({
|
||||
readAllowFromStore,
|
||||
dmPolicy: "pairing",
|
||||
buildPairingReply: () => "pairing",
|
||||
});
|
||||
|
||||
const makeEvent = (id: string): MatrixRawEvent =>
|
||||
createMatrixTextMessageEvent({
|
||||
eventId: id,
|
||||
body: "hello",
|
||||
mentions: { room: true },
|
||||
});
|
||||
|
||||
await handler("!room:example.org", makeEvent("$event1"));
|
||||
await handler("!room:example.org", makeEvent("$event2"));
|
||||
expect(readAllowFromStore).toHaveBeenCalledTimes(1);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(30_001);
|
||||
await handler("!room:example.org", makeEvent("$event3"));
|
||||
|
||||
expect(readAllowFromStore).toHaveBeenCalledTimes(2);
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
it("sends pairing reminders for pending requests with cooldown", async () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-03-01T10:00:00.000Z"));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { ensureMatrixStartupVerification } from "./startup-verification.js";
|
||||
|
||||
function createTempStateDir(): string {
|
||||
@@ -79,10 +79,6 @@ function createHarness(params?: {
|
||||
};
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
describe("ensureMatrixStartupVerification", () => {
|
||||
it("skips automatic requests when the device is already verified", async () => {
|
||||
const tempHome = createTempStateDir();
|
||||
@@ -145,16 +141,15 @@ describe("ensureMatrixStartupVerification", () => {
|
||||
});
|
||||
|
||||
it("respects the startup verification cooldown", async () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-03-08T12:00:00.000Z"));
|
||||
const tempHome = createTempStateDir();
|
||||
const harness = createHarness();
|
||||
const initialNowMs = Date.parse("2026-03-08T12:00:00.000Z");
|
||||
await ensureMatrixStartupVerification({
|
||||
client: harness.client as never,
|
||||
auth: createAuth(),
|
||||
accountConfig: {},
|
||||
stateFilePath: createStateFilePath(tempHome),
|
||||
nowMs: Date.now(),
|
||||
nowMs: initialNowMs,
|
||||
});
|
||||
expect(harness.client.crypto.requestVerification).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -163,7 +158,7 @@ describe("ensureMatrixStartupVerification", () => {
|
||||
auth: createAuth(),
|
||||
accountConfig: {},
|
||||
stateFilePath: createStateFilePath(tempHome),
|
||||
nowMs: Date.now() + 60_000,
|
||||
nowMs: initialNowMs + 60_000,
|
||||
});
|
||||
|
||||
expect(second.kind).toBe("cooldown");
|
||||
|
||||
@@ -40,6 +40,24 @@ describe("matrix thread bindings", () => {
|
||||
accessToken: "token",
|
||||
} as const;
|
||||
|
||||
function resolveBindingsFilePath() {
|
||||
return path.join(
|
||||
resolveMatrixStoragePaths({
|
||||
...auth,
|
||||
env: process.env,
|
||||
}).rootDir,
|
||||
"thread-bindings.json",
|
||||
);
|
||||
}
|
||||
|
||||
async function readPersistedLastActivityAt(bindingsPath: string) {
|
||||
const raw = await fs.readFile(bindingsPath, "utf-8");
|
||||
const parsed = JSON.parse(raw) as {
|
||||
bindings?: Array<{ lastActivityAt?: number }>;
|
||||
};
|
||||
return parsed.bindings?.[0]?.lastActivityAt;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
stateDir = await fs.mkdtemp(path.join(os.tmpdir(), "matrix-thread-bindings-"));
|
||||
__testing.resetSessionBindingAdaptersForTests();
|
||||
@@ -274,6 +292,50 @@ describe("matrix thread bindings", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("persists the latest touched activity only after the debounce window", async () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-03-06T10:00:00.000Z"));
|
||||
try {
|
||||
await createMatrixThreadBindingManager({
|
||||
accountId: "ops",
|
||||
auth,
|
||||
client: {} as never,
|
||||
idleTimeoutMs: 24 * 60 * 60 * 1000,
|
||||
maxAgeMs: 0,
|
||||
enableSweeper: false,
|
||||
});
|
||||
const binding = await getSessionBindingService().bind({
|
||||
targetSessionKey: "agent:ops:subagent:child",
|
||||
targetKind: "subagent",
|
||||
conversation: {
|
||||
channel: "matrix",
|
||||
accountId: "ops",
|
||||
conversationId: "$thread",
|
||||
parentConversationId: "!room:example",
|
||||
},
|
||||
placement: "current",
|
||||
});
|
||||
|
||||
const bindingsPath = resolveBindingsFilePath();
|
||||
const originalLastActivityAt = await readPersistedLastActivityAt(bindingsPath);
|
||||
const firstTouchedAt = Date.parse("2026-03-06T10:05:00.000Z");
|
||||
const secondTouchedAt = Date.parse("2026-03-06T10:10:00.000Z");
|
||||
|
||||
getSessionBindingService().touch(binding.bindingId, firstTouchedAt);
|
||||
getSessionBindingService().touch(binding.bindingId, secondTouchedAt);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(29_000);
|
||||
expect(await readPersistedLastActivityAt(bindingsPath)).toBe(originalLastActivityAt);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(1_000);
|
||||
await vi.waitFor(async () => {
|
||||
expect(await readPersistedLastActivityAt(bindingsPath)).toBe(secondTouchedAt);
|
||||
});
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
it("flushes pending touch persistence on stop", async () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-03-06T10:00:00.000Z"));
|
||||
@@ -303,19 +365,9 @@ describe("matrix thread bindings", () => {
|
||||
manager.stop();
|
||||
vi.useRealTimers();
|
||||
|
||||
const bindingsPath = path.join(
|
||||
resolveMatrixStoragePaths({
|
||||
...auth,
|
||||
env: process.env,
|
||||
}).rootDir,
|
||||
"thread-bindings.json",
|
||||
);
|
||||
const bindingsPath = resolveBindingsFilePath();
|
||||
await vi.waitFor(async () => {
|
||||
const raw = await fs.readFile(bindingsPath, "utf-8");
|
||||
const parsed = JSON.parse(raw) as {
|
||||
bindings?: Array<{ lastActivityAt?: number }>;
|
||||
};
|
||||
expect(parsed.bindings?.[0]?.lastActivityAt).toBe(touchedAt);
|
||||
expect(await readPersistedLastActivityAt(bindingsPath)).toBe(touchedAt);
|
||||
});
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
|
||||
Reference in New Issue
Block a user