mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 11:55:47 -06:00
fix(qa): preserve structured thread action targets
This commit is contained in:
@@ -74,10 +74,12 @@ describe("qa-channel direct message actions", () => {
|
||||
const threadPayload = extractToolPayload(threadResult) as {
|
||||
thread: { id: string; title: string };
|
||||
target: string;
|
||||
threadId: string;
|
||||
};
|
||||
expect(threadPayload.thread.id).toMatch(/^thread-/);
|
||||
expect(threadPayload.thread.title).toBe("QA thread");
|
||||
expect(threadPayload.target).toContain(threadPayload.thread.id);
|
||||
expect(threadPayload.target).toBe("channel:qa-room");
|
||||
expect(threadPayload.threadId).toBe(threadPayload.thread.id);
|
||||
|
||||
const replyResult = await handleAction({
|
||||
channel: "qa-channel",
|
||||
@@ -86,6 +88,7 @@ describe("qa-channel direct message actions", () => {
|
||||
accountId: "default",
|
||||
params: {
|
||||
target: threadPayload.target,
|
||||
threadId: threadPayload.threadId,
|
||||
message: "thread reply",
|
||||
text: "ignored legacy reply",
|
||||
},
|
||||
@@ -111,6 +114,7 @@ describe("qa-channel direct message actions", () => {
|
||||
accountId: "default",
|
||||
params: {
|
||||
to: threadPayload.target,
|
||||
threadId: threadPayload.threadId,
|
||||
messageId: outbound.id,
|
||||
emoji: "white_check_mark",
|
||||
},
|
||||
@@ -123,6 +127,7 @@ describe("qa-channel direct message actions", () => {
|
||||
accountId: "default",
|
||||
params: {
|
||||
target: threadPayload.target,
|
||||
threadId: threadPayload.threadId,
|
||||
messageId: outbound.id,
|
||||
message: "message (edited)",
|
||||
text: "ignored legacy edit",
|
||||
@@ -136,6 +141,7 @@ describe("qa-channel direct message actions", () => {
|
||||
accountId: "default",
|
||||
params: {
|
||||
to: threadPayload.target,
|
||||
threadId: threadPayload.threadId,
|
||||
messageId: outbound.id,
|
||||
},
|
||||
});
|
||||
@@ -165,6 +171,7 @@ describe("qa-channel direct message actions", () => {
|
||||
accountId: "default",
|
||||
params: {
|
||||
to: threadPayload.target,
|
||||
threadId: threadPayload.threadId,
|
||||
messageId: outbound.id,
|
||||
},
|
||||
});
|
||||
@@ -188,7 +195,7 @@ describe("qa-channel direct message actions", () => {
|
||||
cfg,
|
||||
accountId: "default",
|
||||
params: {
|
||||
target: "channel:canonical-room",
|
||||
target: "channel:canonical/room",
|
||||
to: "channel:legacy-to-room",
|
||||
channelId: "legacy-channel-id-room",
|
||||
threadName: "Canonical target thread",
|
||||
@@ -197,10 +204,12 @@ describe("qa-channel direct message actions", () => {
|
||||
const threadPayload = extractToolPayload(threadResult) as {
|
||||
thread: { id: string; conversationId: string };
|
||||
target: string;
|
||||
threadId: string;
|
||||
};
|
||||
|
||||
expect(threadPayload.thread.conversationId).toBe("canonical-room");
|
||||
expect(threadPayload.target).toBe(`thread:canonical-room/${threadPayload.thread.id}`);
|
||||
expect(threadPayload.thread.conversationId).toBe("canonical/room");
|
||||
expect(threadPayload.target).toBe("channel:canonical/room");
|
||||
expect(threadPayload.threadId).toBe(threadPayload.thread.id);
|
||||
|
||||
const replyResult = await handleAction({
|
||||
channel: "qa-channel",
|
||||
@@ -209,13 +218,14 @@ describe("qa-channel direct message actions", () => {
|
||||
accountId: "default",
|
||||
params: {
|
||||
target: threadPayload.target,
|
||||
threadId: threadPayload.threadId,
|
||||
channelId: "legacy-reply-room",
|
||||
message: "canonical target reply",
|
||||
},
|
||||
});
|
||||
expect(extractToolPayload(replyResult)).toMatchObject({
|
||||
message: {
|
||||
conversation: { id: "canonical-room", kind: "channel" },
|
||||
conversation: { id: "canonical/room", kind: "channel" },
|
||||
text: "canonical target reply",
|
||||
threadId: threadPayload.thread.id,
|
||||
},
|
||||
@@ -246,6 +256,7 @@ describe("qa-channel direct message actions", () => {
|
||||
const threadPayload = extractToolPayload(threadResult) as {
|
||||
thread: { id: string; title: string };
|
||||
target: string;
|
||||
threadId: string;
|
||||
};
|
||||
expect(threadPayload.thread.title).toBe("Legacy thread");
|
||||
|
||||
@@ -279,6 +290,7 @@ describe("qa-channel direct message actions", () => {
|
||||
accountId: "default",
|
||||
params: {
|
||||
to: threadPayload.target,
|
||||
threadId: threadPayload.threadId,
|
||||
messageId: outbound.id,
|
||||
text: "legacy edit",
|
||||
},
|
||||
|
||||
@@ -214,7 +214,11 @@ export const qaChannelMessageActions: ChannelMessageActionAdapter = {
|
||||
});
|
||||
return jsonResult({
|
||||
thread,
|
||||
target: `thread:${target.conversationId}/${thread.id}`,
|
||||
target: buildQaTarget({
|
||||
chatType: target.conversationKind,
|
||||
conversationId: target.conversationId,
|
||||
}),
|
||||
threadId: thread.id,
|
||||
});
|
||||
}
|
||||
case "thread-reply": {
|
||||
|
||||
@@ -7,6 +7,7 @@ export function createQaSelfCheckScenario(options?: {
|
||||
}): QaScenarioDefinition {
|
||||
const waitTimeoutMs = options?.waitTimeoutMs ?? 5_000;
|
||||
let lifecycleTarget: string | undefined;
|
||||
let lifecycleThreadId: string | undefined;
|
||||
return {
|
||||
name: "Synthetic Slack-class roundtrip",
|
||||
steps: [
|
||||
@@ -39,12 +40,13 @@ export function createQaSelfCheckScenario(options?: {
|
||||
});
|
||||
const threadPayload = extractQaToolPayload(
|
||||
threadResult as Parameters<typeof extractQaToolPayload>[0],
|
||||
) as { target?: string; thread?: { id?: string } } | undefined;
|
||||
const threadId = threadPayload?.thread?.id;
|
||||
if (!threadId || !threadPayload?.target) {
|
||||
) as { target?: string; threadId?: string; thread?: { id?: string } } | undefined;
|
||||
const threadId = threadPayload?.threadId;
|
||||
if (!threadId || threadId !== threadPayload?.thread?.id || !threadPayload.target) {
|
||||
throw new Error("thread-create did not return thread id and target");
|
||||
}
|
||||
lifecycleTarget = threadPayload.target;
|
||||
lifecycleThreadId = threadId;
|
||||
|
||||
await state.addInboundMessage({
|
||||
conversation: { id: "qa-room", kind: "channel", title: "QA Room" },
|
||||
@@ -78,12 +80,13 @@ export function createQaSelfCheckScenario(options?: {
|
||||
if (!outboundMessage) {
|
||||
throw new Error("threaded outbound message not found");
|
||||
}
|
||||
if (!lifecycleTarget) {
|
||||
if (!lifecycleTarget || !lifecycleThreadId) {
|
||||
throw new Error("thread target not found");
|
||||
}
|
||||
|
||||
await performAction("react", {
|
||||
to: lifecycleTarget,
|
||||
threadId: lifecycleThreadId,
|
||||
messageId: outboundMessage.id,
|
||||
emoji: "white_check_mark",
|
||||
});
|
||||
@@ -97,6 +100,7 @@ export function createQaSelfCheckScenario(options?: {
|
||||
|
||||
await performAction("edit", {
|
||||
to: lifecycleTarget,
|
||||
threadId: lifecycleThreadId,
|
||||
messageId: outboundMessage.id,
|
||||
text: "qa-echo: inside thread (edited)",
|
||||
});
|
||||
@@ -110,6 +114,7 @@ export function createQaSelfCheckScenario(options?: {
|
||||
|
||||
await performAction("delete", {
|
||||
to: lifecycleTarget,
|
||||
threadId: lifecycleThreadId,
|
||||
messageId: outboundMessage.id,
|
||||
});
|
||||
const deleted = await state.readMessage({ messageId: outboundMessage.id });
|
||||
|
||||
@@ -93,7 +93,8 @@ describe("createQaSelfCheckScenario", () => {
|
||||
if (action === "thread-create") {
|
||||
return {
|
||||
details: {
|
||||
target: "thread:qa-room/thread-1",
|
||||
target: "channel:qa-room",
|
||||
threadId: "thread-1",
|
||||
thread: { id: "thread-1" },
|
||||
},
|
||||
};
|
||||
@@ -120,11 +121,7 @@ describe("createQaSelfCheckScenario", () => {
|
||||
await threadStep.run({ state: testState, performAction });
|
||||
await lifecycleStep.run({ state: testState, performAction });
|
||||
|
||||
expect(targets).toEqual([
|
||||
"thread:qa-room/thread-1",
|
||||
"thread:qa-room/thread-1",
|
||||
"thread:qa-room/thread-1",
|
||||
]);
|
||||
expect(targets).toEqual(["channel:qa-room", "channel:qa-room", "channel:qa-room"]);
|
||||
const deletedMessage = state.getSnapshot().messages.find((message) => message.deleted);
|
||||
if (!deletedMessage) {
|
||||
throw new Error("self-check did not preserve its deleted message tombstone");
|
||||
|
||||
Reference in New Issue
Block a user