Tests: fix flaky shard expectations

This commit is contained in:
Peter Steinberger
2026-04-07 12:19:10 +01:00
parent e5716394ca
commit c541a9c110
9 changed files with 43 additions and 28 deletions

View File

@@ -140,7 +140,8 @@ cli note
it("runs wiki doctor and sets a non-zero exit code when warnings exist", async () => {
const { rootDir, config } = await createCliVault({
config: {
obsidian: { enabled: true, useOfficialCli: true },
vaultMode: "bridge",
bridge: { enabled: false },
},
});
const program = new Command();

View File

@@ -1,5 +1,4 @@
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { createServer } from "node:net";
import os from "node:os";
import path from "node:path";
import { describe, expect, it, vi } from "vitest";
@@ -98,18 +97,26 @@ describe("runQaDockerUp", () => {
});
it("falls back to free host ports when defaults are already occupied", async () => {
const gatewayServer = createServer();
const labServer = createServer();
const outputDir = await mkdtemp(path.join(os.tmpdir(), "qa-docker-up-"));
await new Promise<void>((resolve) => gatewayServer.listen(18789, "127.0.0.1", () => resolve()));
await new Promise<void>((resolve) => labServer.listen(43124, "127.0.0.1", () => resolve()));
const gatewayPort = 18789;
const qaLabPort = 43124;
const resolveHostPort = vi.fn(async (preferredPort: number) => {
if (preferredPort === gatewayPort) {
return 28001;
}
if (preferredPort === qaLabPort) {
return 28002;
}
return preferredPort;
});
try {
const result = await runQaDockerUp(
{
repoRoot: "/repo/openclaw",
outputDir,
gatewayPort,
qaLabPort,
skipUiBuild: true,
usePrebuiltImage: true,
},
@@ -122,18 +129,15 @@ describe("runQaDockerUp", () => {
},
fetchImpl: vi.fn(async () => ({ ok: true })),
sleepImpl: vi.fn(async () => {}),
resolveHostPortImpl: resolveHostPort,
},
);
expect(result.gatewayUrl).not.toBe("http://127.0.0.1:18789/");
expect(result.qaLabUrl).not.toBe("http://127.0.0.1:43124");
expect(result.gatewayUrl).not.toBe(`http://127.0.0.1:${gatewayPort}/`);
expect(result.qaLabUrl).not.toBe(`http://127.0.0.1:${qaLabPort}`);
expect(result.gatewayUrl).toBe("http://127.0.0.1:28001/");
expect(result.qaLabUrl).toBe("http://127.0.0.1:28002");
} finally {
await new Promise<void>((resolve, reject) =>
gatewayServer.close((error) => (error ? reject(error) : resolve())),
);
await new Promise<void>((resolve, reject) =>
labServer.close((error) => (error ? reject(error) : resolve())),
);
await rm(outputDir, { recursive: true, force: true });
}
});

View File

@@ -254,15 +254,17 @@ export async function runQaDockerUp(
runCommand?: RunCommand;
fetchImpl?: FetchLike;
sleepImpl?: (ms: number) => Promise<unknown>;
resolveHostPortImpl?: typeof resolveHostPort;
},
): Promise<QaDockerUpResult> {
const repoRoot = path.resolve(params.repoRoot ?? process.cwd());
const outputDir = path.resolve(params.outputDir ?? DEFAULT_QA_DOCKER_DIR);
const gatewayPort = await resolveHostPort(
const resolveHostPortImpl = deps?.resolveHostPortImpl ?? resolveHostPort;
const gatewayPort = await resolveHostPortImpl(
params.gatewayPort ?? 18789,
params.gatewayPort != null,
);
const qaLabPort = await resolveHostPort(params.qaLabPort ?? 43124, params.qaLabPort != null);
const qaLabPort = await resolveHostPortImpl(params.qaLabPort ?? 43124, params.qaLabPort != null);
const runCommand = deps?.runCommand ?? execCommand;
const fetchImpl =
deps?.fetchImpl ??

View File

@@ -3,6 +3,7 @@ import {
createManagedRun,
enqueueSystemEventMock,
requestHeartbeatNowMock,
EXISTING_CODEX_CONFIG,
setupCliRunnerTestModule,
supervisorSpawnMock,
} from "./cli-runner.test-support.js";
@@ -174,6 +175,7 @@ describe("runCliAgent reliability", () => {
prompt: "hi",
provider: "codex-cli",
model: "gpt-5.4",
config: EXISTING_CODEX_CONFIG,
timeoutMs: 1_000,
runId: "run-retry-failure",
cliSessionId: "thread-123",

View File

@@ -5,7 +5,7 @@ import { formatErrorMessage } from "../infra/errors.js";
import { executePreparedCliRun } from "./cli-runner/execute.js";
import { prepareCliRunContext } from "./cli-runner/prepare.js";
import type { RunCliAgentParams } from "./cli-runner/types.js";
import { FailoverError, resolveFailoverStatus } from "./failover-error.js";
import { FailoverError, isFailoverError, resolveFailoverStatus } from "./failover-error.js";
import { classifyFailoverReason, isFailoverErrorMessage } from "./pi-embedded-helpers.js";
import type { EmbeddedPiRunResult } from "./pi-embedded-runner.js";
@@ -56,13 +56,10 @@ export async function runCliAgent(params: RunCliAgentParams): Promise<EmbeddedPi
const effectiveCliSessionId = output.sessionId ?? context.reusableCliSession.sessionId;
return buildCliRunResult({ output, effectiveCliSessionId });
} catch (err) {
if (err instanceof FailoverError) {
if (isFailoverError(err)) {
const retryableSessionId = context.reusableCliSession.sessionId ?? params.cliSessionId;
// Check if this is a session expired error and we have a session to clear
if (
err.reason === "session_expired" &&
context.reusableCliSession.sessionId &&
params.sessionKey
) {
if (err.reason === "session_expired" && retryableSessionId && params.sessionKey) {
// Clear the expired session ID from the session entry
// This requires access to the session store, which we don't have here
// We'll need to modify the caller to handle this case

View File

@@ -40,7 +40,15 @@ export class FailoverError extends Error {
}
export function isFailoverError(err: unknown): err is FailoverError {
return err instanceof FailoverError;
if (err instanceof FailoverError) {
return true;
}
return Boolean(
err &&
typeof err === "object" &&
(err as { name?: unknown }).name === "FailoverError" &&
typeof (err as { reason?: unknown }).reason === "string",
);
}
export function resolveFailoverStatus(reason: FailoverReason): number | undefined {

View File

@@ -178,6 +178,7 @@ describe("callGateway url resolution", () => {
const envSnapshot = captureEnv([
"OPENCLAW_ALLOW_INSECURE_PRIVATE_WS",
"OPENCLAW_CONFIG_PATH",
"OPENCLAW_GATEWAY_PORT",
"OPENCLAW_GATEWAY_URL",
"OPENCLAW_GATEWAY_TOKEN",
"OPENCLAW_STATE_DIR",
@@ -187,6 +188,7 @@ describe("callGateway url resolution", () => {
envSnapshot.restore();
delete process.env.OPENCLAW_ALLOW_INSECURE_PRIVATE_WS;
delete process.env.OPENCLAW_CONFIG_PATH;
delete process.env.OPENCLAW_GATEWAY_PORT;
delete process.env.OPENCLAW_GATEWAY_URL;
delete process.env.OPENCLAW_GATEWAY_TOKEN;
delete process.env.OPENCLAW_STATE_DIR;
@@ -612,6 +614,7 @@ describe("buildGatewayConnectionDetails", () => {
resolveGatewayPort.mockReturnValue(18800);
__testing.setDepsForTests({
loadConfig: {} as never,
resolveGatewayPort: () => 18789,
});
const details = buildGatewayConnectionDetails();

View File

@@ -96,7 +96,7 @@ describe("gateway cli backend live helpers", () => {
clientDisplayName: "vitest-live",
clientVersion: "dev",
mode: GATEWAY_CLIENT_MODES.TEST,
connectChallengeTimeoutMs: 30_000,
connectChallengeTimeoutMs: 45_000,
});
expect(gatewayClientState.lastOptions).not.toHaveProperty("requestTimeoutMs");
});

View File

@@ -76,8 +76,6 @@ describe("stageBundledPluginRuntimeDeps", () => {
name: "@openclaw/fixture-plugin",
version: "1.0.0",
dependencies: { "left-pad": "1.3.0" },
peerDependencies: { react: "^19.0.0" },
peerDependenciesMeta: { react: { optional: true } },
openclaw: { bundle: { stageRuntimeDependencies: true } },
});
});