mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:00:43 +00:00
test: share get-reply edge fixtures
This commit is contained in:
@@ -86,6 +86,62 @@ function parseInlineDirectivesForTest(body: string) {
|
||||
};
|
||||
}
|
||||
|
||||
async function resolveHelloWithModelDefaults(params: {
|
||||
defaultThinking: "off" | "low";
|
||||
defaultReasoning: "on";
|
||||
}) {
|
||||
const resolveDefaultThinkingLevel = vi.fn(async () => params.defaultThinking);
|
||||
const resolveDefaultReasoningLevel = vi.fn(async () => params.defaultReasoning);
|
||||
mocks.createModelSelectionState.mockResolvedValueOnce({
|
||||
provider: "openai",
|
||||
model: "gpt-4o-mini",
|
||||
allowedModelKeys: new Set<string>(),
|
||||
allowedModelCatalog: [],
|
||||
resetModelOverride: false,
|
||||
resolveDefaultThinkingLevel,
|
||||
resolveDefaultReasoningLevel,
|
||||
});
|
||||
|
||||
const result = await resolveReplyDirectives({
|
||||
ctx: buildTestCtx({
|
||||
Body: "hello",
|
||||
CommandBody: "hello",
|
||||
}),
|
||||
cfg: {},
|
||||
agentId: "main",
|
||||
agentDir: "/tmp/main-agent",
|
||||
workspaceDir: "/tmp",
|
||||
agentCfg: {},
|
||||
sessionCtx: {
|
||||
Body: "hello",
|
||||
BodyStripped: "hello",
|
||||
BodyForAgent: "hello",
|
||||
CommandBody: "hello",
|
||||
Provider: "whatsapp",
|
||||
} as TemplateContext,
|
||||
sessionEntry: makeSessionEntry(),
|
||||
sessionStore: {},
|
||||
sessionKey: "agent:main:whatsapp:+2000",
|
||||
storePath: "/tmp/sessions.json",
|
||||
sessionScope: "per-sender",
|
||||
groupResolution: undefined,
|
||||
isGroup: false,
|
||||
triggerBodyNormalized: "hello",
|
||||
commandAuthorized: false,
|
||||
defaultProvider: "openai",
|
||||
defaultModel: "gpt-4o-mini",
|
||||
aliasIndex: { byAlias: new Map(), byKey: new Map() },
|
||||
provider: "openai",
|
||||
model: "gpt-4o-mini",
|
||||
hasResolvedHeartbeatModelOverride: false,
|
||||
typing: makeTypingController(),
|
||||
opts: undefined,
|
||||
skillFilter: undefined,
|
||||
});
|
||||
|
||||
return { result, resolveDefaultReasoningLevel };
|
||||
}
|
||||
|
||||
vi.mock("../../agents/agent-scope.js", () => ({
|
||||
listAgentEntries: vi.fn(() => []),
|
||||
}));
|
||||
@@ -356,53 +412,9 @@ describe("resolveReplyDirectives", () => {
|
||||
});
|
||||
|
||||
it("uses the model reasoning default when thinking is off", async () => {
|
||||
const resolveDefaultThinkingLevel = vi.fn(async () => "off");
|
||||
const resolveDefaultReasoningLevel = vi.fn(async () => "on");
|
||||
mocks.createModelSelectionState.mockResolvedValueOnce({
|
||||
provider: "openai",
|
||||
model: "gpt-4o-mini",
|
||||
allowedModelKeys: new Set<string>(),
|
||||
allowedModelCatalog: [],
|
||||
resetModelOverride: false,
|
||||
resolveDefaultThinkingLevel,
|
||||
resolveDefaultReasoningLevel,
|
||||
});
|
||||
|
||||
const result = await resolveReplyDirectives({
|
||||
ctx: buildTestCtx({
|
||||
Body: "hello",
|
||||
CommandBody: "hello",
|
||||
}),
|
||||
cfg: {},
|
||||
agentId: "main",
|
||||
agentDir: "/tmp/main-agent",
|
||||
workspaceDir: "/tmp",
|
||||
agentCfg: {},
|
||||
sessionCtx: {
|
||||
Body: "hello",
|
||||
BodyStripped: "hello",
|
||||
BodyForAgent: "hello",
|
||||
CommandBody: "hello",
|
||||
Provider: "whatsapp",
|
||||
} as TemplateContext,
|
||||
sessionEntry: makeSessionEntry(),
|
||||
sessionStore: {},
|
||||
sessionKey: "agent:main:whatsapp:+2000",
|
||||
storePath: "/tmp/sessions.json",
|
||||
sessionScope: "per-sender",
|
||||
groupResolution: undefined,
|
||||
isGroup: false,
|
||||
triggerBodyNormalized: "hello",
|
||||
commandAuthorized: false,
|
||||
defaultProvider: "openai",
|
||||
defaultModel: "gpt-4o-mini",
|
||||
aliasIndex: { byAlias: new Map(), byKey: new Map() },
|
||||
provider: "openai",
|
||||
model: "gpt-4o-mini",
|
||||
hasResolvedHeartbeatModelOverride: false,
|
||||
typing: makeTypingController(),
|
||||
opts: undefined,
|
||||
skillFilter: undefined,
|
||||
const { result, resolveDefaultReasoningLevel } = await resolveHelloWithModelDefaults({
|
||||
defaultThinking: "off",
|
||||
defaultReasoning: "on",
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@@ -416,53 +428,9 @@ describe("resolveReplyDirectives", () => {
|
||||
});
|
||||
|
||||
it("skips the model reasoning default when thinking is active", async () => {
|
||||
const resolveDefaultThinkingLevel = vi.fn(async () => "low");
|
||||
const resolveDefaultReasoningLevel = vi.fn(async () => "on");
|
||||
mocks.createModelSelectionState.mockResolvedValueOnce({
|
||||
provider: "openai",
|
||||
model: "gpt-4o-mini",
|
||||
allowedModelKeys: new Set<string>(),
|
||||
allowedModelCatalog: [],
|
||||
resetModelOverride: false,
|
||||
resolveDefaultThinkingLevel,
|
||||
resolveDefaultReasoningLevel,
|
||||
});
|
||||
|
||||
const result = await resolveReplyDirectives({
|
||||
ctx: buildTestCtx({
|
||||
Body: "hello",
|
||||
CommandBody: "hello",
|
||||
}),
|
||||
cfg: {},
|
||||
agentId: "main",
|
||||
agentDir: "/tmp/main-agent",
|
||||
workspaceDir: "/tmp",
|
||||
agentCfg: {},
|
||||
sessionCtx: {
|
||||
Body: "hello",
|
||||
BodyStripped: "hello",
|
||||
BodyForAgent: "hello",
|
||||
CommandBody: "hello",
|
||||
Provider: "whatsapp",
|
||||
} as TemplateContext,
|
||||
sessionEntry: makeSessionEntry(),
|
||||
sessionStore: {},
|
||||
sessionKey: "agent:main:whatsapp:+2000",
|
||||
storePath: "/tmp/sessions.json",
|
||||
sessionScope: "per-sender",
|
||||
groupResolution: undefined,
|
||||
isGroup: false,
|
||||
triggerBodyNormalized: "hello",
|
||||
commandAuthorized: false,
|
||||
defaultProvider: "openai",
|
||||
defaultModel: "gpt-4o-mini",
|
||||
aliasIndex: { byAlias: new Map(), byKey: new Map() },
|
||||
provider: "openai",
|
||||
model: "gpt-4o-mini",
|
||||
hasResolvedHeartbeatModelOverride: false,
|
||||
typing: makeTypingController(),
|
||||
opts: undefined,
|
||||
skillFilter: undefined,
|
||||
const { result, resolveDefaultReasoningLevel } = await resolveHelloWithModelDefaults({
|
||||
defaultThinking: "low",
|
||||
defaultReasoning: "on",
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
|
||||
@@ -116,6 +116,33 @@ async function expectInlineActionSkipped(params: {
|
||||
expect(handleCommandsMock).not.toHaveBeenCalled();
|
||||
}
|
||||
|
||||
async function runInlineStatusAction(storePath?: string) {
|
||||
const typing = createTypingController();
|
||||
const ctx = buildTestCtx({
|
||||
Body: "/status",
|
||||
CommandBody: "/status",
|
||||
});
|
||||
const result = await handleInlineActions(
|
||||
createHandleInlineActionsInput({
|
||||
ctx,
|
||||
typing,
|
||||
cleanedBody: stripInlineStatus("/status").cleaned,
|
||||
command: {
|
||||
isAuthorizedSender: true,
|
||||
rawBodyNormalized: "/status",
|
||||
commandBodyNormalized: "/status",
|
||||
},
|
||||
overrides: {
|
||||
allowTextCommands: true,
|
||||
inlineStatusRequested: true,
|
||||
...(storePath ? { storePath } : {}),
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
return { result, typing };
|
||||
}
|
||||
|
||||
describe("handleInlineActions", () => {
|
||||
beforeEach(() => {
|
||||
handleCommandsMock.mockReset();
|
||||
@@ -235,28 +262,7 @@ describe("handleInlineActions", () => {
|
||||
});
|
||||
|
||||
it("does not run command handlers after replying to an inline status-only turn", async () => {
|
||||
const typing = createTypingController();
|
||||
const ctx = buildTestCtx({
|
||||
Body: "/status",
|
||||
CommandBody: "/status",
|
||||
});
|
||||
|
||||
const result = await handleInlineActions(
|
||||
createHandleInlineActionsInput({
|
||||
ctx,
|
||||
typing,
|
||||
cleanedBody: stripInlineStatus("/status").cleaned,
|
||||
command: {
|
||||
isAuthorizedSender: true,
|
||||
rawBodyNormalized: "/status",
|
||||
commandBodyNormalized: "/status",
|
||||
},
|
||||
overrides: {
|
||||
allowTextCommands: true,
|
||||
inlineStatusRequested: true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
const { result, typing } = await runInlineStatusAction();
|
||||
|
||||
expect(result).toEqual({ kind: "reply", reply: undefined });
|
||||
expect(buildStatusReplyMock).toHaveBeenCalledTimes(1);
|
||||
@@ -270,29 +276,7 @@ describe("handleInlineActions", () => {
|
||||
});
|
||||
|
||||
it("preserves storePath when routing inline status through the shared status builder", async () => {
|
||||
const typing = createTypingController();
|
||||
const ctx = buildTestCtx({
|
||||
Body: "/status",
|
||||
CommandBody: "/status",
|
||||
});
|
||||
|
||||
const result = await handleInlineActions(
|
||||
createHandleInlineActionsInput({
|
||||
ctx,
|
||||
typing,
|
||||
cleanedBody: stripInlineStatus("/status").cleaned,
|
||||
command: {
|
||||
isAuthorizedSender: true,
|
||||
rawBodyNormalized: "/status",
|
||||
commandBodyNormalized: "/status",
|
||||
},
|
||||
overrides: {
|
||||
allowTextCommands: true,
|
||||
inlineStatusRequested: true,
|
||||
storePath: "/tmp/inline-status-store.json",
|
||||
},
|
||||
}),
|
||||
);
|
||||
const { result } = await runInlineStatusAction("/tmp/inline-status-store.json");
|
||||
|
||||
expect(result).toEqual({ kind: "reply", reply: undefined });
|
||||
expect(buildStatusReplyMock).toHaveBeenCalledWith(
|
||||
|
||||
@@ -206,6 +206,15 @@ function baseParams(
|
||||
};
|
||||
}
|
||||
|
||||
function ownerParams(): Parameters<typeof runPreparedReply>[0] {
|
||||
const params = baseParams();
|
||||
params.command = {
|
||||
...(params.command as Record<string, unknown>),
|
||||
senderIsOwner: true,
|
||||
} as never;
|
||||
return params;
|
||||
}
|
||||
|
||||
describe("runPreparedReply media-only handling", () => {
|
||||
beforeAll(async () => {
|
||||
({ runPreparedReply } = await import("./get-reply-run.js"));
|
||||
@@ -752,11 +761,7 @@ describe("runPreparedReply media-only handling", () => {
|
||||
vi.mocked(drainFormattedSystemEvents).mockResolvedValueOnce(
|
||||
"System (untrusted): [t] External webhook payload.",
|
||||
);
|
||||
const params = baseParams();
|
||||
params.command = {
|
||||
...(params.command as Record<string, unknown>),
|
||||
senderIsOwner: true,
|
||||
} as never;
|
||||
const params = ownerParams();
|
||||
|
||||
await runPreparedReply(params);
|
||||
|
||||
@@ -766,11 +771,7 @@ describe("runPreparedReply media-only handling", () => {
|
||||
|
||||
it("keeps sender ownership when drained system events are trusted", async () => {
|
||||
vi.mocked(drainFormattedSystemEvents).mockResolvedValueOnce("System: [t] Trusted event.");
|
||||
const params = baseParams();
|
||||
params.command = {
|
||||
...(params.command as Record<string, unknown>),
|
||||
senderIsOwner: true,
|
||||
} as never;
|
||||
const params = ownerParams();
|
||||
|
||||
await runPreparedReply(params);
|
||||
|
||||
@@ -782,11 +783,7 @@ describe("runPreparedReply media-only handling", () => {
|
||||
vi.mocked(drainFormattedSystemEvents).mockResolvedValueOnce(
|
||||
"System: [t] Relay text mentions System (untrusted): but event is trusted.",
|
||||
);
|
||||
const params = baseParams();
|
||||
params.command = {
|
||||
...(params.command as Record<string, unknown>),
|
||||
senderIsOwner: true,
|
||||
} as never;
|
||||
const params = ownerParams();
|
||||
|
||||
await runPreparedReply(params);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user