fix: stabilize full-suite regressions

This commit is contained in:
Peter Steinberger
2026-06-01 09:16:58 -04:00
parent b226a752a1
commit cb7a4239ef
10 changed files with 128 additions and 42 deletions

View File

@@ -3218,7 +3218,7 @@ describe("active-memory plugin", () => {
testing.setSetupGraceTimeoutMsForTests(0);
api.pluginConfig = {
agents: ["main"],
timeoutMs: 100,
timeoutMs: 1_000,
};
plugin.register(api as unknown as OpenClawPluginApi);
hoisted.sessionStore["agent:main:memory-get-miss"] = {

View File

@@ -25,12 +25,14 @@ describe("mantis visual task runtime", () => {
let repoRoot: string;
beforeEach(async () => {
vi.restoreAllMocks();
vi.useRealTimers();
repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), "mantis-visual-task-"));
});
afterEach(async () => {
await fs.rm(repoRoot, { force: true, recursive: true });
vi.restoreAllMocks();
vi.useRealTimers();
});

View File

@@ -520,9 +520,12 @@ export async function runMantisVisualDriver(
runner,
stdio: "inherit",
});
await new Promise((resolve) => {
setTimeout(resolve, opts.settleMs ?? DEFAULT_SETTLE_MS);
});
const settleMs = opts.settleMs ?? DEFAULT_SETTLE_MS;
if (settleMs > 0) {
await new Promise((resolve) => {
setTimeout(resolve, settleMs);
});
}
await runCommandWithExternalOutput({
command: crabboxBin,
outputPath: screenshotPath,

View File

@@ -16,6 +16,7 @@ function createFetchFixture(payload: unknown): typeof fetch {
describe("scanOpenRouterModels", () => {
beforeEach(() => {
vi.restoreAllMocks();
vi.useRealTimers();
});
@@ -113,6 +114,7 @@ describe("scanOpenRouterModels", () => {
});
it("applies the scan timeout to the OpenRouter catalog request", async () => {
vi.useFakeTimers();
const fetchImpl: typeof fetch = async (_input, init) =>
await new Promise<Response>((_resolve, reject) => {
const signal = typeof init === "object" && init ? init.signal : undefined;
@@ -125,13 +127,16 @@ describe("scanOpenRouterModels", () => {
});
});
await expect(
const scan = expect(
scanOpenRouterModels({
fetchImpl,
probe: false,
timeoutMs: 1,
}),
).rejects.toThrow(/catalog aborted/);
await vi.advanceTimersByTimeAsync(1);
await scan;
});
it("caps oversized scan timeouts before scheduling catalog aborts", async () => {

View File

@@ -6,6 +6,7 @@ const pluginRegistryMocks = vi.hoisted(() => {
loadPluginManifestRegistryForInstalledIndex: loadManifestRegistry,
loadPluginManifestRegistryForPluginRegistry: loadManifestRegistry,
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
resolveInstalledManifestRegistryIndexFingerprint: vi.fn(() => "test-index"),
loadPluginMetadataSnapshot: vi.fn((params: unknown) => {
const registry = loadManifestRegistry(params) ?? { plugins: [], diagnostics: [] };
return {
@@ -26,6 +27,8 @@ const pluginRegistryMocks = vi.hoisted(() => {
vi.mock("../plugins/manifest-registry-installed.js", () => ({
loadPluginManifestRegistryForInstalledIndex:
pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex,
resolveInstalledManifestRegistryIndexFingerprint:
pluginRegistryMocks.resolveInstalledManifestRegistryIndexFingerprint,
}));
vi.mock("../plugins/plugin-registry.js", () => ({
@@ -38,12 +41,68 @@ vi.mock("../plugins/plugin-metadata-snapshot.js", () => ({
loadPluginMetadataSnapshot: pluginRegistryMocks.loadPluginMetadataSnapshot,
}));
import { clearCurrentPluginMetadataSnapshot } from "../plugins/current-plugin-metadata-snapshot.js";
import {
clearCurrentPluginMetadataSnapshot,
setCurrentPluginMetadataSnapshot,
} from "../plugins/current-plugin-metadata-snapshot.js";
import { resolveInstalledPluginIndexPolicyHash } from "../plugins/installed-plugin-index-policy.js";
import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.types.js";
import {
resetProviderAuthAliasMapCacheForTest,
resolveProviderIdForAuth,
} from "./provider-auth-aliases.js";
function createPluginMetadataSnapshot(params: {
config?: Parameters<typeof resolveInstalledPluginIndexPolicyHash>[0];
plugins: readonly PluginManifestRecord[];
}): PluginMetadataSnapshot {
const policyHash = resolveInstalledPluginIndexPolicyHash(params.config);
return {
policyHash,
index: {
version: 1,
hostContractVersion: "test",
compatRegistryVersion: "test",
migrationVersion: 1,
policyHash,
generatedAtMs: 1,
installRecords: {},
plugins: params.plugins.map((plugin) => ({
pluginId: plugin.id,
origin: plugin.origin ?? "global",
enabled: true,
enabledByDefault: true,
})),
diagnostics: [],
},
registryDiagnostics: [],
manifestRegistry: { plugins: params.plugins, diagnostics: [] },
plugins: params.plugins,
diagnostics: [],
byPluginId: new Map(params.plugins.map((plugin) => [plugin.id, plugin])),
normalizePluginId: (pluginId) => pluginId,
owners: {
channels: new Map(),
channelConfigs: new Map(),
providers: new Map(),
modelCatalogProviders: new Map(),
cliBackends: new Map(),
setupProviders: new Map(),
commandAliases: new Map(),
contracts: new Map(),
},
metrics: {
registrySnapshotMs: 0,
manifestRegistryMs: 0,
ownerMapsMs: 0,
totalMs: 0,
indexPluginCount: params.plugins.length,
manifestPluginCount: params.plugins.length,
},
};
}
describe("provider auth aliases", () => {
beforeEach(() => {
clearCurrentPluginMetadataSnapshot();
@@ -56,7 +115,7 @@ describe("provider auth aliases", () => {
});
it("treats deprecated auth choice ids as provider auth aliases", () => {
pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({
const metadataSnapshot = createPluginMetadataSnapshot({
plugins: [
{
id: "openai",
@@ -71,21 +130,22 @@ describe("provider auth aliases", () => {
],
},
],
diagnostics: [],
});
expect(resolveProviderIdForAuth("codex-cli")).toBe("openai");
expect(resolveProviderIdForAuth("openai-chatgpt-import")).toBe("openai");
expect(resolveProviderIdForAuth("openai")).toBe("openai");
expect(resolveProviderIdForAuth("codex-cli", { metadataSnapshot })).toBe("openai");
expect(resolveProviderIdForAuth("openai-chatgpt-import", { metadataSnapshot })).toBe("openai");
expect(resolveProviderIdForAuth("openai", { metadataSnapshot })).toBe("openai");
});
it("does not reuse aliases across env-resolved plugin roots", () => {
const config = {};
const env = {
HOME: "/home/one",
OPENCLAW_HOME: undefined,
} as NodeJS.ProcessEnv;
pluginRegistryMocks.loadPluginManifestRegistryForPluginRegistry
.mockReturnValueOnce({
setCurrentPluginMetadataSnapshot(
createPluginMetadataSnapshot({
config,
plugins: [
{
id: "one",
@@ -93,9 +153,15 @@ describe("provider auth aliases", () => {
providerAuthAliases: { fixture: "provider-one" },
},
],
diagnostics: [],
})
.mockReturnValueOnce({
}),
{ config, env },
);
expect(resolveProviderIdForAuth("fixture", { config, env })).toBe("provider-one");
env.HOME = "/home/two";
setCurrentPluginMetadataSnapshot(
createPluginMetadataSnapshot({
config,
plugins: [
{
id: "two",
@@ -103,15 +169,11 @@ describe("provider auth aliases", () => {
providerAuthAliases: { fixture: "provider-two" },
},
],
diagnostics: [],
});
expect(resolveProviderIdForAuth("fixture", { config: {}, env })).toBe("provider-one");
env.HOME = "/home/two";
expect(resolveProviderIdForAuth("fixture", { config: {}, env })).toBe("provider-two");
expect(pluginRegistryMocks.loadPluginManifestRegistryForPluginRegistry).toHaveBeenCalledTimes(
2,
}),
{ config, env },
);
expect(resolveProviderIdForAuth("fixture", { config, env })).toBe("provider-two");
});
it("uses caller-provided metadata snapshots without loading plugin metadata", () => {

View File

@@ -827,7 +827,6 @@ describe("Tool Search", () => {
}, 5_000);
it("aborts already-started bridged calls when code mode times out", async () => {
testing.setToolSearchMinCodeTimeoutMsForTest(100);
const codeTool = fakeTool(TOOL_SEARCH_CODE_MODE_TOOL_NAME, "code mode");
const target = pluginTool("fake_abort_on_timeout", "Long-running target tool");
let observedSignal: AbortSignal | undefined;
@@ -860,7 +859,7 @@ describe("Tool Search", () => {
const config = {
tools: {
toolSearch: { enabled: true, mode: "code", codeTimeoutMs: 100 },
toolSearch: { enabled: true, mode: "code", codeTimeoutMs: 1_000 },
},
} as never;
applyToolSearchCatalog({

View File

@@ -115,6 +115,8 @@ describe("startProxy", () => {
});
afterEach(() => {
resetProxyLifecycleForTests();
resetActiveManagedProxyStateForTests();
for (const dir of tempDirs.splice(0)) {
rmSync(dir, { recursive: true, force: true });
}

View File

@@ -347,7 +347,7 @@ describe("run-oxlint", () => {
" ...process.env,",
" OPENCLAW_OXLINT_SHARD_HEARTBEAT_MS: '0',",
" OPENCLAW_OXLINT_SHARD_TIMEOUT_MS: '0',",
" OPENCLAW_OXLINT_SHARD_KILL_GRACE_MS: '25',",
" OPENCLAW_OXLINT_SHARD_KILL_GRACE_MS: '250',",
" },",
" extraArgs: [],",
" runner: process.env.RUNNER_FILE,",

View File

@@ -182,23 +182,35 @@ setInterval(() => {}, 1000);
args: [scriptPath, grandchildPidPath],
command: process.execPath,
cwd: root,
timeoutKillGraceMs: 25,
timeoutMs: 100,
timeoutKillGraceMs: 100,
timeoutMs: 500,
});
const runResult = runPromise.then(
() => ({ ok: true as const }),
(error: unknown) => ({ error, ok: false as const }),
);
try {
await waitFor(() => fs.existsSync(grandchildPidPath));
grandchildPid = Number.parseInt(fs.readFileSync(grandchildPidPath, "utf8"), 10);
await waitFor(() => {
if (!fs.existsSync(grandchildPidPath)) {
return false;
}
grandchildPid = Number.parseInt(fs.readFileSync(grandchildPidPath, "utf8"), 10);
return Number.isInteger(grandchildPid) && isProcessAlive(grandchildPid);
});
expect(Number.isInteger(grandchildPid)).toBe(true);
expect(isProcessAlive(grandchildPid)).toBe(true);
await expect(runPromise).rejects.toMatchObject({
code: "ETIMEDOUT",
message: expect.stringContaining("timed out after 100ms"),
const result = await runResult;
expect(result).toMatchObject({
error: {
code: "ETIMEDOUT",
message: expect.stringContaining("timed out after 500ms"),
},
ok: false,
});
await waitFor(() => !isProcessAlive(grandchildPid));
} finally {
await runPromise.catch(() => {});
await runResult.catch(() => {});
if (grandchildPid && isProcessAlive(grandchildPid)) {
process.kill(grandchildPid, "SIGKILL");
}

View File

@@ -1602,12 +1602,13 @@ describe("grouped chat rendering", () => {
},
{ interval: 1, timeout: 100 },
);
expect(fetchMock).toHaveBeenCalledTimes(1);
const [fetchUrl, fetchInit] = requireFetchCall(fetchMock);
expect(fetchUrl).toBe(
"/api/chat/media/outgoing/agent%3Amain%3Amain/00000000-0000-4000-8000-000000000000/full",
);
expectSameOriginGet(fetchInit);
expect(fetchMock).toHaveBeenCalled();
for (const [fetchUrl, fetchInit] of fetchMock.mock.calls as [string, RequestInit?][]) {
expect(fetchUrl).toBe(
"/api/chat/media/outgoing/agent%3Amain%3Amain/00000000-0000-4000-8000-000000000000/full",
);
expectSameOriginGet(fetchInit);
}
});
it("does not send auth to cross-origin managed-image-looking URLs", () => {