From 3cb851be9078db567bb56d56a18c23a17abe7d7d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 2 Mar 2026 22:29:04 +0000 Subject: [PATCH] test: micro-optimize heavy gateway/browser/telegram suites --- extensions/acpx/src/runtime.test.ts | 57 +++++----- ...erged-skills-into-target-workspace.test.ts | 51 +++++---- ...skills.buildworkspaceskillsnapshot.test.ts | 52 +++++---- src/browser/extension-relay.test.ts | 106 +++++++++--------- src/gateway/server.auth.shared.ts | 2 + ...sessions.gateway-server-sessions-a.test.ts | 12 +- src/gateway/test-helpers.server.ts | 51 +++++++++ src/telegram/bot.create-telegram-bot.test.ts | 6 +- src/telegram/bot.test.ts | 31 +++-- test/scripts/ios-team-id.test.ts | 17 ++- 10 files changed, 242 insertions(+), 143 deletions(-) diff --git a/extensions/acpx/src/runtime.test.ts b/extensions/acpx/src/runtime.test.ts index 2e773820528..44f02cabd5a 100644 --- a/extensions/acpx/src/runtime.test.ts +++ b/extensions/acpx/src/runtime.test.ts @@ -11,13 +11,28 @@ import { import { AcpxRuntime, decodeAcpxRuntimeHandleState } from "./runtime.js"; let sharedFixture: Awaited> | null = null; +let missingCommandRuntime: AcpxRuntime | null = null; beforeAll(async () => { sharedFixture = await createMockRuntimeFixture(); + missingCommandRuntime = new AcpxRuntime( + { + command: "/definitely/missing/acpx", + allowPluginLocalInstall: false, + installCommand: "n/a", + cwd: process.cwd(), + permissionMode: "approve-reads", + nonInteractivePermissions: "fail", + strictWindowsCmdWrapper: true, + queueOwnerTtlSeconds: 0.1, + }, + { logger: NOOP_LOGGER }, + ); }); afterAll(async () => { sharedFixture = null; + missingCommandRuntime = null; await cleanupMockRuntimeFixtures(); }); @@ -319,22 +334,12 @@ describe("AcpxRuntime", () => { }); it("marks runtime unhealthy when command is missing", async () => { - const runtime = new AcpxRuntime( - { - command: "/definitely/missing/acpx", - allowPluginLocalInstall: false, - installCommand: "n/a", - cwd: process.cwd(), - permissionMode: "approve-reads", - nonInteractivePermissions: "fail", - strictWindowsCmdWrapper: true, - queueOwnerTtlSeconds: 0.1, - }, - { logger: NOOP_LOGGER }, - ); - - await runtime.probeAvailability(); - expect(runtime.isHealthy()).toBe(false); + expect(missingCommandRuntime).toBeDefined(); + if (!missingCommandRuntime) { + throw new Error("missing-command runtime fixture missing"); + } + await missingCommandRuntime.probeAvailability(); + expect(missingCommandRuntime.isHealthy()).toBe(false); }); it("logs ACPX spawn resolution once per command policy", async () => { @@ -363,21 +368,11 @@ describe("AcpxRuntime", () => { }); it("returns doctor report for missing command", async () => { - const runtime = new AcpxRuntime( - { - command: "/definitely/missing/acpx", - allowPluginLocalInstall: false, - installCommand: "n/a", - cwd: process.cwd(), - permissionMode: "approve-reads", - nonInteractivePermissions: "fail", - strictWindowsCmdWrapper: true, - queueOwnerTtlSeconds: 0.1, - }, - { logger: NOOP_LOGGER }, - ); - - const report = await runtime.doctor(); + expect(missingCommandRuntime).toBeDefined(); + if (!missingCommandRuntime) { + throw new Error("missing-command runtime fixture missing"); + } + const report = await missingCommandRuntime.doctor(); expect(report.ok).toBe(false); expect(report.code).toBe("ACP_BACKEND_UNAVAILABLE"); expect(report.installCommand).toContain("acpx"); diff --git a/src/agents/skills.build-workspace-skills-prompt.syncs-merged-skills-into-target-workspace.test.ts b/src/agents/skills.build-workspace-skills-prompt.syncs-merged-skills-into-target-workspace.test.ts index 5a883e181db..cced568ecbc 100644 --- a/src/agents/skills.build-workspace-skills-prompt.syncs-merged-skills-into-target-workspace.test.ts +++ b/src/agents/skills.build-workspace-skills-prompt.syncs-merged-skills-into-target-workspace.test.ts @@ -17,6 +17,7 @@ async function pathExists(filePath: string): Promise { let fixtureRoot = ""; let fixtureCount = 0; +let syncSourceTemplateDir = ""; async function createCaseDir(prefix: string): Promise { const dir = path.join(fixtureRoot, `${prefix}-${fixtureCount++}`); @@ -26,6 +27,27 @@ async function createCaseDir(prefix: string): Promise { beforeAll(async () => { fixtureRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-skills-sync-suite-")); + syncSourceTemplateDir = await createCaseDir("source-template"); + await writeSkill({ + dir: path.join(syncSourceTemplateDir, ".extra", "demo-skill"), + name: "demo-skill", + description: "Extra version", + }); + await writeSkill({ + dir: path.join(syncSourceTemplateDir, ".bundled", "demo-skill"), + name: "demo-skill", + description: "Bundled version", + }); + await writeSkill({ + dir: path.join(syncSourceTemplateDir, ".managed", "demo-skill"), + name: "demo-skill", + description: "Managed version", + }); + await writeSkill({ + dir: path.join(syncSourceTemplateDir, "skills", "demo-skill"), + name: "demo-skill", + description: "Workspace version", + }); }); afterAll(async () => { @@ -39,34 +61,19 @@ describe("buildWorkspaceSkillsPrompt", () => { ) => withEnv({ HOME: workspaceDir, PATH: "" }, () => buildWorkspaceSkillsPrompt(workspaceDir, opts)); - it("syncs merged skills into a target workspace", async () => { + const cloneSourceTemplate = async () => { const sourceWorkspace = await createCaseDir("source"); + await fs.cp(syncSourceTemplateDir, sourceWorkspace, { recursive: true }); + return sourceWorkspace; + }; + + it("syncs merged skills into a target workspace", async () => { + const sourceWorkspace = await cloneSourceTemplate(); const targetWorkspace = await createCaseDir("target"); const extraDir = path.join(sourceWorkspace, ".extra"); const bundledDir = path.join(sourceWorkspace, ".bundled"); const managedDir = path.join(sourceWorkspace, ".managed"); - await writeSkill({ - dir: path.join(extraDir, "demo-skill"), - name: "demo-skill", - description: "Extra version", - }); - await writeSkill({ - dir: path.join(bundledDir, "demo-skill"), - name: "demo-skill", - description: "Bundled version", - }); - await writeSkill({ - dir: path.join(managedDir, "demo-skill"), - name: "demo-skill", - description: "Managed version", - }); - await writeSkill({ - dir: path.join(sourceWorkspace, "skills", "demo-skill"), - name: "demo-skill", - description: "Workspace version", - }); - await withEnv({ HOME: sourceWorkspace, PATH: "" }, () => syncSkillsToWorkspace({ sourceWorkspaceDir: sourceWorkspace, diff --git a/src/agents/skills.buildworkspaceskillsnapshot.test.ts b/src/agents/skills.buildworkspaceskillsnapshot.test.ts index 35b9b93aa0b..aec0da8b49a 100644 --- a/src/agents/skills.buildworkspaceskillsnapshot.test.ts +++ b/src/agents/skills.buildworkspaceskillsnapshot.test.ts @@ -7,9 +7,32 @@ import { writeSkill } from "./skills.e2e-test-helpers.js"; import { buildWorkspaceSkillSnapshot, buildWorkspaceSkillsPrompt } from "./skills.js"; const fixtureSuite = createFixtureSuite("openclaw-skills-snapshot-suite-"); +let truncationWorkspaceTemplateDir = ""; +let nestedRepoTemplateDir = ""; beforeAll(async () => { await fixtureSuite.setup(); + truncationWorkspaceTemplateDir = await fixtureSuite.createCaseDir( + "template-truncation-workspace", + ); + for (let i = 0; i < 8; i += 1) { + const name = `skill-${String(i).padStart(2, "0")}`; + await writeSkill({ + dir: path.join(truncationWorkspaceTemplateDir, "skills", name), + name, + description: "x".repeat(800), + }); + } + + nestedRepoTemplateDir = await fixtureSuite.createCaseDir("template-skills-repo"); + for (let i = 0; i < 8; i += 1) { + const name = `repo-skill-${String(i).padStart(2, "0")}`; + await writeSkill({ + dir: path.join(nestedRepoTemplateDir, "skills", name), + name, + description: `Desc ${i}`, + }); + } }); afterAll(async () => { @@ -20,6 +43,12 @@ function withWorkspaceHome(workspaceDir: string, cb: () => T): T { return withEnv({ HOME: workspaceDir, PATH: "" }, cb); } +async function cloneTemplateDir(templateDir: string, prefix: string): Promise { + const cloned = await fixtureSuite.createCaseDir(prefix); + await fs.cp(templateDir, cloned, { recursive: true }); + return cloned; +} + describe("buildWorkspaceSkillSnapshot", () => { it("returns an empty snapshot when skills dirs are missing", async () => { const workspaceDir = await fixtureSuite.createCaseDir("workspace"); @@ -110,17 +139,7 @@ describe("buildWorkspaceSkillSnapshot", () => { }); it("truncates the skills prompt when it exceeds the configured char budget", async () => { - const workspaceDir = await fixtureSuite.createCaseDir("workspace"); - - // Keep fixture size modest while still forcing truncation logic. - for (let i = 0; i < 8; i += 1) { - const name = `skill-${String(i).padStart(2, "0")}`; - await writeSkill({ - dir: path.join(workspaceDir, "skills", name), - name, - description: "x".repeat(800), - }); - } + const workspaceDir = await cloneTemplateDir(truncationWorkspaceTemplateDir, "workspace"); const snapshot = withWorkspaceHome(workspaceDir, () => buildWorkspaceSkillSnapshot(workspaceDir, { @@ -143,16 +162,7 @@ describe("buildWorkspaceSkillSnapshot", () => { it("limits discovery for nested repo-style skills roots (dir/skills/*)", async () => { const workspaceDir = await fixtureSuite.createCaseDir("workspace"); - const repoDir = await fixtureSuite.createCaseDir("skills-repo"); - - for (let i = 0; i < 8; i += 1) { - const name = `repo-skill-${String(i).padStart(2, "0")}`; - await writeSkill({ - dir: path.join(repoDir, "skills", name), - name, - description: `Desc ${i}`, - }); - } + const repoDir = await cloneTemplateDir(nestedRepoTemplateDir, "skills-repo"); const snapshot = withWorkspaceHome(workspaceDir, () => buildWorkspaceSkillSnapshot(workspaceDir, { diff --git a/src/browser/extension-relay.test.ts b/src/browser/extension-relay.test.ts index d185875bca6..b1478feabd4 100644 --- a/src/browser/extension-relay.test.ts +++ b/src/browser/extension-relay.test.ts @@ -1,5 +1,5 @@ import { createServer } from "node:http"; -import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { afterAll, afterEach, beforeEach, describe, expect, it } from "vitest"; import WebSocket from "ws"; import { captureEnv } from "../test-utils/env.js"; import { @@ -141,6 +141,7 @@ async function waitForListMatch( describe("chrome extension relay server", () => { const TEST_GATEWAY_TOKEN = "test-gateway-token"; let cdpUrl = ""; + let sharedCdpUrl = ""; let envSnapshot: ReturnType; beforeEach(() => { @@ -162,6 +163,24 @@ describe("chrome extension relay server", () => { envSnapshot.restore(); }); + afterAll(async () => { + if (!sharedCdpUrl) { + return; + } + await stopChromeExtensionRelayServer({ cdpUrl: sharedCdpUrl }).catch(() => {}); + sharedCdpUrl = ""; + }); + + async function ensureSharedRelayServer() { + if (sharedCdpUrl) { + return sharedCdpUrl; + } + const port = await getFreePort(); + sharedCdpUrl = `http://127.0.0.1:${port}`; + await ensureChromeExtensionRelayServer({ cdpUrl: sharedCdpUrl }); + return sharedCdpUrl; + } + async function startRelayWithExtension() { const port = await getFreePort(); cdpUrl = `http://127.0.0.1:${port}`; @@ -205,57 +224,51 @@ describe("chrome extension relay server", () => { const unknown = getChromeExtensionRelayAuthHeaders(`http://127.0.0.1:${port}`); expect(unknown).toEqual({}); - cdpUrl = `http://127.0.0.1:${port}`; - await ensureChromeExtensionRelayServer({ cdpUrl }); + const sharedUrl = await ensureSharedRelayServer(); - const headers = getChromeExtensionRelayAuthHeaders(cdpUrl); + const headers = getChromeExtensionRelayAuthHeaders(sharedUrl); expect(Object.keys(headers)).toContain("x-openclaw-relay-token"); expect(headers["x-openclaw-relay-token"]).not.toBe(TEST_GATEWAY_TOKEN); }); it("rejects CDP access without relay auth token", async () => { - const port = await getFreePort(); - cdpUrl = `http://127.0.0.1:${port}`; - await ensureChromeExtensionRelayServer({ cdpUrl }); + const sharedUrl = await ensureSharedRelayServer(); + const sharedPort = new URL(sharedUrl).port; - const res = await fetch(`${cdpUrl}/json/version`); + const res = await fetch(`${sharedUrl}/json/version`); expect(res.status).toBe(401); - const cdp = new WebSocket(`ws://127.0.0.1:${port}/cdp`); + const cdp = new WebSocket(`ws://127.0.0.1:${sharedPort}/cdp`); const err = await waitForError(cdp); expect(err.message).toContain("401"); }); it("returns 400 for malformed percent-encoding in target action routes", async () => { - const port = await getFreePort(); - cdpUrl = `http://127.0.0.1:${port}`; - await ensureChromeExtensionRelayServer({ cdpUrl }); + const sharedUrl = await ensureSharedRelayServer(); - const res = await fetch(`${cdpUrl}/json/activate/%E0%A4%A`, { - headers: relayAuthHeaders(cdpUrl), + const res = await fetch(`${sharedUrl}/json/activate/%E0%A4%A`, { + headers: relayAuthHeaders(sharedUrl), }); expect(res.status).toBe(400); expect(await res.text()).toContain("invalid targetId encoding"); }); it("deduplicates concurrent relay starts for the same requested port", async () => { - const port = await getFreePort(); - cdpUrl = `http://127.0.0.1:${port}`; + const sharedUrl = await ensureSharedRelayServer(); + const port = Number(new URL(sharedUrl).port); const [first, second] = await Promise.all([ - ensureChromeExtensionRelayServer({ cdpUrl }), - ensureChromeExtensionRelayServer({ cdpUrl }), + ensureChromeExtensionRelayServer({ cdpUrl: sharedUrl }), + ensureChromeExtensionRelayServer({ cdpUrl: sharedUrl }), ]); expect(first).toBe(second); expect(first.port).toBe(port); }); it("allows CORS preflight from chrome-extension origins", async () => { - const port = await getFreePort(); - cdpUrl = `http://127.0.0.1:${port}`; - await ensureChromeExtensionRelayServer({ cdpUrl }); + const sharedUrl = await ensureSharedRelayServer(); const origin = "chrome-extension://abcdefghijklmnop"; - const res = await fetch(`${cdpUrl}/json/version`, { + const res = await fetch(`${sharedUrl}/json/version`, { method: "OPTIONS", headers: { Origin: origin, @@ -272,11 +285,9 @@ describe("chrome extension relay server", () => { }); it("rejects CORS preflight from non-extension origins", async () => { - const port = await getFreePort(); - cdpUrl = `http://127.0.0.1:${port}`; - await ensureChromeExtensionRelayServer({ cdpUrl }); + const sharedUrl = await ensureSharedRelayServer(); - const res = await fetch(`${cdpUrl}/json/version`, { + const res = await fetch(`${sharedUrl}/json/version`, { method: "OPTIONS", headers: { Origin: "https://example.com", @@ -288,15 +299,13 @@ describe("chrome extension relay server", () => { }); it("returns CORS headers on JSON responses for extension origins", async () => { - const port = await getFreePort(); - cdpUrl = `http://127.0.0.1:${port}`; - await ensureChromeExtensionRelayServer({ cdpUrl }); + const sharedUrl = await ensureSharedRelayServer(); const origin = "chrome-extension://abcdefghijklmnop"; - const res = await fetch(`${cdpUrl}/json/version`, { + const res = await fetch(`${sharedUrl}/json/version`, { headers: { Origin: origin, - ...relayAuthHeaders(cdpUrl), + ...relayAuthHeaders(sharedUrl), }, }); @@ -305,11 +314,10 @@ describe("chrome extension relay server", () => { }); it("rejects extension websocket access without relay auth token", async () => { - const port = await getFreePort(); - cdpUrl = `http://127.0.0.1:${port}`; - await ensureChromeExtensionRelayServer({ cdpUrl }); + const sharedUrl = await ensureSharedRelayServer(); + const sharedPort = new URL(sharedUrl).port; - const ext = new WebSocket(`ws://127.0.0.1:${port}/extension`); + const ext = new WebSocket(`ws://127.0.0.1:${sharedPort}/extension`); const err = await waitForError(ext); expect(err.message).toContain("401"); }); @@ -566,44 +574,42 @@ describe("chrome extension relay server", () => { }); it("accepts extension websocket access with relay token query param", async () => { - const port = await getFreePort(); - cdpUrl = `http://127.0.0.1:${port}`; - await ensureChromeExtensionRelayServer({ cdpUrl }); + const sharedUrl = await ensureSharedRelayServer(); + const sharedPort = new URL(sharedUrl).port; - const token = relayAuthHeaders(`ws://127.0.0.1:${port}/extension`)["x-openclaw-relay-token"]; + const token = relayAuthHeaders(`ws://127.0.0.1:${sharedPort}/extension`)[ + "x-openclaw-relay-token" + ]; expect(token).toBeTruthy(); const ext = new WebSocket( - `ws://127.0.0.1:${port}/extension?token=${encodeURIComponent(String(token))}`, + `ws://127.0.0.1:${sharedPort}/extension?token=${encodeURIComponent(String(token))}`, ); await waitForOpen(ext); ext.close(); }); it("accepts /json endpoints with relay token query param", async () => { - const port = await getFreePort(); - cdpUrl = `http://127.0.0.1:${port}`; - await ensureChromeExtensionRelayServer({ cdpUrl }); + const sharedUrl = await ensureSharedRelayServer(); - const token = relayAuthHeaders(cdpUrl)["x-openclaw-relay-token"]; + const token = relayAuthHeaders(sharedUrl)["x-openclaw-relay-token"]; expect(token).toBeTruthy(); const versionRes = await fetch( - `${cdpUrl}/json/version?token=${encodeURIComponent(String(token))}`, + `${sharedUrl}/json/version?token=${encodeURIComponent(String(token))}`, ); expect(versionRes.status).toBe(200); }); it("accepts raw gateway token for relay auth compatibility", async () => { - const port = await getFreePort(); - cdpUrl = `http://127.0.0.1:${port}`; - await ensureChromeExtensionRelayServer({ cdpUrl }); + const sharedUrl = await ensureSharedRelayServer(); + const sharedPort = new URL(sharedUrl).port; - const versionRes = await fetch(`${cdpUrl}/json/version`, { + const versionRes = await fetch(`${sharedUrl}/json/version`, { headers: { "x-openclaw-relay-token": TEST_GATEWAY_TOKEN }, }); expect(versionRes.status).toBe(200); const ext = new WebSocket( - `ws://127.0.0.1:${port}/extension?token=${encodeURIComponent(TEST_GATEWAY_TOKEN)}`, + `ws://127.0.0.1:${sharedPort}/extension?token=${encodeURIComponent(TEST_GATEWAY_TOKEN)}`, ); await waitForOpen(ext); ext.close(); diff --git a/src/gateway/server.auth.shared.ts b/src/gateway/server.auth.shared.ts index e9ed780193b..c50543edbdf 100644 --- a/src/gateway/server.auth.shared.ts +++ b/src/gateway/server.auth.shared.ts @@ -7,6 +7,7 @@ import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-cha import { buildDeviceAuthPayload } from "./device-auth.js"; import { PROTOCOL_VERSION } from "./protocol/index.js"; import { + createGatewaySuiteHarness, connectReq, getTrackedConnectChallengeNonce, getFreePort, @@ -360,6 +361,7 @@ export { connectReq, CONTROL_UI_CLIENT, createSignedDevice, + createGatewaySuiteHarness, ensurePairedDeviceTokenForCurrentIdentity, expectHelloOkServerVersion, getFreePort, diff --git a/src/gateway/server.sessions.gateway-server-sessions-a.test.ts b/src/gateway/server.sessions.gateway-server-sessions-a.test.ts index 09090e3c2f8..90b8e656b7e 100644 --- a/src/gateway/server.sessions.gateway-server-sessions-a.test.ts +++ b/src/gateway/server.sessions.gateway-server-sessions-a.test.ts @@ -115,12 +115,11 @@ installGatewayTestHooks({ scope: "suite" }); let harness: GatewayServerHarness; let sharedSessionStoreDir: string; -let sharedSessionStorePath: string; +let sessionStoreCaseSeq = 0; beforeAll(async () => { harness = await startGatewayServerHarness(); sharedSessionStoreDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-sessions-")); - sharedSessionStorePath = path.join(sharedSessionStoreDir, "sessions.json"); }); afterAll(async () => { @@ -131,10 +130,11 @@ afterAll(async () => { const openClient = async (opts?: Parameters[1]) => await harness.openClient(opts); async function createSessionStoreDir() { - await fs.rm(sharedSessionStoreDir, { recursive: true, force: true }); - await fs.mkdir(sharedSessionStoreDir, { recursive: true }); - testState.sessionStorePath = sharedSessionStorePath; - return { dir: sharedSessionStoreDir, storePath: sharedSessionStorePath }; + const dir = path.join(sharedSessionStoreDir, `case-${sessionStoreCaseSeq++}`); + await fs.mkdir(dir, { recursive: true }); + const storePath = path.join(dir, "sessions.json"); + testState.sessionStorePath = storePath; + return { dir, storePath }; } async function writeSingleLineSession(dir: string, sessionId: string, content: string) { diff --git a/src/gateway/test-helpers.server.ts b/src/gateway/test-helpers.server.ts index 944f0a0f3ae..ab5269f09b5 100644 --- a/src/gateway/test-helpers.server.ts +++ b/src/gateway/test-helpers.server.ts @@ -354,6 +354,57 @@ export async function withGatewayServer( } } +export async function createGatewaySuiteHarness(opts?: { + port?: number; + serverOptions?: GatewayServerOptions; +}): Promise<{ + port: number; + server: Awaited>; + openWs: (headers?: Record) => Promise; + close: () => Promise; +}> { + const started = await startGatewayServerWithRetries({ + port: opts?.port ?? (await getFreePort()), + opts: opts?.serverOptions, + }); + return { + port: started.port, + server: started.server, + openWs: async (headers?: Record) => { + const ws = new WebSocket(`ws://127.0.0.1:${started.port}`, headers ? { headers } : undefined); + trackConnectChallengeNonce(ws); + await new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error("timeout waiting for ws open")), 10_000); + const cleanup = () => { + clearTimeout(timer); + ws.off("open", onOpen); + ws.off("error", onError); + ws.off("close", onClose); + }; + const onOpen = () => { + cleanup(); + resolve(); + }; + const onError = (err: unknown) => { + cleanup(); + reject(err instanceof Error ? err : new Error(String(err))); + }; + const onClose = (code: number, reason: Buffer) => { + cleanup(); + reject(new Error(`closed ${code}: ${reason.toString()}`)); + }; + ws.once("open", onOpen); + ws.once("error", onError); + ws.once("close", onClose); + }); + return ws; + }, + close: async () => { + await started.server.close(); + }, + }; +} + export async function startServerWithClient( token?: string, opts?: GatewayServerOptions & { wsHeaders?: Record }, diff --git a/src/telegram/bot.create-telegram-bot.test.ts b/src/telegram/bot.create-telegram-bot.test.ts index 4196b1c9851..50e104a401d 100644 --- a/src/telegram/bot.create-telegram-bot.test.ts +++ b/src/telegram/bot.create-telegram-bot.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import type { Chat, Message } from "@grammyjs/types"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { escapeRegExp, formatEnvelopeTimestamp } from "../../test/helpers/envelope-timestamp.js"; import { withEnvAsync } from "../test-utils/env.js"; import { @@ -52,10 +52,10 @@ const TELEGRAM_TEST_TIMINGS = { } as const; describe("createTelegramBot", () => { - beforeEach(() => { + beforeAll(() => { process.env.TZ = "UTC"; }); - afterEach(() => { + afterAll(() => { process.env.TZ = ORIGINAL_TZ; }); diff --git a/src/telegram/bot.test.ts b/src/telegram/bot.test.ts index ff869570e20..2fe9636ee9b 100644 --- a/src/telegram/bot.test.ts +++ b/src/telegram/bot.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { escapeRegExp, formatEnvelopeTimestamp } from "../../test/helpers/envelope-timestamp.js"; import { expectInboundContextContract } from "../../test/helpers/inbound-contract.js"; import { @@ -36,8 +36,14 @@ function resolveSkillCommands(config: Parameters { - beforeEach(() => { + beforeAll(() => { process.env.TZ = "UTC"; + }); + afterAll(() => { + process.env.TZ = ORIGINAL_TZ; + }); + + beforeEach(() => { loadConfig.mockReturnValue({ agents: { defaults: { @@ -49,11 +55,8 @@ describe("createTelegramBot", () => { }, }); }); - afterEach(() => { - process.env.TZ = ORIGINAL_TZ; - }); - it("merges custom commands with native commands", () => { + it("merges custom commands with native commands", async () => { const config = { channels: { telegram: { @@ -68,6 +71,10 @@ describe("createTelegramBot", () => { createTelegramBot({ token: "tok" }); + await vi.waitFor(() => { + expect(setMyCommandsSpy).toHaveBeenCalled(); + }); + const registered = setMyCommandsSpy.mock.calls[0]?.[0] as Array<{ command: string; description: string; @@ -84,7 +91,7 @@ describe("createTelegramBot", () => { ]); }); - it("ignores custom commands that collide with native commands", () => { + it("ignores custom commands that collide with native commands", async () => { const errorSpy = vi.fn(); const config = { channels: { @@ -109,6 +116,10 @@ describe("createTelegramBot", () => { }, }); + await vi.waitFor(() => { + expect(setMyCommandsSpy).toHaveBeenCalled(); + }); + const registered = setMyCommandsSpy.mock.calls[0]?.[0] as Array<{ command: string; description: string; @@ -126,7 +137,7 @@ describe("createTelegramBot", () => { expect(errorSpy).toHaveBeenCalled(); }); - it("registers custom commands when native commands are disabled", () => { + it("registers custom commands when native commands are disabled", async () => { const config = { commands: { native: false }, channels: { @@ -142,6 +153,10 @@ describe("createTelegramBot", () => { createTelegramBot({ token: "tok" }); + await vi.waitFor(() => { + expect(setMyCommandsSpy).toHaveBeenCalled(); + }); + const registered = setMyCommandsSpy.mock.calls[0]?.[0] as Array<{ command: string; description: string; diff --git a/test/scripts/ios-team-id.test.ts b/test/scripts/ios-team-id.test.ts index e6f1f4de3f1..aade0d19f69 100644 --- a/test/scripts/ios-team-id.test.ts +++ b/test/scripts/ios-team-id.test.ts @@ -15,6 +15,7 @@ let sharedBinDir = ""; let sharedHomeDir = ""; let sharedHomeBinDir = ""; let sharedFakePythonPath = ""; +const runScriptCache = new Map(); async function writeExecutable(filePath: string, body: string): Promise { await writeFile(filePath, body, "utf8"); @@ -29,6 +30,14 @@ function runScript( stdout: string; stderr: string; } { + const cacheKey = JSON.stringify({ + homeDir, + extraEnv: Object.entries(extraEnv).toSorted(([a], [b]) => a.localeCompare(b)), + }); + const cached = runScriptCache.get(cacheKey); + if (cached) { + return cached; + } const binDir = path.join(homeDir, "bin"); const env = { HOME: homeDir, @@ -42,7 +51,9 @@ function runScript( encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], }); - return { ok: true, stdout: stdout.trim(), stderr: "" }; + const result = { ok: true, stdout: stdout.trim(), stderr: "" }; + runScriptCache.set(cacheKey, result); + return result; } catch (error) { const e = error as { stdout?: string | Buffer; @@ -50,7 +61,9 @@ function runScript( }; const stdout = typeof e.stdout === "string" ? e.stdout : (e.stdout?.toString("utf8") ?? ""); const stderr = typeof e.stderr === "string" ? e.stderr : (e.stderr?.toString("utf8") ?? ""); - return { ok: false, stdout: stdout.trim(), stderr: stderr.trim() }; + const result = { ok: false, stdout: stdout.trim(), stderr: stderr.trim() }; + runScriptCache.set(cacheKey, result); + return result; } }