mirror of https://github.com/openclaw/openclaw.git
refactor: share cron and ollama test helpers
This commit is contained in:
parent
8473a29da7
commit
fff514c7f2
|
|
@ -1,31 +1,11 @@
|
|||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { jsonResponse, requestBodyText, requestUrl } from "../test-helpers/http.js";
|
||||
import {
|
||||
enrichOllamaModelsWithContext,
|
||||
resolveOllamaApiBase,
|
||||
type OllamaTagModel,
|
||||
} from "./ollama-models.js";
|
||||
|
||||
function jsonResponse(body: unknown, status = 200): Response {
|
||||
return new Response(JSON.stringify(body), {
|
||||
status,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
function requestUrl(input: string | URL | Request): string {
|
||||
if (typeof input === "string") {
|
||||
return input;
|
||||
}
|
||||
if (input instanceof URL) {
|
||||
return input.toString();
|
||||
}
|
||||
return input.url;
|
||||
}
|
||||
|
||||
function requestBody(body: BodyInit | null | undefined): string {
|
||||
return typeof body === "string" ? body : "{}";
|
||||
}
|
||||
|
||||
describe("ollama-models", () => {
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
|
|
@ -43,7 +23,7 @@ describe("ollama-models", () => {
|
|||
if (!url.endsWith("/api/show")) {
|
||||
throw new Error(`Unexpected fetch: ${url}`);
|
||||
}
|
||||
const body = JSON.parse(requestBody(init?.body)) as { name?: string };
|
||||
const body = JSON.parse(requestBodyText(init?.body)) as { name?: string };
|
||||
if (body.name === "llama3:8b") {
|
||||
return jsonResponse({ model_info: { "llama.context_length": 65536 } });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { jsonResponse, requestBodyText, requestUrl } from "../test-helpers/http.js";
|
||||
import type { WizardPrompter } from "../wizard/prompts.js";
|
||||
import {
|
||||
configureOllamaNonInteractive,
|
||||
|
|
@ -23,27 +24,6 @@ vi.mock("./oauth-env.js", () => ({
|
|||
isRemoteEnvironment: isRemoteEnvironmentMock,
|
||||
}));
|
||||
|
||||
function jsonResponse(body: unknown, status = 200): Response {
|
||||
return new Response(JSON.stringify(body), {
|
||||
status,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
function requestUrl(input: string | URL | Request): string {
|
||||
if (typeof input === "string") {
|
||||
return input;
|
||||
}
|
||||
if (input instanceof URL) {
|
||||
return input.toString();
|
||||
}
|
||||
return input.url;
|
||||
}
|
||||
|
||||
function requestBody(body: BodyInit | null | undefined): string {
|
||||
return typeof body === "string" ? body : "{}";
|
||||
}
|
||||
|
||||
function createOllamaFetchMock(params: {
|
||||
tags?: string[];
|
||||
show?: Record<string, number | undefined>;
|
||||
|
|
@ -61,7 +41,7 @@ function createOllamaFetchMock(params: {
|
|||
return jsonResponse({ models: (params.tags ?? []).map((name) => ({ name })) });
|
||||
}
|
||||
if (url.endsWith("/api/show")) {
|
||||
const body = JSON.parse(requestBody(init?.body)) as { name?: string };
|
||||
const body = JSON.parse(requestBodyText(init?.body)) as { name?: string };
|
||||
const contextWindow = body.name ? params.show?.[body.name] : undefined;
|
||||
return contextWindow
|
||||
? jsonResponse({ model_info: { "llama.context_length": contextWindow } })
|
||||
|
|
@ -359,7 +339,7 @@ describe("ollama setup", () => {
|
|||
});
|
||||
|
||||
const pullRequest = fetchMock.mock.calls[1]?.[1];
|
||||
expect(JSON.parse(requestBody(pullRequest?.body))).toEqual({ name: "llama3.2:latest" });
|
||||
expect(JSON.parse(requestBodyText(pullRequest?.body))).toEqual({ name: "llama3.2:latest" });
|
||||
expect(result.agents?.defaults?.model).toEqual(
|
||||
expect.objectContaining({ primary: "ollama/llama3.2:latest" }),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import "./isolated-agent.mocks.js";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { runEmbeddedPiAgent } from "../agents/pi-embedded.js";
|
||||
import { createCliDeps, mockAgentPayloads } from "./isolated-agent.delivery.test-helpers.js";
|
||||
import { runCronIsolatedAgentTurn } from "./isolated-agent.js";
|
||||
import {
|
||||
makeCfg,
|
||||
|
|
@ -9,27 +10,6 @@ import {
|
|||
writeSessionStoreEntries,
|
||||
} from "./isolated-agent.test-harness.js";
|
||||
|
||||
function makeDeps() {
|
||||
return {
|
||||
sendMessageSlack: vi.fn(),
|
||||
sendMessageWhatsApp: vi.fn(),
|
||||
sendMessageTelegram: vi.fn(),
|
||||
sendMessageDiscord: vi.fn(),
|
||||
sendMessageSignal: vi.fn(),
|
||||
sendMessageIMessage: vi.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
function mockEmbeddedOk() {
|
||||
vi.mocked(runEmbeddedPiAgent).mockResolvedValue({
|
||||
payloads: [{ text: "ok" }],
|
||||
meta: {
|
||||
durationMs: 5,
|
||||
agentMeta: { sessionId: "s", provider: "p", model: "m" },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function lastEmbeddedLane(): string | undefined {
|
||||
const calls = vi.mocked(runEmbeddedPiAgent).mock.calls;
|
||||
expect(calls.length).toBeGreaterThan(0);
|
||||
|
|
@ -45,11 +25,11 @@ async function runLaneCase(home: string, lane?: string) {
|
|||
lastTo: "",
|
||||
},
|
||||
});
|
||||
mockEmbeddedOk();
|
||||
mockAgentPayloads([{ text: "ok" }]);
|
||||
|
||||
await runCronIsolatedAgentTurn({
|
||||
cfg: makeCfg(home, storePath),
|
||||
deps: makeDeps(),
|
||||
deps: createCliDeps(),
|
||||
job: makeJob({ kind: "agentTurn", message: "do it", deliver: false }),
|
||||
message: "do it",
|
||||
sessionKey: "cron:job-1",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import "./isolated-agent.mocks.js";
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { loadModelCatalog } from "../agents/model-catalog.js";
|
||||
import { runEmbeddedPiAgent } from "../agents/pi-embedded.js";
|
||||
import { createCliDeps, mockAgentPayloads } from "./isolated-agent.delivery.test-helpers.js";
|
||||
import { runCronIsolatedAgentTurn } from "./isolated-agent.js";
|
||||
import {
|
||||
makeCfg,
|
||||
|
|
@ -13,27 +14,6 @@ import type { CronJob } from "./types.js";
|
|||
|
||||
const withTempHome = withTempCronHome;
|
||||
|
||||
function makeDeps() {
|
||||
return {
|
||||
sendMessageSlack: vi.fn(),
|
||||
sendMessageWhatsApp: vi.fn(),
|
||||
sendMessageTelegram: vi.fn(),
|
||||
sendMessageDiscord: vi.fn(),
|
||||
sendMessageSignal: vi.fn(),
|
||||
sendMessageIMessage: vi.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
function mockEmbeddedOk() {
|
||||
vi.mocked(runEmbeddedPiAgent).mockResolvedValue({
|
||||
payloads: [{ text: "ok" }],
|
||||
meta: {
|
||||
durationMs: 5,
|
||||
agentMeta: { sessionId: "s", provider: "p", model: "m" },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the provider and model from the last runEmbeddedPiAgent call.
|
||||
*/
|
||||
|
|
@ -62,7 +42,7 @@ async function runTurnCore(home: string, options: TurnOptions = {}) {
|
|||
},
|
||||
...options.storeEntries,
|
||||
});
|
||||
mockEmbeddedOk();
|
||||
mockAgentPayloads([{ text: "ok" }]);
|
||||
|
||||
const jobPayload = options.jobPayload ?? {
|
||||
kind: "agentTurn" as const,
|
||||
|
|
@ -72,7 +52,7 @@ async function runTurnCore(home: string, options: TurnOptions = {}) {
|
|||
|
||||
const res = await runCronIsolatedAgentTurn({
|
||||
cfg: makeCfg(home, storePath, options.cfgOverrides),
|
||||
deps: makeDeps(),
|
||||
deps: createCliDeps(),
|
||||
job: makeJob(jobPayload),
|
||||
message: DEFAULT_MESSAGE,
|
||||
sessionKey: options.sessionKey ?? "cron:job-1",
|
||||
|
|
@ -310,7 +290,7 @@ describe("cron model formatting and precedence edge cases", () => {
|
|||
|
||||
// Step 2: No job model, session store says openai
|
||||
vi.mocked(runEmbeddedPiAgent).mockClear();
|
||||
mockEmbeddedOk();
|
||||
mockAgentPayloads([{ text: "ok" }]);
|
||||
const step2 = await runTurn(home, {
|
||||
jobPayload: { kind: "agentTurn", message: DEFAULT_MESSAGE, deliver: false },
|
||||
storeEntries: {
|
||||
|
|
@ -327,7 +307,7 @@ describe("cron model formatting and precedence edge cases", () => {
|
|||
|
||||
// Step 3: Job payload says anthropic, session store still says openai
|
||||
vi.mocked(runEmbeddedPiAgent).mockClear();
|
||||
mockEmbeddedOk();
|
||||
mockAgentPayloads([{ text: "ok" }]);
|
||||
const step3 = await runTurn(home, {
|
||||
jobPayload: {
|
||||
kind: "agentTurn",
|
||||
|
|
@ -365,7 +345,7 @@ describe("cron model formatting and precedence edge cases", () => {
|
|||
|
||||
// Run 2: no override — must revert to default anthropic
|
||||
vi.mocked(runEmbeddedPiAgent).mockClear();
|
||||
mockEmbeddedOk();
|
||||
mockAgentPayloads([{ text: "ok" }]);
|
||||
const r2 = await runTurn(home, {
|
||||
jobPayload: { kind: "agentTurn", message: DEFAULT_MESSAGE, deliver: false },
|
||||
});
|
||||
|
|
|
|||
|
|
@ -133,6 +133,16 @@ async function runTelegramDeliveryResult(bestEffort: boolean) {
|
|||
return outcome;
|
||||
}
|
||||
|
||||
function expectSuccessfulTelegramTextDelivery(params: {
|
||||
res: Awaited<ReturnType<typeof runCronIsolatedAgentTurn>>;
|
||||
deps: CliDeps;
|
||||
}): void {
|
||||
expect(params.res.status).toBe("ok");
|
||||
expect(params.res.delivered).toBe(true);
|
||||
expect(params.res.deliveryAttempted).toBe(true);
|
||||
expect(runSubagentAnnounceFlow).not.toHaveBeenCalled();
|
||||
}
|
||||
|
||||
async function runSignalDeliveryResult(bestEffort: boolean) {
|
||||
let outcome:
|
||||
| {
|
||||
|
|
@ -379,31 +389,11 @@ describe("runCronIsolatedAgentTurn", () => {
|
|||
});
|
||||
|
||||
it("delivers text directly when best-effort is disabled", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const storePath = await writeSessionStore(home, { lastProvider: "webchat", lastTo: "" });
|
||||
const deps = createCliDeps();
|
||||
mockAgentPayloads([{ text: "hello from cron" }]);
|
||||
|
||||
const res = await runTelegramAnnounceTurn({
|
||||
home,
|
||||
storePath,
|
||||
deps,
|
||||
delivery: {
|
||||
mode: "announce",
|
||||
channel: "telegram",
|
||||
to: "123",
|
||||
bestEffort: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toBe("ok");
|
||||
expect(res.delivered).toBe(true);
|
||||
expect(res.deliveryAttempted).toBe(true);
|
||||
expect(runSubagentAnnounceFlow).not.toHaveBeenCalled();
|
||||
expectDirectTelegramDelivery(deps, {
|
||||
chatId: "123",
|
||||
text: "hello from cron",
|
||||
});
|
||||
const { res, deps } = await runTelegramDeliveryResult(false);
|
||||
expectSuccessfulTelegramTextDelivery({ res, deps });
|
||||
expectDirectTelegramDelivery(deps, {
|
||||
chatId: "123",
|
||||
text: "hello from cron",
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -459,10 +449,7 @@ describe("runCronIsolatedAgentTurn", () => {
|
|||
},
|
||||
});
|
||||
|
||||
expect(res.status).toBe("ok");
|
||||
expect(res.delivered).toBe(true);
|
||||
expect(res.deliveryAttempted).toBe(true);
|
||||
expect(runSubagentAnnounceFlow).not.toHaveBeenCalled();
|
||||
expectSuccessfulTelegramTextDelivery({ res, deps });
|
||||
expect(deps.sendMessageTelegram).toHaveBeenCalledTimes(2);
|
||||
expect(deps.sendMessageTelegram).toHaveBeenLastCalledWith(
|
||||
"123",
|
||||
|
|
@ -490,10 +477,7 @@ describe("runCronIsolatedAgentTurn", () => {
|
|||
|
||||
it("delivers text directly when best-effort is enabled", async () => {
|
||||
const { res, deps } = await runTelegramDeliveryResult(true);
|
||||
expect(res.status).toBe("ok");
|
||||
expect(res.delivered).toBe(true);
|
||||
expect(res.deliveryAttempted).toBe(true);
|
||||
expect(runSubagentAnnounceFlow).not.toHaveBeenCalled();
|
||||
expectSuccessfulTelegramTextDelivery({ res, deps });
|
||||
expectDirectTelegramDelivery(deps, {
|
||||
chatId: "123",
|
||||
text: "hello from cron",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
export function jsonResponse(body: unknown, status = 200): Response {
|
||||
return new Response(JSON.stringify(body), {
|
||||
status,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
export function requestUrl(input: string | URL | Request): string {
|
||||
if (typeof input === "string") {
|
||||
return input;
|
||||
}
|
||||
if (input instanceof URL) {
|
||||
return input.toString();
|
||||
}
|
||||
return input.url;
|
||||
}
|
||||
|
||||
export function requestBodyText(body: BodyInit | null | undefined): string {
|
||||
return typeof body === "string" ? body : "{}";
|
||||
}
|
||||
Loading…
Reference in New Issue