mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 09:41:11 +00:00
fix: preserve reset hook sender policy context
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
import type { AcpSessionStore } from "acpx/runtime";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { AcpRuntime } from "../runtime-api.js";
|
||||
import { AcpxRuntime } from "./runtime.js";
|
||||
|
||||
function makeRuntime(baseStore: AcpSessionStore): {
|
||||
type TestSessionStore = {
|
||||
load(sessionId: string): Promise<Record<string, unknown> | undefined>;
|
||||
save(record: Record<string, unknown>): Promise<void>;
|
||||
};
|
||||
|
||||
function makeRuntime(baseStore: TestSessionStore): {
|
||||
runtime: AcpxRuntime;
|
||||
wrappedStore: AcpSessionStore & { markFresh: (sessionKey: string) => void };
|
||||
wrappedStore: TestSessionStore & { markFresh: (sessionKey: string) => void };
|
||||
delegate: { close: AcpRuntime["close"] };
|
||||
} {
|
||||
const runtime = new AcpxRuntime({
|
||||
@@ -22,7 +26,7 @@ function makeRuntime(baseStore: AcpSessionStore): {
|
||||
runtime,
|
||||
wrappedStore: (
|
||||
runtime as unknown as {
|
||||
sessionStore: AcpSessionStore & { markFresh: (sessionKey: string) => void };
|
||||
sessionStore: TestSessionStore & { markFresh: (sessionKey: string) => void };
|
||||
}
|
||||
).sessionStore,
|
||||
delegate: (runtime as unknown as { delegate: { close: AcpRuntime["close"] } }).delegate,
|
||||
@@ -35,7 +39,7 @@ describe("AcpxRuntime fresh reset wrapper", () => {
|
||||
});
|
||||
|
||||
it("keeps stale persistent loads hidden until a fresh record is saved", async () => {
|
||||
const baseStore: AcpSessionStore = {
|
||||
const baseStore: TestSessionStore = {
|
||||
load: vi.fn(async () => ({ acpxRecordId: "stale" }) as never),
|
||||
save: vi.fn(async () => {}),
|
||||
};
|
||||
@@ -68,7 +72,7 @@ describe("AcpxRuntime fresh reset wrapper", () => {
|
||||
});
|
||||
|
||||
it("marks the session fresh after discardPersistentState close", async () => {
|
||||
const baseStore: AcpSessionStore = {
|
||||
const baseStore: TestSessionStore = {
|
||||
load: vi.fn(async () => ({ acpxRecordId: "stale" }) as never),
|
||||
save: vi.fn(async () => {}),
|
||||
};
|
||||
|
||||
@@ -163,7 +163,7 @@ describe("createMSTeamsReplyDispatcher", () => {
|
||||
if (!lastCreatedDispatcher) {
|
||||
throw new Error("createDispatcher must be called first");
|
||||
}
|
||||
await lastCreatedDispatcher.replyOptions.onPartialReply?.({ text });
|
||||
lastCreatedDispatcher.replyOptions.onPartialReply?.({ text });
|
||||
}
|
||||
|
||||
it("sends an informative status update on reply start for personal chats", async () => {
|
||||
|
||||
@@ -6,6 +6,9 @@ import type { HandleCommandsParams } from "./commands-types.js";
|
||||
import { parseInlineDirectives } from "./directive-handling.parse.js";
|
||||
|
||||
const triggerInternalHookMock = vi.hoisted(() => vi.fn().mockResolvedValue(undefined));
|
||||
const routeReplyMock = vi.hoisted(() =>
|
||||
vi.fn<(params: unknown) => Promise<{ ok: boolean }>>(async () => ({ ok: true })),
|
||||
);
|
||||
const resetMocks = vi.hoisted(() => ({
|
||||
resetConfiguredBindingTargetInPlace: vi.fn().mockResolvedValue({ ok: true as const }),
|
||||
resolveBoundAcpThreadSessionKey: vi.fn(() => undefined as string | undefined),
|
||||
@@ -49,6 +52,10 @@ vi.mock("./commands-handlers.runtime.js", () => ({
|
||||
loadCommandHandlers: () => [],
|
||||
}));
|
||||
|
||||
vi.mock("./route-reply.runtime.js", () => ({
|
||||
routeReply: (params: unknown) => routeReplyMock(params),
|
||||
}));
|
||||
|
||||
function buildResetParams(
|
||||
commandBody: string,
|
||||
cfg: OpenClawConfig,
|
||||
@@ -102,6 +109,7 @@ describe("handleCommands reset hooks", () => {
|
||||
vi.clearAllMocks();
|
||||
resetMocks.resetConfiguredBindingTargetInPlace.mockResolvedValue({ ok: true });
|
||||
resetMocks.resolveBoundAcpThreadSessionKey.mockReturnValue(undefined);
|
||||
triggerInternalHookMock.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
it("triggers hooks for /new commands", async () => {
|
||||
@@ -213,4 +221,38 @@ describe("handleCommands reset hooks", () => {
|
||||
expect(params.ctx.CommandBody).toBe("who are you");
|
||||
expect(params.ctx.AcpDispatchTailAfterReset).toBe(true);
|
||||
});
|
||||
|
||||
it("forwards non-id sender fields when reset hooks emit routed replies", async () => {
|
||||
triggerInternalHookMock.mockImplementationOnce(async (event: { messages: string[] }) => {
|
||||
event.messages.push("Reset hook says hi");
|
||||
});
|
||||
const params = buildResetParams(
|
||||
"/new",
|
||||
{
|
||||
commands: { text: true },
|
||||
channels: { whatsapp: { allowFrom: ["*"] } },
|
||||
} as OpenClawConfig,
|
||||
{
|
||||
SenderId: "id:whatsapp:123",
|
||||
SenderName: "Alice",
|
||||
SenderUsername: "alice_u",
|
||||
SenderE164: "+15551234567",
|
||||
OriginatingChannel: "whatsapp",
|
||||
OriginatingTo: "group:ops",
|
||||
MessageThreadId: "thread-1",
|
||||
},
|
||||
);
|
||||
|
||||
await maybeHandleResetCommand(params);
|
||||
|
||||
expect(routeReplyMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
requesterSenderId: "id:whatsapp:123",
|
||||
requesterSenderName: "Alice",
|
||||
requesterSenderUsername: "alice_u",
|
||||
requesterSenderE164: "+15551234567",
|
||||
threadId: "thread-1",
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -128,6 +128,9 @@ export async function emitResetCommandHooks(params: {
|
||||
sessionKey: params.sessionKey,
|
||||
accountId: params.ctx.AccountId,
|
||||
requesterSenderId: params.command.senderId,
|
||||
requesterSenderName: params.ctx.SenderName,
|
||||
requesterSenderUsername: params.ctx.SenderUsername,
|
||||
requesterSenderE164: params.ctx.SenderE164,
|
||||
threadId: params.ctx.MessageThreadId,
|
||||
cfg: params.cfg,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user