mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-04 07:49:02 +00:00
test: dedupe auto-reply web and signal flows
This commit is contained in:
@@ -42,6 +42,39 @@ vi.mock("../../agents/subagent-registry.js", () => ({
|
||||
}));
|
||||
|
||||
describe("abort detection", () => {
|
||||
async function writeSessionStore(
|
||||
storePath: string,
|
||||
sessionIdsByKey: Record<string, string>,
|
||||
nowMs = Date.now(),
|
||||
) {
|
||||
const storeEntries = Object.fromEntries(
|
||||
Object.entries(sessionIdsByKey).map(([key, sessionId]) => [
|
||||
key,
|
||||
{ sessionId, updatedAt: nowMs },
|
||||
]),
|
||||
);
|
||||
await fs.writeFile(storePath, JSON.stringify(storeEntries, null, 2));
|
||||
}
|
||||
|
||||
async function createAbortConfig(params?: {
|
||||
commandsTextEnabled?: boolean;
|
||||
sessionIdsByKey?: Record<string, string>;
|
||||
nowMs?: number;
|
||||
}) {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-abort-"));
|
||||
const storePath = path.join(root, "sessions.json");
|
||||
const cfg = {
|
||||
session: { store: storePath },
|
||||
...(typeof params?.commandsTextEnabled === "boolean"
|
||||
? { commands: { text: params.commandsTextEnabled } }
|
||||
: {}),
|
||||
} as OpenClawConfig;
|
||||
if (params?.sessionIdsByKey) {
|
||||
await writeSessionStore(storePath, params.sessionIdsByKey, params.nowMs);
|
||||
}
|
||||
return { root, storePath, cfg };
|
||||
}
|
||||
|
||||
async function runStopCommand(params: {
|
||||
cfg: OpenClawConfig;
|
||||
sessionKey: string;
|
||||
@@ -142,9 +175,7 @@ describe("abort detection", () => {
|
||||
});
|
||||
|
||||
it("fast-aborts even when text commands are disabled", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-abort-"));
|
||||
const storePath = path.join(root, "sessions.json");
|
||||
const cfg = { session: { store: storePath }, commands: { text: false } } as OpenClawConfig;
|
||||
const { cfg } = await createAbortConfig({ commandsTextEnabled: false });
|
||||
|
||||
const result = await runStopCommand({
|
||||
cfg,
|
||||
@@ -157,24 +188,11 @@ describe("abort detection", () => {
|
||||
});
|
||||
|
||||
it("fast-abort clears queued followups and session lane", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-abort-"));
|
||||
const storePath = path.join(root, "sessions.json");
|
||||
const cfg = { session: { store: storePath } } as OpenClawConfig;
|
||||
const sessionKey = "telegram:123";
|
||||
const sessionId = "session-123";
|
||||
await fs.writeFile(
|
||||
storePath,
|
||||
JSON.stringify(
|
||||
{
|
||||
[sessionKey]: {
|
||||
sessionId,
|
||||
updatedAt: Date.now(),
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
const { root, cfg } = await createAbortConfig({
|
||||
sessionIdsByKey: { [sessionKey]: sessionId },
|
||||
});
|
||||
const followupRun: FollowupRun = {
|
||||
prompt: "queued",
|
||||
enqueuedAt: Date.now(),
|
||||
@@ -215,30 +233,16 @@ describe("abort detection", () => {
|
||||
});
|
||||
|
||||
it("fast-abort stops active subagent runs for requester session", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-abort-"));
|
||||
const storePath = path.join(root, "sessions.json");
|
||||
const cfg = { session: { store: storePath } } as OpenClawConfig;
|
||||
const sessionKey = "telegram:parent";
|
||||
const childKey = "agent:main:subagent:child-1";
|
||||
const sessionId = "session-parent";
|
||||
const childSessionId = "session-child";
|
||||
await fs.writeFile(
|
||||
storePath,
|
||||
JSON.stringify(
|
||||
{
|
||||
[sessionKey]: {
|
||||
sessionId,
|
||||
updatedAt: Date.now(),
|
||||
},
|
||||
[childKey]: {
|
||||
sessionId: childSessionId,
|
||||
updatedAt: Date.now(),
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
const { cfg } = await createAbortConfig({
|
||||
sessionIdsByKey: {
|
||||
[sessionKey]: sessionId,
|
||||
[childKey]: childSessionId,
|
||||
},
|
||||
});
|
||||
|
||||
subagentRegistryMocks.listSubagentRunsForRequester.mockReturnValueOnce([
|
||||
{
|
||||
@@ -264,36 +268,19 @@ describe("abort detection", () => {
|
||||
});
|
||||
|
||||
it("cascade stop kills depth-2 children when stopping depth-1 agent", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-abort-"));
|
||||
const storePath = path.join(root, "sessions.json");
|
||||
const cfg = { session: { store: storePath } } as OpenClawConfig;
|
||||
const sessionKey = "telegram:parent";
|
||||
const depth1Key = "agent:main:subagent:child-1";
|
||||
const depth2Key = "agent:main:subagent:child-1:subagent:grandchild-1";
|
||||
const sessionId = "session-parent";
|
||||
const depth1SessionId = "session-child";
|
||||
const depth2SessionId = "session-grandchild";
|
||||
await fs.writeFile(
|
||||
storePath,
|
||||
JSON.stringify(
|
||||
{
|
||||
[sessionKey]: {
|
||||
sessionId,
|
||||
updatedAt: Date.now(),
|
||||
},
|
||||
[depth1Key]: {
|
||||
sessionId: depth1SessionId,
|
||||
updatedAt: Date.now(),
|
||||
},
|
||||
[depth2Key]: {
|
||||
sessionId: depth2SessionId,
|
||||
updatedAt: Date.now(),
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
const { cfg } = await createAbortConfig({
|
||||
sessionIdsByKey: {
|
||||
[sessionKey]: sessionId,
|
||||
[depth1Key]: depth1SessionId,
|
||||
[depth2Key]: depth2SessionId,
|
||||
},
|
||||
});
|
||||
|
||||
// First call: main session lists depth-1 children
|
||||
// Second call (cascade): depth-1 session lists depth-2 children
|
||||
@@ -339,34 +326,18 @@ describe("abort detection", () => {
|
||||
it("cascade stop traverses ended depth-1 parents to stop active depth-2 children", async () => {
|
||||
subagentRegistryMocks.listSubagentRunsForRequester.mockClear();
|
||||
subagentRegistryMocks.markSubagentRunTerminated.mockClear();
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-abort-"));
|
||||
const storePath = path.join(root, "sessions.json");
|
||||
const cfg = { session: { store: storePath } } as OpenClawConfig;
|
||||
const sessionKey = "telegram:parent";
|
||||
const depth1Key = "agent:main:subagent:child-ended";
|
||||
const depth2Key = "agent:main:subagent:child-ended:subagent:grandchild-active";
|
||||
const now = Date.now();
|
||||
await fs.writeFile(
|
||||
storePath,
|
||||
JSON.stringify(
|
||||
{
|
||||
[sessionKey]: {
|
||||
sessionId: "session-parent",
|
||||
updatedAt: now,
|
||||
},
|
||||
[depth1Key]: {
|
||||
sessionId: "session-child-ended",
|
||||
updatedAt: now,
|
||||
},
|
||||
[depth2Key]: {
|
||||
sessionId: "session-grandchild-active",
|
||||
updatedAt: now,
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
const { cfg } = await createAbortConfig({
|
||||
nowMs: now,
|
||||
sessionIdsByKey: {
|
||||
[sessionKey]: "session-parent",
|
||||
[depth1Key]: "session-child-ended",
|
||||
[depth2Key]: "session-grandchild-active",
|
||||
},
|
||||
});
|
||||
|
||||
// main -> ended depth-1 parent
|
||||
// depth-1 parent -> active depth-2 child
|
||||
|
||||
Reference in New Issue
Block a user