test: fix no-isolate integration regressions

This commit is contained in:
Peter Steinberger
2026-03-22 20:40:32 +00:00
parent 773fb9cead
commit 7a3346871e
4 changed files with 70 additions and 114 deletions

View File

@@ -2,20 +2,38 @@ import { Command } from "commander";
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { captureEnv } from "../test-utils/env.js";
const loadConfigMock = vi.fn();
const readConfigFileSnapshotMock = vi.fn();
const resolveGatewayPortMock = vi.fn(() => 18789);
const copyToClipboardMock = vi.fn(async () => false);
const loadConfigMock = vi.hoisted(() => vi.fn());
const readConfigFileSnapshotMock = vi.hoisted(() => vi.fn());
const resolveGatewayPortMock = vi.hoisted(() => vi.fn(() => 18789));
const copyToClipboardMock = vi.hoisted(() => vi.fn(async () => false));
const runtimeLogs: string[] = [];
const runtimeErrors: string[] = [];
const runtime = {
const runtime = vi.hoisted(() => ({
log: (message: string) => runtimeLogs.push(message),
error: (message: string) => runtimeErrors.push(message),
exit: (code: number) => {
throw new Error(`__exit__:${code}`);
},
};
}));
vi.mock("../config/config.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../config/config.js")>();
return {
...actual,
loadConfig: loadConfigMock,
readConfigFileSnapshot: readConfigFileSnapshotMock,
resolveGatewayPort: resolveGatewayPortMock,
};
});
vi.mock("../infra/clipboard.js", () => ({
copyToClipboard: copyToClipboardMock,
}));
vi.mock("../runtime.js", () => ({
defaultRuntime: runtime,
}));
function createGatewayTokenRefFixture() {
return {
@@ -75,29 +93,12 @@ async function withCliModules<T>(
}) => Promise<T>,
): Promise<T> {
vi.resetModules();
vi.doMock("../config/config.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../config/config.js")>();
return {
...actual,
loadConfig: loadConfigMock,
readConfigFileSnapshot: readConfigFileSnapshotMock,
resolveGatewayPort: resolveGatewayPortMock,
};
});
vi.doMock("../infra/clipboard.js", () => ({
copyToClipboard: copyToClipboardMock,
}));
vi.doMock("../runtime.js", () => ({
defaultRuntime: runtime,
}));
try {
const { registerQrCli } = await import("./qr-cli.js");
const { registerMaintenanceCommands } = await import("./program/register.maintenance.js");
return await run({ registerQrCli, registerMaintenanceCommands });
} finally {
vi.doUnmock("../config/config.js");
vi.doUnmock("../infra/clipboard.js");
vi.doUnmock("../runtime.js");
vi.restoreAllMocks();
}
}

View File

