Skip to content
File

Blob: tests/worker/routes/shared-pages.workers.test.ts

typescript121 lines
1import { beforeEach, describe, expect, it } from "vitest";
2 
3import { resetD1Tables } from "@tests/worker/helpers/db";
4import { apiRequest } from "@tests/worker/helpers/request";
5import { seedMembership, seedPage, seedPageShare, seedUser, seedWorkspace } from "@tests/worker/helpers/seeds";
6 
7type SharedPagesResponse = {
8 items: Array<{
9 page_id: string;
10 title: string;
11 permission: "view" | "edit";
12 workspace: { id: string; slug: string; role: string | null };
13 shared_by: string;
14 shared_by_name: string;
15 }>;
16 workspace_summaries: Array<{
17 workspace: { id: string; slug: string; name: string };
18 count: number;
19 }>;
20};
21 
22describe("GET /me/shared-pages", () => {
23 beforeEach(async () => {
24 await resetD1Tables();
25 });
26 
27 it("partitions user-grantee shares into cross-workspace items and same-workspace summaries", async () => {
28 const caller = await seedUser();
29 const sharerOther = await seedUser({ name: "Alice" });
30 const sharerHome = await seedUser({ name: "Bob" });
31 
32 const otherWs = await seedWorkspace({ owner_id: sharerOther.id, slug: "other", name: "Other" });
33 const homeWs = await seedWorkspace({ owner_id: sharerHome.id, slug: "home", name: "Home" });
34 await seedMembership({ user_id: caller.id, workspace_id: homeWs.id, role: "member" });
35 
36 const crossPage = await seedPage({
37 workspace_id: otherWs.id,
38 created_by: sharerOther.id,
39 title: "Cross-workspace page",
40 });
41 await seedPageShare({
42 page_id: crossPage.id,
43 created_by: sharerOther.id,
44 grantee_type: "user",
45 grantee_id: caller.id,
46 permission: "view",
47 });
48 
49 const sameWsPageA = await seedPage({ workspace_id: homeWs.id, created_by: sharerHome.id, title: "A" });
50 const sameWsPageB = await seedPage({ workspace_id: homeWs.id, created_by: sharerHome.id, title: "B" });
51 const sameWsPageC = await seedPage({ workspace_id: homeWs.id, created_by: sharerHome.id, title: "C" });
52 for (const page of [sameWsPageA, sameWsPageB, sameWsPageC]) {
53 await seedPageShare({
54 page_id: page.id,
55 created_by: sharerHome.id,
56 grantee_type: "user",
57 grantee_id: caller.id,
58 permission: "edit",
59 });
60 }
61 
62 const res = await apiRequest("/api/v1/me/shared-pages", { userId: caller.id });
63 expect(res.status).toBe(200);
64 
65 const body = (await res.json()) as SharedPagesResponse;
66 
67 expect(body.items).toHaveLength(1);
68 expect(body.items[0]).toMatchObject({
69 page_id: crossPage.id,
70 title: "Cross-workspace page",
71 workspace: { id: otherWs.id, slug: "other", role: null },
72 permission: "view",
73 shared_by: sharerOther.id,
74 shared_by_name: "Alice",
75 });
76 
77 expect(body.workspace_summaries).toHaveLength(1);
78 expect(body.workspace_summaries[0]).toMatchObject({
79 workspace: { id: homeWs.id, slug: "home", name: "Home" },
80 count: 3,
81 });
82 });
83 
84 it("ignores link-grantee shares and archived pages", async () => {
85 const caller = await seedUser();
86 const sharer = await seedUser();
87 const ws = await seedWorkspace({ owner_id: sharer.id });
88 await seedMembership({ user_id: caller.id, workspace_id: ws.id, role: "member" });
89 
90 const archivedPage = await seedPage({
91 workspace_id: ws.id,
92 created_by: sharer.id,
93 archived_at: "2026-04-01T00:00:00.000Z",
94 });
95 await seedPageShare({
96 page_id: archivedPage.id,
97 created_by: sharer.id,
98 grantee_type: "user",
99 grantee_id: caller.id,
100 permission: "view",
101 });
102 
103 const livePage = await seedPage({ workspace_id: ws.id, created_by: sharer.id });
104 await seedPageShare({
105 page_id: livePage.id,
106 created_by: sharer.id,
107 grantee_type: "link",
108 grantee_id: null,
109 link_token: "link-token-1",
110 permission: "view",
111 });
112 
113 const res = await apiRequest("/api/v1/me/shared-pages", { userId: caller.id });
114 expect(res.status).toBe(200);
115 
116 const body = (await res.json()) as SharedPagesResponse;
117 expect(body.items).toEqual([]);
118 expect(body.workspace_summaries).toEqual([]);
119 });
120});