import { describe, expect, it } from "vitest"; import { canAccessRecord, canVisitorAccess, DEFAULT_RECORD_ACCESS, parseActorKey, actorKey } from "./record-access"; import type { ColineActorRef } from "./types"; const alice: ColineActorRef = { kind: "user", userId: "u_alice" }; const bob: ColineActorRef = { kind: "user", userId: "u_bob" }; const app: ColineActorRef = { kind: "app" }; const guest: ColineActorRef = { kind: "visitor", visitorId: "vis_a", signedIn: false, name: null, email: null }; const otherGuest: ColineActorRef = { kind: "visitor", visitorId: "vis_b", signedIn: false, name: null, email: null }; const privateAccess = { workspace: "none" as const, readers: [], writers: [] }; describe("canAccessRecord", () => { it("lets the exact creator manage its record", () => { expect(canAccessRecord(privateAccess, alice, alice, "manage")).toBe(true); expect(canAccessRecord(privateAccess, app, app, "manage")).toBe(true); }); it("does not treat other principals as the owner", () => { expect(canAccessRecord(privateAccess, alice, bob, "read")).toBe(false); expect(canAccessRecord(privateAccess, alice, app, "read")).toBe(false); expect(canAccessRecord(privateAccess, guest, app, "read")).toBe(false); }); it("never admits a visitor through the record ACL, not even the creator", () => { expect(canAccessRecord(DEFAULT_RECORD_ACCESS, guest, guest, "read")).toBe(false); expect(canAccessRecord(DEFAULT_RECORD_ACCESS, app, guest, "read")).toBe(false); expect(canAccessRecord(DEFAULT_RECORD_ACCESS, alice, guest, "write")).toBe(false); }); it("keeps the workspace default readable and writable by members and app logic", () => { expect(canAccessRecord(DEFAULT_RECORD_ACCESS, guest, alice, "write")).toBe(true); expect(canAccessRecord(DEFAULT_RECORD_ACCESS, guest, app, "write")).toBe(true); expect(canAccessRecord(DEFAULT_RECORD_ACCESS, guest, app, "manage")).toBe(false); }); it("matches readers and writers by user id only", () => { const shared = { workspace: "none" as const, readers: ["u_bob"], writers: [] }; expect(canAccessRecord(shared, alice, bob, "read")).toBe(true); expect(canAccessRecord(shared, alice, bob, "write")).toBe(false); expect(canAccessRecord(shared, alice, app, "read")).toBe(false); }); }); describe("canVisitorAccess", () => { const signedIn = { visitorId: "vis_a", signedIn: true }; const anonymous = { visitorId: "vis_a", signedIn: false }; it("maps each subject to the visitors it admits", () => { expect(canVisitorAccess("public", anonymous, null)).toBe(true); expect(canVisitorAccess("visitor", anonymous, null)).toBe(true); expect(canVisitorAccess("signedIn", anonymous, null)).toBe(false); expect(canVisitorAccess("signedIn", signedIn, null)).toBe(true); expect(canVisitorAccess("team", signedIn, null)).toBe(false); }); it("admits only the creating visitor for owner", () => { expect(canVisitorAccess("owner", anonymous, guest)).toBe(true); expect(canVisitorAccess("owner", anonymous, otherGuest)).toBe(false); expect(canVisitorAccess("owner", anonymous, app)).toBe(false); expect(canVisitorAccess("owner", anonymous, null)).toBe(false); }); }); describe("actor keys", () => { it("round-trips every principal kind", () => { expect(actorKey(alice)).toBe("user:u_alice"); expect(actorKey(guest)).toBe("visitor:vis_a"); expect(actorKey(app)).toBe("app"); expect(parseActorKey("visitor:vis_a")).toEqual(guest); expect(parseActorKey("user:u_alice")).toEqual(alice); expect(parseActorKey("app")).toEqual(app); }); });