test: share get-reply edge fixtures

This commit is contained in:
Peter Steinberger
2026-04-20 19:55:22 +01:00
parent f48d040bf5
commit 43fa394b83
3 changed files with 103 additions and 154 deletions

View File

@@ -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({

View File

@@ -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(

View File

@@ -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);