mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 04:37:40 +00:00
refactor(test): dedupe agent and discord test fixtures
This commit is contained in:
@@ -7,9 +7,9 @@ vi.mock("../pi-model-discovery.js", () => ({
|
||||
|
||||
import { buildInlineProviderModels, resolveModel } from "./model.js";
|
||||
import {
|
||||
buildOpenAICodexForwardCompatExpectation,
|
||||
makeModel,
|
||||
mockDiscoveredModel,
|
||||
OPENAI_CODEX_TEMPLATE_MODEL,
|
||||
mockOpenAICodexTemplateModel,
|
||||
resetMockDiscoverModels,
|
||||
} from "./model.test-harness.js";
|
||||
|
||||
@@ -38,21 +38,11 @@ describe("pi embedded model e2e smoke", () => {
|
||||
});
|
||||
|
||||
it("builds an openai-codex forward-compat fallback for gpt-5.3-codex", () => {
|
||||
mockDiscoveredModel({
|
||||
provider: "openai-codex",
|
||||
modelId: "gpt-5.2-codex",
|
||||
templateModel: OPENAI_CODEX_TEMPLATE_MODEL,
|
||||
});
|
||||
mockOpenAICodexTemplateModel();
|
||||
|
||||
const result = resolveModel("openai-codex", "gpt-5.3-codex", "/tmp/agent");
|
||||
expect(result.error).toBeUndefined();
|
||||
expect(result.model).toMatchObject({
|
||||
provider: "openai-codex",
|
||||
id: "gpt-5.3-codex",
|
||||
api: "openai-codex-responses",
|
||||
baseUrl: "https://chatgpt.com/backend-api",
|
||||
reasoning: true,
|
||||
});
|
||||
expect(result.model).toMatchObject(buildOpenAICodexForwardCompatExpectation("gpt-5.3-codex"));
|
||||
});
|
||||
|
||||
it("keeps unknown-model errors for non-forward-compat IDs", () => {
|
||||
|
||||
@@ -25,6 +25,28 @@ export const OPENAI_CODEX_TEMPLATE_MODEL = {
|
||||
maxTokens: 128000,
|
||||
};
|
||||
|
||||
export function mockOpenAICodexTemplateModel(): void {
|
||||
mockDiscoveredModel({
|
||||
provider: "openai-codex",
|
||||
modelId: "gpt-5.2-codex",
|
||||
templateModel: OPENAI_CODEX_TEMPLATE_MODEL,
|
||||
});
|
||||
}
|
||||
|
||||
export function buildOpenAICodexForwardCompatExpectation(
|
||||
id: string = "gpt-5.3-codex",
|
||||
): Partial<typeof OPENAI_CODEX_TEMPLATE_MODEL> & { provider: string; id: string } {
|
||||
return {
|
||||
provider: "openai-codex",
|
||||
id,
|
||||
api: "openai-codex-responses",
|
||||
baseUrl: "https://chatgpt.com/backend-api",
|
||||
reasoning: true,
|
||||
contextWindow: 272000,
|
||||
maxTokens: 128000,
|
||||
};
|
||||
}
|
||||
|
||||
export function resetMockDiscoverModels(): void {
|
||||
vi.mocked(discoverModels).mockReturnValue({
|
||||
find: vi.fn(() => null),
|
||||
|
||||
@@ -8,9 +8,10 @@ vi.mock("../pi-model-discovery.js", () => ({
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { buildInlineProviderModels, resolveModel } from "./model.js";
|
||||
import {
|
||||
buildOpenAICodexForwardCompatExpectation,
|
||||
makeModel,
|
||||
mockDiscoveredModel,
|
||||
OPENAI_CODEX_TEMPLATE_MODEL,
|
||||
mockOpenAICodexTemplateModel,
|
||||
resetMockDiscoverModels,
|
||||
} from "./model.test-harness.js";
|
||||
|
||||
@@ -171,24 +172,12 @@ describe("resolveModel", () => {
|
||||
});
|
||||
|
||||
it("builds an openai-codex fallback for gpt-5.3-codex", () => {
|
||||
mockDiscoveredModel({
|
||||
provider: "openai-codex",
|
||||
modelId: "gpt-5.2-codex",
|
||||
templateModel: OPENAI_CODEX_TEMPLATE_MODEL,
|
||||
});
|
||||
mockOpenAICodexTemplateModel();
|
||||
|
||||
const result = resolveModel("openai-codex", "gpt-5.3-codex", "/tmp/agent");
|
||||
|
||||
expect(result.error).toBeUndefined();
|
||||
expect(result.model).toMatchObject({
|
||||
provider: "openai-codex",
|
||||
id: "gpt-5.3-codex",
|
||||
api: "openai-codex-responses",
|
||||
baseUrl: "https://chatgpt.com/backend-api",
|
||||
reasoning: true,
|
||||
contextWindow: 272000,
|
||||
maxTokens: 128000,
|
||||
});
|
||||
expect(result.model).toMatchObject(buildOpenAICodexForwardCompatExpectation("gpt-5.3-codex"));
|
||||
});
|
||||
|
||||
it("builds an anthropic forward-compat fallback for claude-opus-4-6", () => {
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
import type { EmbeddedRunAttemptResult } from "./run/types.js";
|
||||
|
||||
export const DEFAULT_OVERFLOW_ERROR_MESSAGE =
|
||||
"request_too_large: Request size exceeds model context window";
|
||||
|
||||
export function makeOverflowError(message: string = DEFAULT_OVERFLOW_ERROR_MESSAGE): Error {
|
||||
return new Error(message);
|
||||
}
|
||||
|
||||
export function makeCompactionSuccess(params: {
|
||||
summary: string;
|
||||
firstKeptEntryId: string;
|
||||
tokensBefore: number;
|
||||
}) {
|
||||
return {
|
||||
ok: true as const,
|
||||
compacted: true as const,
|
||||
result: {
|
||||
summary: params.summary,
|
||||
firstKeptEntryId: params.firstKeptEntryId,
|
||||
tokensBefore: params.tokensBefore,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function makeAttemptResult(
|
||||
overrides: Partial<EmbeddedRunAttemptResult> = {},
|
||||
): EmbeddedRunAttemptResult {
|
||||
@@ -43,24 +66,38 @@ export function mockOverflowRetrySuccess(params: {
|
||||
compactDirect: MockCompactDirect;
|
||||
overflowMessage?: string;
|
||||
}) {
|
||||
const overflowError = new Error(
|
||||
params.overflowMessage ?? "request_too_large: Request size exceeds model context window",
|
||||
);
|
||||
const overflowError = makeOverflowError(params.overflowMessage);
|
||||
|
||||
params.runEmbeddedAttempt.mockResolvedValueOnce(
|
||||
makeAttemptResult({ promptError: overflowError }),
|
||||
);
|
||||
params.runEmbeddedAttempt.mockResolvedValueOnce(makeAttemptResult({ promptError: null }));
|
||||
|
||||
params.compactDirect.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
compacted: true,
|
||||
result: {
|
||||
params.compactDirect.mockResolvedValueOnce(
|
||||
makeCompactionSuccess({
|
||||
summary: "Compacted session",
|
||||
firstKeptEntryId: "entry-5",
|
||||
tokensBefore: 150000,
|
||||
},
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
return overflowError;
|
||||
}
|
||||
|
||||
export function queueOverflowAttemptWithOversizedToolOutput(
|
||||
runEmbeddedAttempt: MockRunEmbeddedAttempt,
|
||||
overflowError: Error = makeOverflowError(),
|
||||
): Error {
|
||||
runEmbeddedAttempt.mockResolvedValueOnce(
|
||||
makeAttemptResult({
|
||||
promptError: overflowError,
|
||||
messagesSnapshot: [
|
||||
{
|
||||
role: "assistant",
|
||||
content: "big tool output",
|
||||
} as unknown as EmbeddedRunAttemptResult["messagesSnapshot"][number],
|
||||
],
|
||||
}),
|
||||
);
|
||||
return overflowError;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,13 @@ vi.mock("../../utils.js", () => ({
|
||||
|
||||
import { log } from "./logger.js";
|
||||
import { runEmbeddedPiAgent } from "./run.js";
|
||||
import { makeAttemptResult, mockOverflowRetrySuccess } from "./run.overflow-compaction.fixture.js";
|
||||
import {
|
||||
makeAttemptResult,
|
||||
makeCompactionSuccess,
|
||||
makeOverflowError,
|
||||
mockOverflowRetrySuccess,
|
||||
queueOverflowAttemptWithOversizedToolOutput,
|
||||
} from "./run.overflow-compaction.fixture.js";
|
||||
import {
|
||||
mockedCompactDirect,
|
||||
mockedRunEmbeddedAttempt,
|
||||
@@ -86,15 +92,13 @@ describe("overflow compaction in run loop", () => {
|
||||
.mockResolvedValueOnce(makeAttemptResult({ promptError: overflowHintError }))
|
||||
.mockResolvedValueOnce(makeAttemptResult({ promptError: null }));
|
||||
|
||||
mockedCompactDirect.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
compacted: true,
|
||||
result: {
|
||||
mockedCompactDirect.mockResolvedValueOnce(
|
||||
makeCompactionSuccess({
|
||||
summary: "Compacted session",
|
||||
firstKeptEntryId: "entry-6",
|
||||
tokensBefore: 140000,
|
||||
},
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await runEmbeddedPiAgent(baseParams);
|
||||
|
||||
@@ -105,7 +109,7 @@ describe("overflow compaction in run loop", () => {
|
||||
});
|
||||
|
||||
it("returns error if compaction fails", async () => {
|
||||
const overflowError = new Error("request_too_large: Request size exceeds model context window");
|
||||
const overflowError = makeOverflowError();
|
||||
|
||||
mockedRunEmbeddedAttempt.mockResolvedValue(makeAttemptResult({ promptError: overflowError }));
|
||||
|
||||
@@ -125,21 +129,8 @@ describe("overflow compaction in run loop", () => {
|
||||
});
|
||||
|
||||
it("falls back to tool-result truncation and retries when oversized results are detected", async () => {
|
||||
const overflowError = new Error("request_too_large: Request size exceeds model context window");
|
||||
|
||||
mockedRunEmbeddedAttempt
|
||||
.mockResolvedValueOnce(
|
||||
makeAttemptResult({
|
||||
promptError: overflowError,
|
||||
messagesSnapshot: [
|
||||
{
|
||||
role: "assistant",
|
||||
content: "big tool output",
|
||||
} as unknown as EmbeddedRunAttemptResult["messagesSnapshot"][number],
|
||||
],
|
||||
}),
|
||||
)
|
||||
.mockResolvedValueOnce(makeAttemptResult({ promptError: null }));
|
||||
queueOverflowAttemptWithOversizedToolOutput(mockedRunEmbeddedAttempt, makeOverflowError());
|
||||
mockedRunEmbeddedAttempt.mockResolvedValueOnce(makeAttemptResult({ promptError: null }));
|
||||
|
||||
mockedCompactDirect.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
@@ -167,7 +158,7 @@ describe("overflow compaction in run loop", () => {
|
||||
});
|
||||
|
||||
it("retries compaction up to 3 times before giving up", async () => {
|
||||
const overflowError = new Error("request_too_large: Request size exceeds model context window");
|
||||
const overflowError = makeOverflowError();
|
||||
|
||||
// 4 overflow errors: 3 compaction retries + final failure
|
||||
mockedRunEmbeddedAttempt
|
||||
@@ -177,21 +168,27 @@ describe("overflow compaction in run loop", () => {
|
||||
.mockResolvedValueOnce(makeAttemptResult({ promptError: overflowError }));
|
||||
|
||||
mockedCompactDirect
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
compacted: true,
|
||||
result: { summary: "Compacted 1", firstKeptEntryId: "entry-3", tokensBefore: 180000 },
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
compacted: true,
|
||||
result: { summary: "Compacted 2", firstKeptEntryId: "entry-5", tokensBefore: 160000 },
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
compacted: true,
|
||||
result: { summary: "Compacted 3", firstKeptEntryId: "entry-7", tokensBefore: 140000 },
|
||||
});
|
||||
.mockResolvedValueOnce(
|
||||
makeCompactionSuccess({
|
||||
summary: "Compacted 1",
|
||||
firstKeptEntryId: "entry-3",
|
||||
tokensBefore: 180000,
|
||||
}),
|
||||
)
|
||||
.mockResolvedValueOnce(
|
||||
makeCompactionSuccess({
|
||||
summary: "Compacted 2",
|
||||
firstKeptEntryId: "entry-5",
|
||||
tokensBefore: 160000,
|
||||
}),
|
||||
)
|
||||
.mockResolvedValueOnce(
|
||||
makeCompactionSuccess({
|
||||
summary: "Compacted 3",
|
||||
firstKeptEntryId: "entry-7",
|
||||
tokensBefore: 140000,
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await runEmbeddedPiAgent(baseParams);
|
||||
|
||||
@@ -204,7 +201,7 @@ describe("overflow compaction in run loop", () => {
|
||||
});
|
||||
|
||||
it("succeeds after second compaction attempt", async () => {
|
||||
const overflowError = new Error("request_too_large: Request size exceeds model context window");
|
||||
const overflowError = makeOverflowError();
|
||||
|
||||
mockedRunEmbeddedAttempt
|
||||
.mockResolvedValueOnce(makeAttemptResult({ promptError: overflowError }))
|
||||
@@ -212,16 +209,20 @@ describe("overflow compaction in run loop", () => {
|
||||
.mockResolvedValueOnce(makeAttemptResult({ promptError: null }));
|
||||
|
||||
mockedCompactDirect
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
compacted: true,
|
||||
result: { summary: "Compacted 1", firstKeptEntryId: "entry-3", tokensBefore: 180000 },
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
compacted: true,
|
||||
result: { summary: "Compacted 2", firstKeptEntryId: "entry-5", tokensBefore: 160000 },
|
||||
});
|
||||
.mockResolvedValueOnce(
|
||||
makeCompactionSuccess({
|
||||
summary: "Compacted 1",
|
||||
firstKeptEntryId: "entry-3",
|
||||
tokensBefore: 180000,
|
||||
}),
|
||||
)
|
||||
.mockResolvedValueOnce(
|
||||
makeCompactionSuccess({
|
||||
summary: "Compacted 2",
|
||||
firstKeptEntryId: "entry-5",
|
||||
tokensBefore: 160000,
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await runEmbeddedPiAgent(baseParams);
|
||||
|
||||
@@ -259,15 +260,13 @@ describe("overflow compaction in run loop", () => {
|
||||
)
|
||||
.mockResolvedValueOnce(makeAttemptResult({ promptError: null }));
|
||||
|
||||
mockedCompactDirect.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
compacted: true,
|
||||
result: {
|
||||
mockedCompactDirect.mockResolvedValueOnce(
|
||||
makeCompactionSuccess({
|
||||
summary: "Compacted session",
|
||||
firstKeptEntryId: "entry-5",
|
||||
tokensBefore: 150000,
|
||||
},
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await runEmbeddedPiAgent(baseParams);
|
||||
|
||||
|
||||
@@ -2,7 +2,13 @@ import "./run.overflow-compaction.mocks.shared.js";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { pickFallbackThinkingLevel } from "../pi-embedded-helpers.js";
|
||||
import { runEmbeddedPiAgent } from "./run.js";
|
||||
import { makeAttemptResult, mockOverflowRetrySuccess } from "./run.overflow-compaction.fixture.js";
|
||||
import {
|
||||
makeAttemptResult,
|
||||
makeCompactionSuccess,
|
||||
makeOverflowError,
|
||||
mockOverflowRetrySuccess,
|
||||
queueOverflowAttemptWithOversizedToolOutput,
|
||||
} from "./run.overflow-compaction.fixture.js";
|
||||
import { mockedGlobalHookRunner } from "./run.overflow-compaction.mocks.shared.js";
|
||||
import {
|
||||
mockedCompactDirect,
|
||||
@@ -11,7 +17,6 @@ import {
|
||||
mockedTruncateOversizedToolResultsInSession,
|
||||
overflowBaseRunParams,
|
||||
} from "./run.overflow-compaction.shared-test.js";
|
||||
import type { EmbeddedRunAttemptResult } from "./run/types.js";
|
||||
const mockedPickFallbackThinkingLevel = vi.mocked(pickFallbackThinkingLevel);
|
||||
|
||||
describe("runEmbeddedPiAgent overflow compaction trigger routing", () => {
|
||||
@@ -67,20 +72,11 @@ describe("runEmbeddedPiAgent overflow compaction trigger routing", () => {
|
||||
});
|
||||
|
||||
it("does not reset compaction attempt budget after successful tool-result truncation", async () => {
|
||||
const overflowError = new Error("request_too_large: Request size exceeds model context window");
|
||||
|
||||
const overflowError = queueOverflowAttemptWithOversizedToolOutput(
|
||||
mockedRunEmbeddedAttempt,
|
||||
makeOverflowError(),
|
||||
);
|
||||
mockedRunEmbeddedAttempt
|
||||
.mockResolvedValueOnce(
|
||||
makeAttemptResult({
|
||||
promptError: overflowError,
|
||||
messagesSnapshot: [
|
||||
{
|
||||
role: "assistant",
|
||||
content: "big tool output",
|
||||
} as unknown as EmbeddedRunAttemptResult["messagesSnapshot"][number],
|
||||
],
|
||||
}),
|
||||
)
|
||||
.mockResolvedValueOnce(makeAttemptResult({ promptError: overflowError }))
|
||||
.mockResolvedValueOnce(makeAttemptResult({ promptError: overflowError }))
|
||||
.mockResolvedValueOnce(makeAttemptResult({ promptError: overflowError }));
|
||||
@@ -91,16 +87,20 @@ describe("runEmbeddedPiAgent overflow compaction trigger routing", () => {
|
||||
compacted: false,
|
||||
reason: "nothing to compact",
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
compacted: true,
|
||||
result: { summary: "Compacted 2", firstKeptEntryId: "entry-5", tokensBefore: 160000 },
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
compacted: true,
|
||||
result: { summary: "Compacted 3", firstKeptEntryId: "entry-7", tokensBefore: 140000 },
|
||||
});
|
||||
.mockResolvedValueOnce(
|
||||
makeCompactionSuccess({
|
||||
summary: "Compacted 2",
|
||||
firstKeptEntryId: "entry-5",
|
||||
tokensBefore: 160000,
|
||||
}),
|
||||
)
|
||||
.mockResolvedValueOnce(
|
||||
makeCompactionSuccess({
|
||||
summary: "Compacted 3",
|
||||
firstKeptEntryId: "entry-7",
|
||||
tokensBefore: 140000,
|
||||
}),
|
||||
);
|
||||
|
||||
mockedSessionLikelyHasOversizedToolResults.mockReturnValue(true);
|
||||
mockedTruncateOversizedToolResultsInSession.mockResolvedValueOnce({
|
||||
|
||||
Reference in New Issue
Block a user