Files
AgentCompany/tests/unit/conversation-pinning.test.ts
T
ZacharyZhang-NY 3c44ff959d Messages pins the founder's CEO conversation at the top
buildConversations learns the org's CEO (c_suite, the runtime predicate)
and ranks the viewer's own direct conversation with them above recency —
counterpart resolution already works from the viewer's seat, so agent-to-
agent CEO chats never match. The row carries a CEO badge and, before the
first exchange, invites asking about progress, goals, or direction.
Being first, it is also the default conversation the page opens on.
2026-07-14 00:16:38 -04:00

106 lines
4.4 KiB
TypeScript

import { describe, expect, test } from "vitest";
import { buildConversations } from "../../src/renderer/src/pages/chat/chat-helpers";
import type {
AgentRecord,
ChatMembershipRecord,
ChatMessageRecord,
ChatSpaceRecord,
EmployeeRecord,
} from "../../src/shared/domain";
const ORG = "org_1";
const FOUNDER = "emp_founder";
const CEO = "agt_ceo";
const ENGINEER = "agt_engineer";
function space(id: string, spaceType: string, name: string, createdAt: string): ChatSpaceRecord {
return { id, orgId: ORG, spaceType, name, topic: "t", visibility: "restricted", retentionPolicy: "standard", createdAt };
}
function member(spaceId: string, principalType: "employee" | "agent", principalId: string): ChatMembershipRecord {
return { spaceId, principalType, principalId, role: "member", muteState: "unmuted" };
}
function message(id: string, spaceId: string, senderId: string, createdAt: string): ChatMessageRecord {
return {
id, orgId: ORG, spaceId, threadId: `thr_${id}`, senderType: "agent", senderId,
bodyMd: "hello", bodyStructuredJson: "{}", deliveryState: "sent", createdAt, editedAt: null, deletedAt: null,
};
}
function agent(id: string, name: string, roleLevel: string): AgentRecord {
return {
id, orgId: ORG, name, role: name, department: "Executive", modelProviderId: null, modelId: null,
budgetCents: 0, managerAgentId: roleLevel === "c_suite" ? null : CEO, title: name, roleLevel,
modelStrategy: "explicit", workspaceId: null, heartbeatEnabled: false, heartbeatIntervalSec: 0,
lifecycle: "active", avatarEmoji: null, avatarColor: null, avatarImage: null, createdAt: "2026-01-01T00:00:00Z",
};
}
const employees: EmployeeRecord[] = [{
id: FOUNDER, orgId: ORG, displayName: "Yang", role: "founder",
avatarEmoji: null, avatarColor: null, avatarImage: null, createdAt: "2026-01-01T00:00:00Z",
}];
const agents: AgentRecord[] = [agent(CEO, "CEO", "c_suite"), agent(ENGINEER, "Dana", "staff")];
// The founder⇄CEO conversation is the OLDEST and QUIETEST space; the busy
// channel and the CEO⇄engineer conversation both out-recency it. Pinning
// must beat recency, and only from the founder's own seat.
function fixtures() {
const spaces = [
space("spc_ceo_dm", "direct", "Yang · CEO", "2026-01-01T00:00:00Z"),
space("spc_general", "company", "general", "2026-01-02T00:00:00Z"),
space("spc_agents_dm", "direct", "CEO · Dana", "2026-01-03T00:00:00Z"),
];
const memberships = [
member("spc_ceo_dm", "employee", FOUNDER),
member("spc_ceo_dm", "agent", CEO),
member("spc_general", "employee", FOUNDER),
member("spc_general", "agent", CEO),
member("spc_general", "agent", ENGINEER),
member("spc_agents_dm", "agent", CEO),
member("spc_agents_dm", "agent", ENGINEER),
];
const messages = [
message("m1", "spc_ceo_dm", CEO, "2026-01-04T00:00:00Z"),
message("m2", "spc_general", ENGINEER, "2026-01-05T00:00:00Z"),
message("m3", "spc_agents_dm", CEO, "2026-01-06T00:00:00Z"),
];
return { spaces, memberships, messages };
}
function build(overrides: Partial<Parameters<typeof buildConversations>[0]> = {}) {
const { spaces, memberships, messages } = fixtures();
return buildConversations({
spaces, memberships, messages, readStates: [], employees, agents,
currentEmployeeId: FOUNDER, ceoAgentId: CEO, ...overrides,
});
}
describe("conversation pinning", () => {
test("the founder's CEO conversation outranks recency and sits first", () => {
const conversations = build();
expect(conversations.map((item) => item.space.id)).toEqual(["spc_ceo_dm", "spc_agents_dm", "spc_general"]);
expect(conversations[0].isCeo).toBe(true);
expect(conversations[0].title).toBe("CEO");
});
test("a CEO⇄agent conversation is not the founder's pinned line", () => {
const conversations = build();
const agentsDm = conversations.find((item) => item.space.id === "spc_agents_dm");
expect(agentsDm?.isCeo).toBe(false);
});
test("without a CEO the list is pure recency", () => {
const conversations = build({ ceoAgentId: null });
expect(conversations.every((item) => !item.isCeo)).toBe(true);
expect(conversations.map((item) => item.space.id)).toEqual(["spc_agents_dm", "spc_general", "spc_ceo_dm"]);
});
test("the pinned row still counts unread like any conversation", () => {
const conversations = build();
expect(conversations[0].unread).toBe(1);
expect(conversations[0].preview).toContain("CEO:");
});
});