Skip to content
File

Blob: tests/worker/routes/page-snapshot.workers.test.ts

typescript85 lines
1import { beforeEach, describe, expect, it } from "vitest";
2 
3import { resetD1Tables } from "@tests/worker/helpers/db";
4import { apiRequest } from "@tests/worker/helpers/request";
5import { buildYjsDocBytes, seedDocSyncSnapshot } from "@tests/worker/helpers/do";
6import { seedMembership, seedPage, seedPageShare, seedUser, seedWorkspace } from "@tests/worker/helpers/seeds";
7 
8describe("GET /workspaces/:wid/pages/:id/snapshot", () => {
9 beforeEach(async () => {
10 await resetD1Tables();
11 });
12 
13 it("streams the persisted snapshot bytes for full workspace members", async () => {
14 const owner = await seedUser();
15 const member = await seedUser();
16 const ws = await seedWorkspace({ owner_id: owner.id });
17 await seedMembership({ user_id: member.id, workspace_id: ws.id, role: "member" });
18 const page = await seedPage({ workspace_id: ws.id, created_by: owner.id, title: "Alpha" });
19 const snapshotBytes = buildYjsDocBytes("Alpha", "The quick brown fox.");
20 await seedDocSyncSnapshot(page.id, snapshotBytes);
21 
22 const res = await apiRequest(`/api/v1/workspaces/${ws.id}/pages/${page.id}/snapshot`, {
23 userId: member.id,
24 });
25 
26 expect(res.status).toBe(200);
27 const served = new Uint8Array(await res.arrayBuffer());
28 expect(served.length).toBe(snapshotBytes.length);
29 expect(Array.from(served)).toEqual(Array.from(snapshotBytes));
30 });
31 
32 it("returns 204 when no persisted snapshot exists yet", async () => {
33 const owner = await seedUser();
34 const member = await seedUser();
35 const ws = await seedWorkspace({ owner_id: owner.id });
36 await seedMembership({ user_id: member.id, workspace_id: ws.id, role: "member" });
37 const page = await seedPage({ workspace_id: ws.id, created_by: owner.id });
38 
39 const res = await apiRequest(`/api/v1/workspaces/${ws.id}/pages/${page.id}/snapshot`, {
40 userId: member.id,
41 });
42 
43 expect(res.status).toBe(204);
44 expect(await res.text()).toBe("");
45 });
46 
47 it("serves the snapshot for an anonymous caller presenting a valid share token", async () => {
48 const owner = await seedUser();
49 const ws = await seedWorkspace({ owner_id: owner.id });
50 const page = await seedPage({ workspace_id: ws.id, created_by: owner.id });
51 await seedPageShare({
52 page_id: page.id,
53 created_by: owner.id,
54 grantee_type: "link",
55 grantee_id: null,
56 link_token: "tok-snapshot",
57 permission: "view",
58 });
59 const snapshotBytes = buildYjsDocBytes("Title", "Shared view body.");
60 await seedDocSyncSnapshot(page.id, snapshotBytes);
61 
62 const res = await apiRequest(`/api/v1/workspaces/${ws.id}/pages/${page.id}/snapshot`, {
63 shareToken: "tok-snapshot",
64 });
65 
66 expect(res.status).toBe(200);
67 const served = new Uint8Array(await res.arrayBuffer());
68 expect(Array.from(served)).toEqual(Array.from(snapshotBytes));
69 });
70 
71 it("returns 404 (no existence leak) for a stranger with no access", async () => {
72 const owner = await seedUser();
73 const stranger = await seedUser();
74 const ws = await seedWorkspace({ owner_id: owner.id });
75 const page = await seedPage({ workspace_id: ws.id, created_by: owner.id });
76 await seedDocSyncSnapshot(page.id, buildYjsDocBytes("Title", "body"));
77 
78 const res = await apiRequest(`/api/v1/workspaces/${ws.id}/pages/${page.id}/snapshot`, {
79 userId: stranger.id,
80 });
81 
82 expect(res.status).toBe(404);
83 });
84});