@@ -1,22 +1,13 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { parseTelegramTarget } from "../../extensions/telegram/src/targets.js";
import type { OpenClawConfig } from "../config/config.js";
// Mock session store so we can control what entries exist.
const mockStore: Record<string, Record<string, unknown>> = {};
type DeliveryTargetModule = typeof import("./isolated-agent/delivery-target.js");
let resolveDeliveryTarget: DeliveryTargetModule["resolveDeliveryTarget"];
beforeEach(() => {
vi.clearAllMocks();
for (const key of Object.keys(mockStore)) {
delete mockStore[key];
}
});
async function withResolveDeliveryTarget<T>(
run: (resolveDeliveryTarget: DeliveryTargetModule["resolveDeliveryTarget"]) => Promise<T>,
): Promise<T> {
vi.resetModules();
beforeAll(async () => {
vi.doMock("../config/sessions.js", () => ({
loadSessionStore: vi.fn((storePath: string) => mockStore[storePath] ?? {}),
resolveAgentMainSessionKey: vi.fn(
@@ -48,17 +39,22 @@ async function withResolveDeliveryTarget<T>(
})),
normalizeChannelId: vi.fn((id: string) => id),
}));
try {
const { resolveDeliveryTarget } = await import("./isolated-agent/delivery-target.js");
return await run(resolveDeliveryTarget);
} finally {
vi.restoreAllMocks();
vi.doUnmock("../config/sessions.js");
vi.doUnmock("../infra/outbound/channel-selection.js");
vi.doUnmock("../channels/plugins/index.js");
vi.resetModules();
({ resolveDeliveryTarget } = await import("./isolated-agent/delivery-target.js"));
});
afterAll(() => {
vi.doUnmock("../config/sessions.js");
vi.doUnmock("../infra/outbound/channel-selection.js");
vi.doUnmock("../channels/plugins/index.js");
vi.resetModules();
});
beforeEach(() => {
vi.clearAllMocks();
for (const key of Object.keys(mockStore)) {
delete mockStore[key];
}
}
});
describe("resolveDeliveryTarget thread session lookup", () => {
const cfg: OpenClawConfig = {};
@@ -80,13 +76,10 @@ describe("resolveDeliveryTarget thread session lookup", () => {
},
};
const result = await withResolveDeliveryTarget(
async (resolveDeliveryTarget) =>
await resolveDeliveryTarget(cfg, "main", {
channel: "last",
sessionKey: "agent:main:main:thread:9999",
}),
);
const result = await resolveDeliveryTarget(cfg, "main", {
channel: "last",
sessionKey: "agent:main:main:thread:9999",
});
expect(result.to).toBe("-100111");
expect(result.threadId).toBe(9999);
@@ -103,13 +96,10 @@ describe("resolveDeliveryTarget thread session lookup", () => {
},
};
const result = await withResolveDeliveryTarget(
async (resolveDeliveryTarget) =>
await resolveDeliveryTarget(cfg, "main", {
channel: "last",
sessionKey: "agent:main:main:thread:nonexistent",
}),
);
const result = await resolveDeliveryTarget(cfg, "main", {
channel: "last",
sessionKey: "agent:main:main:thread:nonexistent",
});
expect(result.to).toBe("-100222");
expect(result.threadId).toBeUndefined();
@@ -126,12 +116,9 @@ describe("resolveDeliveryTarget thread session lookup", () => {
},
};
const result = await withResolveDeliveryTarget(
async (resolveDeliveryTarget) =>
await resolveDeliveryTarget(cfg, "main", {
channel: "last",
}),
);
const result = await resolveDeliveryTarget(cfg, "main", {
channel: "last",
});
expect(result.to).toBe("-100333");
expect(result.threadId).toBeUndefined();
@@ -140,13 +127,10 @@ describe("resolveDeliveryTarget thread session lookup", () => {
it("preserves threadId from :topic: in delivery.to on first run (no session history)", async () => {
mockStore["/mock/store.json"] = {};
const result = await withResolveDeliveryTarget(
async (resolveDeliveryTarget) =>
await resolveDeliveryTarget(cfg, "main", {
channel: "telegram",
to: "63448508:topic:1008013",
}),
);
const result = await resolveDeliveryTarget(cfg, "main", {
channel: "telegram",
to: "63448508:topic:1008013",
});
expect(result.to).toBe("63448508");
expect(result.threadId).toBe(1008013);
@@ -164,14 +148,11 @@ describe("resolveDeliveryTarget thread session lookup", () => {
},
};
const result = await withResolveDeliveryTarget(
async (resolveDeliveryTarget) =>
await resolveDeliveryTarget(cfg, "main", {
channel: "telegram",
to: "-100444",
accountId: "explicit-account",
}),
);
const result = await resolveDeliveryTarget(cfg, "main", {
channel: "telegram",
to: "-100444",
accountId: "explicit-account",
});
expect(result.accountId).toBe("explicit-account");
expect(result.to).toBe("-100444");
@@ -187,13 +168,10 @@ describe("resolveDeliveryTarget thread session lookup", () => {
},
};
const result = await withResolveDeliveryTarget(
async (resolveDeliveryTarget) =>
await resolveDeliveryTarget(cfg, "main", {
channel: "telegram",
to: "63448508:topic:1008013",
}),
);
const result = await resolveDeliveryTarget(cfg, "main", {
channel: "telegram",
to: "63448508:topic:1008013",
});
expect(result.to).toBe("63448508");
expect(result.threadId).toBe(1008013);

View File

@@ -2,10 +2,7 @@ import crypto from "node:crypto";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { afterAll, afterEach, beforeAll, beforeEach, vi } from "vitest";
import { clearAllBootstrapSnapshots } from "../agents/bootstrap-cache.js";
import { clearSessionStoreCacheForTest } from "../config/sessions/store.js";
import { resetAgentRunContextForTest } from "../infra/agent-events.js";
import { afterAll, beforeAll, beforeEach, vi } from "vitest";
import { useFrozenTime, useRealTime } from "../test-utils/frozen-time.js";
import type { CronService } from "./service.js";
import type { CronJob, CronJobState } from "./types.js";
@@ -31,21 +28,11 @@ export function setupCronIssueRegressionFixtures() {
});
beforeEach(() => {
vi.clearAllTimers();
useFrozenTime("2026-02-06T10:05:00.000Z");
});
afterEach(() => {
vi.clearAllTimers();
useRealTime();
clearSessionStoreCacheForTest();
resetAgentRunContextForTest();
clearAllBootstrapSnapshots();
vi.restoreAllMocks();
vi.resetModules();
});
afterAll(async () => {
useRealTime();
await fs.rm(fixtureRoot, { recursive: true, force: true });
});

View File

@@ -1,5 +1,5 @@
import fs from "node:fs/promises";
import { afterEach, describe, expect, it, vi } from "vitest";
import { describe, expect, it, vi } from "vitest";
import type { HeartbeatRunResult } from "../infra/heartbeat-wake.js";
import { clearCommandLane, setCommandLaneConcurrency } from "../process/command-queue.js";
import { CommandLane } from "../process/lanes.js";
@@ -39,16 +39,6 @@ const FAST_TIMEOUT_SECONDS = 0.0025;
describe("Cron issue regressions", () => {
const { makeStorePath } = setupCronIssueRegressionFixtures();
afterEach(() => {
// Shared-state runs can begin collecting the next file before runner-level
// cleanup unwinds this suite's fake timers or command-lane mutations.
vi.clearAllTimers();
vi.useRealTimers();
vi.restoreAllMocks();
clearCommandLane(CommandLane.Cron);
setCommandLaneConcurrency(CommandLane.Cron, 1);
});
it("covers schedule updates and payload patching", async () => {
const store = makeStorePath();
const cron = await startCronForStore({