From 3525a965edc1ec320e39b71c2f6d3ea85810dbff Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 31 May 2026 12:09:33 +0100 Subject: [PATCH] test(release): stabilize beta validation lanes --- .github/workflows/openclaw-release-checks.yml | 2 +- extensions/acpx/src/codex-auth-bridge.test.ts | 2 +- extensions/acpx/src/manifest.test.ts | 10 +++--- extensions/qa-lab/src/browser-runtime.test.ts | 6 +++- extensions/qa-lab/src/bus-server.test.ts | 6 +++- .../slack/slack-live.runtime.test.ts | 6 +++- .../slack/slack-live.runtime.ts | 35 ++++++++++++------- .../src/bot.create-telegram-bot.test.ts | 19 ++++++++-- 8 files changed, 61 insertions(+), 25 deletions(-) diff --git a/.github/workflows/openclaw-release-checks.yml b/.github/workflows/openclaw-release-checks.yml index 896fc8e26fd..b366b812ccb 100644 --- a/.github/workflows/openclaw-release-checks.yml +++ b/.github/workflows/openclaw-release-checks.yml @@ -1075,7 +1075,7 @@ jobs: needs: [resolve_target] if: contains(fromJSON('["all","qa","qa-live"]'), needs.resolve_target.outputs.rerun_group) && needs.resolve_target.outputs.qa_live_matrix_enabled == 'true' continue-on-error: true - runs-on: ubuntu-24.04 + runs-on: blacksmith-16vcpu-ubuntu-2404 timeout-minutes: 60 permissions: contents: read diff --git a/extensions/acpx/src/codex-auth-bridge.test.ts b/extensions/acpx/src/codex-auth-bridge.test.ts index 9cac5dd8587..d1ee63c3dfc 100644 --- a/extensions/acpx/src/codex-auth-bridge.test.ts +++ b/extensions/acpx/src/codex-auth-bridge.test.ts @@ -195,7 +195,7 @@ describe("prepareAcpxCodexAuthConfig", () => { }); const wrapper = await fs.readFile(generated.wrapperPath, "utf8"); - expect(wrapper).toContain('"@agentclientprotocol/claude-agent-acp@0.37.0"'); + expect(wrapper).toContain('"@agentclientprotocol/claude-agent-acp@0.39.0"'); expect(wrapper).toContain('"--", "claude-agent-acp"'); expect(wrapper).not.toContain("@agentclientprotocol/claude-agent-acp@^0.31.0"); expect(wrapper).not.toContain("@agentclientprotocol/claude-agent-acp@0.31.0"); diff --git a/extensions/acpx/src/manifest.test.ts b/extensions/acpx/src/manifest.test.ts index 102817ab307..ce8ed193943 100644 --- a/extensions/acpx/src/manifest.test.ts +++ b/extensions/acpx/src/manifest.test.ts @@ -6,16 +6,16 @@ type AcpxPackageManifest = { devDependencies?: Record; }; +const packageJson = JSON.parse( + fs.readFileSync(new URL("../package.json", import.meta.url), "utf8"), +) as AcpxPackageManifest; + describe("acpx package manifest", () => { it("keeps runtime dependencies in the package manifest", () => { - const packageJson = JSON.parse( - fs.readFileSync(new URL("../package.json", import.meta.url), "utf8"), - ) as AcpxPackageManifest; - expect(packageJson.dependencies?.acpx).toBeTypeOf("string"); expect(packageJson.dependencies?.acpx).not.toBe(""); expect(packageJson.dependencies?.["@zed-industries/codex-acp"]).toBe("0.15.0"); - expect(packageJson.dependencies?.["@agentclientprotocol/claude-agent-acp"]).toBe("0.37.0"); + expect(packageJson.dependencies?.["@agentclientprotocol/claude-agent-acp"]).toBe("0.39.0"); expect(packageJson.devDependencies?.["@agentclientprotocol/claude-agent-acp"]).toBeUndefined(); }); }); diff --git a/extensions/qa-lab/src/browser-runtime.test.ts b/extensions/qa-lab/src/browser-runtime.test.ts index 2f9a6879ffb..721a8f4049d 100644 --- a/extensions/qa-lab/src/browser-runtime.test.ts +++ b/extensions/qa-lab/src/browser-runtime.test.ts @@ -1,5 +1,5 @@ import { MAX_TIMER_TIMEOUT_MS } from "openclaw/plugin-sdk/number-runtime"; -import { describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import { callQaBrowserRequest, qaBrowserAct, @@ -17,6 +17,10 @@ function createEnv() { } describe("browser-runtime", () => { + beforeEach(() => { + vi.useRealTimers(); + }); + it("sends normalized browser.request payloads through the gateway", async () => { const env = createEnv(); diff --git a/extensions/qa-lab/src/bus-server.test.ts b/extensions/qa-lab/src/bus-server.test.ts index ad094a196ee..31a7be0a609 100644 --- a/extensions/qa-lab/src/bus-server.test.ts +++ b/extensions/qa-lab/src/bus-server.test.ts @@ -1,5 +1,5 @@ import { Agent, createServer, request } from "node:http"; -import { afterEach, describe, expect, it } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { closeQaHttpServer, handleQaBusRequest, startQaBusServer } from "./bus-server.js"; import { createQaBusState } from "./bus-state.js"; import type { QaBusPollResult } from "./runtime-api.js"; @@ -86,6 +86,10 @@ describe("closeQaHttpServer", () => { describe("qa-bus server", () => { const stops: Array<() => Promise> = []; + beforeEach(() => { + vi.useRealTimers(); + }); + afterEach(async () => { await Promise.all(stops.splice(0).map((stop) => stop())); }); diff --git a/extensions/qa-lab/src/live-transports/slack/slack-live.runtime.test.ts b/extensions/qa-lab/src/live-transports/slack/slack-live.runtime.test.ts index 1445cd99d1e..fbdee62fee0 100644 --- a/extensions/qa-lab/src/live-transports/slack/slack-live.runtime.test.ts +++ b/extensions/qa-lab/src/live-transports/slack/slack-live.runtime.test.ts @@ -1,10 +1,14 @@ import fs from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; -import { describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import { testing, runSlackQaLive } from "./slack-live.runtime.js"; describe("Slack live QA runtime helpers", () => { + beforeEach(() => { + vi.useRealTimers(); + }); + it("resolves env credential payloads", () => { expect( testing.resolveSlackQaRuntimeEnv({ diff --git a/extensions/qa-lab/src/live-transports/slack/slack-live.runtime.ts b/extensions/qa-lab/src/live-transports/slack/slack-live.runtime.ts index b0f5fc79273..54394f5356c 100644 --- a/extensions/qa-lab/src/live-transports/slack/slack-live.runtime.ts +++ b/extensions/qa-lab/src/live-transports/slack/slack-live.runtime.ts @@ -966,6 +966,11 @@ async function waitForSlackNoReply(params: { timeoutMs: number; }) { const startedAt = Date.now(); + const observedKeys = new Set( + params.observedMessages + .map((message) => `${message.channelId ?? params.channelId}:${message.ts ?? ""}`) + .filter((key) => !key.endsWith(":")), + ); let elapsedMs = Date.now() - startedAt; while (elapsedMs < params.timeoutMs) { const messages = await listSlackMessages({ @@ -983,19 +988,23 @@ async function waitForSlackNoReply(params: { continue; } const matchedScenario = text.includes(params.matchText); - params.observedMessages.push({ - actionValues: collectSlackActionValues(message.blocks), - blockText: collectSlackBlockText(message.blocks), - botId: message.bot_id, - channelId: params.channelId, - matchedScenario, - scenarioId: params.observationScenarioId, - scenarioTitle: params.observationScenarioTitle, - text, - threadTs: message.thread_ts, - ts: message.ts, - userId: message.user, - }); + const observedKey = `${params.channelId}:${message.ts}`; + if (!observedKeys.has(observedKey)) { + observedKeys.add(observedKey); + params.observedMessages.push({ + actionValues: collectSlackActionValues(message.blocks), + blockText: collectSlackBlockText(message.blocks), + botId: message.bot_id, + channelId: params.channelId, + matchedScenario, + scenarioId: params.observationScenarioId, + scenarioTitle: params.observationScenarioTitle, + text, + threadTs: message.thread_ts, + ts: message.ts, + userId: message.user, + }); + } if (matchedScenario) { throw new Error("unexpected Slack SUT reply observed"); } diff --git a/extensions/telegram/src/bot.create-telegram-bot.test.ts b/extensions/telegram/src/bot.create-telegram-bot.test.ts index 7f1841f1bbe..0f91e03603d 100644 --- a/extensions/telegram/src/bot.create-telegram-bot.test.ts +++ b/extensions/telegram/src/bot.create-telegram-bot.test.ts @@ -6,10 +6,11 @@ import { import type { TelegramGroupConfig } from "openclaw/plugin-sdk/config-contracts"; import type { GetReplyOptions, MsgContext } from "openclaw/plugin-sdk/reply-runtime"; import { sanitizeTerminalText } from "openclaw/plugin-sdk/test-fixtures"; -import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { TelegramBotOptions } from "./bot.types.js"; import type { TelegramGetChat } from "./bot/types.js"; const harness = await import("./bot.create-telegram-bot.test-harness.js"); +const pluginStateTestRuntime = await import("openclaw/plugin-sdk/plugin-state-test-runtime"); const conversationRuntime = await import("openclaw/plugin-sdk/conversation-runtime"); const configMutation = await import("openclaw/plugin-sdk/config-mutation"); const sessionStoreRuntime = await import("openclaw/plugin-sdk/session-store-runtime"); @@ -57,6 +58,7 @@ const { } = await import("./bot-core.js"); const { resolveTelegramConversationRoute } = await import("./conversation-route.js"); const { clearAccountThrottlersForTest } = await import("./account-throttler.js"); +const messageDispatchDedupe = await import("./message-dispatch-dedupe.js"); const { buildTelegramGroupFrom, buildTelegramThreadParams, @@ -232,7 +234,11 @@ describe("createTelegramBot", () => { process.env.TZ = ORIGINAL_TZ; } }); - beforeEach(() => { + afterEach(() => { + messageDispatchDedupe.setTelegramMessageDispatchDedupeStoreForTest(undefined); + pluginStateTestRuntime.resetPluginStateStoreForTests(); + }); + beforeEach(async () => { resetTelegramForumFlagCacheForTest(); clearAccountThrottlersForTest(); throttlerSpy.mockReset(); @@ -244,6 +250,15 @@ describe("createTelegramBot", () => { ...opts, telegramDeps: telegramBotDepsForTest, }); + pluginStateTestRuntime.resetPluginStateStoreForTests({ closeDatabase: false }); + const store = pluginStateTestRuntime.createPluginStateKeyedStoreForTests("telegram", { + namespace: messageDispatchDedupe.TELEGRAM_MESSAGE_DISPATCH_DEDUPE_NAMESPACE, + maxEntries: messageDispatchDedupe.TELEGRAM_MESSAGE_DISPATCH_DEDUPE_MAX_ENTRIES, + }) as NonNullable< + Parameters[0] + >; + await store.clear(); + messageDispatchDedupe.setTelegramMessageDispatchDedupeStoreForTest(store); }); // groupPolicy tests