feat(FN-3484): normalize dashboard mailbox and user identity for messaging
This merge normalizes dashboard user identity and mailbox messaging (FN-3484, 4 steps), adds workflow step execution for plugins (FN-3490), and updates the restart integration store mock for plugin templates (FN-3096). Core changes touch the message store and store modules with identity normalizatio Fusion-Task-Id: FN-3484
This commit is contained in:
@@ -4,6 +4,7 @@ import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { Database } from "../db.js";
|
||||
import { MessageStore } from "../message-store.js";
|
||||
import { DASHBOARD_USER_ID } from "../types.js";
|
||||
import type { Message, Mailbox } from "../types.js";
|
||||
|
||||
describe("MessageStore", () => {
|
||||
@@ -123,6 +124,40 @@ describe("MessageStore", () => {
|
||||
const result = store.getMessage("msg-nonexistent");
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it.each(["dashboard", "user:dashboard", "User: user:dashboard"])(
|
||||
"canonicalizes dashboard user alias '%s' when writing recipient",
|
||||
(dashboardAlias) => {
|
||||
const message = store.sendMessage({
|
||||
fromId: "agent-1",
|
||||
fromType: "agent",
|
||||
toId: dashboardAlias,
|
||||
toType: "user",
|
||||
content: "Hello dashboard",
|
||||
type: "agent-to-user",
|
||||
});
|
||||
|
||||
expect(message.toId).toBe(DASHBOARD_USER_ID);
|
||||
expect(store.getMessage(message.id)?.toId).toBe(DASHBOARD_USER_ID);
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["dashboard", "user:dashboard", "User: user:dashboard"])(
|
||||
"canonicalizes dashboard user alias '%s' when writing sender",
|
||||
(dashboardAlias) => {
|
||||
const message = store.sendMessage({
|
||||
fromId: dashboardAlias,
|
||||
fromType: "user",
|
||||
toId: "agent-1",
|
||||
toType: "agent",
|
||||
content: "Reply",
|
||||
type: "user-to-agent",
|
||||
});
|
||||
|
||||
expect(message.fromId).toBe(DASHBOARD_USER_ID);
|
||||
expect(store.getMessage(message.id)?.fromId).toBe(DASHBOARD_USER_ID);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("message-to-agent hook", () => {
|
||||
@@ -234,6 +269,15 @@ describe("MessageStore", () => {
|
||||
expect(inbox).toEqual([]);
|
||||
});
|
||||
|
||||
it("includes legacy dashboard aliases in canonical dashboard inbox reads", () => {
|
||||
store.sendMessage({ fromId: "agent-1", fromType: "agent", toId: DASHBOARD_USER_ID, toType: "user", content: "A", type: "agent-to-user" });
|
||||
store.sendMessage({ fromId: "agent-1", fromType: "agent", toId: "user:dashboard", toType: "user", content: "B", type: "agent-to-user" });
|
||||
store.sendMessage({ fromId: "agent-1", fromType: "agent", toId: "User: user:dashboard", toType: "user", content: "C", type: "agent-to-user" });
|
||||
|
||||
const inbox = store.getInbox(DASHBOARD_USER_ID, "user");
|
||||
expect(inbox).toHaveLength(3);
|
||||
});
|
||||
|
||||
it("filters by read status", () => {
|
||||
const msg1 = store.sendMessage({
|
||||
fromId: "agent-1",
|
||||
@@ -418,6 +462,14 @@ describe("MessageStore", () => {
|
||||
const count = store.markAllAsRead("user-99", "user");
|
||||
expect(count).toBe(0);
|
||||
});
|
||||
|
||||
it("marks canonical dashboard aliases as read together", () => {
|
||||
store.sendMessage({ fromId: "agent-1", fromType: "agent", toId: DASHBOARD_USER_ID, toType: "user", content: "A", type: "agent-to-user" });
|
||||
store.sendMessage({ fromId: "agent-2", fromType: "agent", toId: "user:dashboard", toType: "user", content: "B", type: "agent-to-user" });
|
||||
const marked = store.markAllAsRead(DASHBOARD_USER_ID, "user");
|
||||
expect(marked).toBe(2);
|
||||
expect(store.getMailbox(DASHBOARD_USER_ID, "user").unreadCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("deleteMessage()", () => {
|
||||
@@ -524,6 +576,31 @@ describe("MessageStore", () => {
|
||||
);
|
||||
expect(conversation).toEqual([]);
|
||||
});
|
||||
|
||||
it("treats canonical dashboard identity as equivalent to legacy aliases in conversation reads", () => {
|
||||
const sent = store.sendMessage({
|
||||
fromId: "dashboard",
|
||||
fromType: "user",
|
||||
toId: "agent-1",
|
||||
toType: "agent",
|
||||
content: "Question",
|
||||
type: "user-to-agent",
|
||||
});
|
||||
const reply = store.sendMessage({
|
||||
fromId: "agent-1",
|
||||
fromType: "agent",
|
||||
toId: "user:dashboard",
|
||||
toType: "user",
|
||||
content: "Answer",
|
||||
type: "agent-to-user",
|
||||
});
|
||||
|
||||
const conversation = store.getConversation(
|
||||
{ id: DASHBOARD_USER_ID, type: "user" },
|
||||
{ id: "agent-1", type: "agent" },
|
||||
);
|
||||
expect(conversation.map((message) => message.id)).toEqual([sent.id, reply.id]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getMailbox()", () => {
|
||||
@@ -561,6 +638,15 @@ describe("MessageStore", () => {
|
||||
expect(mailbox.lastMessage).toBeUndefined();
|
||||
});
|
||||
|
||||
it("aggregates unread count across canonical and legacy dashboard aliases", () => {
|
||||
store.sendMessage({ fromId: "agent-1", fromType: "agent", toId: DASHBOARD_USER_ID, toType: "user", content: "A", type: "agent-to-user" });
|
||||
store.sendMessage({ fromId: "agent-1", fromType: "agent", toId: "User: user:dashboard", toType: "user", content: "B", type: "agent-to-user" });
|
||||
|
||||
const mailbox = store.getMailbox(DASHBOARD_USER_ID, "user");
|
||||
expect(mailbox.unreadCount).toBe(2);
|
||||
expect(mailbox.lastMessage).toBeTruthy();
|
||||
});
|
||||
|
||||
it("counts only unread messages", () => {
|
||||
const msg1 = store.sendMessage({
|
||||
fromId: "agent-1",
|
||||
|
||||
Reference in New Issue
Block a user