Skip to content
File

Blob: tests/client/stores/workspace-lifecycle.test.ts

typescript129 lines
1import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2import { createFreshDb, deleteDb } from "@tests/client/util/idb";
3import { createMember, createMembershipSummary, createPage, createWorkspace } from "@tests/client/util/fixtures";
4import type { BlandDatabase } from "@/client/stores/db/bland-db";
5 
6let db: BlandDatabase;
7let replicaCommands: typeof import("@/client/stores/db/workspace-replica").replicaCommands;
8let directoryCommands: typeof import("@/client/stores/db/workspace-directory").directoryCommands;
9let navigationCommands: typeof import("@/client/stores/db/workspace-navigation").navigationCommands;
10let workspaceLifecycleCommands: typeof import("@/client/stores/db/workspace-lifecycle").workspaceLifecycleCommands;
11 
12beforeEach(async () => {
13 vi.resetModules();
14 db = createFreshDb();
15 vi.doMock("@/client/stores/db/bland-db", async () => {
16 const actual = await vi.importActual<typeof import("@/client/stores/db/bland-db")>("@/client/stores/db/bland-db");
17 return { ...actual, db };
18 });
19 replicaCommands = (await import("@/client/stores/db/workspace-replica")).replicaCommands;
20 directoryCommands = (await import("@/client/stores/db/workspace-directory")).directoryCommands;
21 navigationCommands = (await import("@/client/stores/db/workspace-navigation")).navigationCommands;
22 workspaceLifecycleCommands = (await import("@/client/stores/db/workspace-lifecycle")).workspaceLifecycleCommands;
23});
24 
25afterEach(async () => {
26 await deleteDb(db);
27 vi.restoreAllMocks();
28});
29 
30describe("workspaceLifecycleCommands.removeWorkspace", () => {
31 async function seedTwoWorkspaces() {
32 const target = createMembershipSummary({ id: "ws-1", slug: "one" });
33 const other = createMembershipSummary({ id: "ws-2", slug: "two" });
34 await directoryCommands.replaceAll([target, other]);
35 
36 await replicaCommands.replaceWorkspace({
37 workspace: createWorkspace({ id: "ws-1" }),
38 accessMode: "member",
39 workspaceRole: "member",
40 pages: [createPage({ id: "p1", workspace_id: "ws-1" }), createPage({ id: "p2", workspace_id: "ws-1" })],
41 members: [createMember({ workspace_id: "ws-1" })],
42 });
43 await replicaCommands.replaceWorkspace({
44 workspace: createWorkspace({ id: "ws-2" }),
45 accessMode: "member",
46 workspaceRole: "member",
47 pages: [createPage({ id: "p3", workspace_id: "ws-2" })],
48 members: [createMember({ workspace_id: "ws-2", user_id: "u-other" })],
49 });
50 await replicaCommands.upsertPageAccess("p1", "edit");
51 await replicaCommands.upsertPageAccess("p2", "view");
52 await replicaCommands.upsertPageAccess("p3", "edit");
53 
54 await navigationCommands.setLastVisitedWorkspaceId("ws-1");
55 await navigationCommands.setLastVisitedPage("ws-1", "p1");
56 await navigationCommands.setLastVisitedPage("ws-2", "p3");
57 }
58 
59 it("cascades across directory, replica, pages, members, pageAccess, and lastVisitedPages", async () => {
60 await seedTwoWorkspaces();
61 
62 await workspaceLifecycleCommands.removeWorkspace("ws-1");
63 
64 expect(await db.memberWorkspaces.get("ws-1")).toBeUndefined();
65 expect(await db.workspaceReplicas.get("ws-1")).toBeUndefined();
66 expect(await db.workspacePages.where("workspace_id").equals("ws-1").count()).toBe(0);
67 expect(await db.workspaceMembers.where("workspace_id").equals("ws-1").count()).toBe(0);
68 expect(await db.pageAccess.get("p1")).toBeUndefined();
69 expect(await db.pageAccess.get("p2")).toBeUndefined();
70 expect(await db.lastVisitedPages.get("ws-1")).toBeUndefined();
71 });
72 
73 it("clears lastVisitedWorkspaceId when it matches the removed workspace", async () => {
74 await seedTwoWorkspaces();
75 await workspaceLifecycleCommands.removeWorkspace("ws-1");
76 const lv = await db.workspaceMeta.get("lastVisitedWorkspaceId");
77 expect(lv?.value).toBeNull();
78 });
79 
80 it("preserves lastVisitedWorkspaceId when another workspace was the target", async () => {
81 await seedTwoWorkspaces();
82 await workspaceLifecycleCommands.removeWorkspace("ws-2");
83 const lv = await db.workspaceMeta.get("lastVisitedWorkspaceId");
84 expect(lv?.value).toBe("ws-1");
85 });
86 
87 it("leaves other workspaces' pages, members, and pageAccess intact", async () => {
88 await seedTwoWorkspaces();
89 await workspaceLifecycleCommands.removeWorkspace("ws-1");
90 expect(await db.workspaceReplicas.get("ws-2")).toBeDefined();
91 expect(await db.workspacePages.get("p3")).toBeDefined();
92 expect((await db.workspaceMembers.where("workspace_id").equals("ws-2").toArray()).length).toBe(1);
93 expect((await db.pageAccess.get("p3"))?.mode).toBe("edit");
94 expect(await db.lastVisitedPages.get("ws-2")).toBeDefined();
95 });
96});
97 
98describe("workspaceLifecycleCommands.clearAllLocal", () => {
99 it("wipes every workspace-scoped table and resets lastVisitedWorkspaceId but keeps the meta row structure", async () => {
100 await directoryCommands.replaceAll([createMembershipSummary({ id: "ws-1" })]);
101 await replicaCommands.replaceWorkspace({
102 workspace: createWorkspace({ id: "ws-1" }),
103 accessMode: "member",
104 workspaceRole: "member",
105 pages: [createPage({ id: "p1", workspace_id: "ws-1" })],
106 members: [createMember({ workspace_id: "ws-1" })],
107 });
108 await replicaCommands.upsertPageAccess("p1", "edit");
109 await navigationCommands.setLastVisitedWorkspaceId("ws-1");
110 await navigationCommands.setLastVisitedPage("ws-1", "p1");
111 await db.workspaceMeta.put({ key: "owner", value: "user-1" });
112 
113 await workspaceLifecycleCommands.clearAllLocal();
114 
115 expect(await db.memberWorkspaces.count()).toBe(0);
116 expect(await db.workspaceReplicas.count()).toBe(0);
117 expect(await db.workspacePages.count()).toBe(0);
118 expect(await db.workspaceMembers.count()).toBe(0);
119 expect(await db.pageAccess.count()).toBe(0);
120 expect(await db.sharedInboxItems.count()).toBe(0);
121 expect(await db.lastVisitedPages.count()).toBe(0);
122 // owner row is NOT touched by clearAllLocal; the caller
123 // (ensureWorkspaceLocalOwner / resetWorkspaceLocalOwner) writes the new
124 // owner value separately.
125 expect((await db.workspaceMeta.get("owner"))?.value).toBe("user-1");
126 expect((await db.workspaceMeta.get("lastVisitedWorkspaceId"))?.value).toBeNull();
127 });
128});