Files
PaperClipAI/ui/src/lib/issue-hierarchy-context.test.ts
T
DottaandPaperclip be380ba155 fix(ui): hide agent-created tasks by default (#16157)
Hide agent-created, system-created, and chat-channel tasks from independent collections by default. Preserve task hierarchy context and existing inbox participation. Add a saved opt-in checkbox without helper text.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 09:26:37 -05:00

51 lines
2.7 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { Issue } from "@paperclipai/shared";
import { includeIssueHierarchyContext, loadIssueHierarchyChildren } from "./issue-hierarchy-context";
const task = (id: string, parentId: string | null = null) => ({ id, parentId, companyId: "company" } as Issue);
describe("task hierarchy context", () => {
it("retains ancestors and descendants without promoting siblings or orphans", () => {
const match = task("human", "agent-parent");
const context = [task("agent-parent", "grandparent"), task("grandparent"), task("agent-child", "human"), task("grandchild", "agent-child"), task("unrelated"), task("orphan", "missing"), task("sibling", "agent-parent")];
expect(includeIssueHierarchyContext([match], context).map((issue) => issue.id)).toEqual([
"human", "agent-parent", "grandparent", "agent-child", "grandchild",
]);
expect(includeIssueHierarchyContext([], context)).toEqual([]);
});
it("deduplicates matches and terminates cycles", () => {
const match = task("human", "parent");
expect(includeIssueHierarchyContext([match], [match, task("parent", "human")]).map((issue) => issue.id)).toEqual(["human", "parent"]);
});
});
describe("bounded hierarchy reads", () => {
it("caps rows and requests, and shares overlapping matching subtrees", async () => {
const root = task("root");
const child = task("child", "root");
const calls: string[] = [];
const read = async (id: string, limit: number, offset: number) => {
calls.push(id);
const rows = id === "root" ? [child] : id === "child"
? Array.from({ length: 500 }, (_, index) => task(`nested-${index}`, "child")) : [];
return rows.slice(offset, offset + limit);
};
const first = await loadIssueHierarchyChildren("company", [root, child], [], 5, read);
expect(first.issues).toHaveLength(5);
expect(first.hasMore).toBe(true);
expect(calls).toEqual(["root", "child"]);
calls.length = 0;
expect((await loadIssueHierarchyChildren("company", [root, child], [], 10, read)).issues).toHaveLength(10);
expect(calls).toEqual(["root", "child"]);
});
it("probes collapsed parents without fetching their tree and rejects foreign-company rows", async () => {
const calls: Array<[string, number]> = [];
const rows = await loadIssueHierarchyChildren("company", [task("root")], ["root"], 5,
async (id, limit) => { calls.push([id, limit]); return [task("child", "root")]; });
expect(rows.issues.map((issue) => issue.id)).toEqual(["child"]);
expect(calls).toEqual([["root", 1]]);
expect(await loadIssueHierarchyChildren("company", [task("root")], [], 1,
async () => [{ ...task("foreign", "root"), companyId: "other" }])).toEqual({ issues: [], hasMore: true });
});
});