mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-19 01:46:15 +02:00
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>
51 lines
2.7 KiB
TypeScript
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 });
|
|
});
|
|
});
|