From 7c39f8f3a8c6d761d801587f5955d99221a4d977 Mon Sep 17 00:00:00 2001 From: drifterza Date: Tue, 24 Feb 2026 17:39:42 +0200 Subject: [PATCH] add tests for role assignment upsert behavior --- tests/unit/sessions/sessions.test.ts | 180 +++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 tests/unit/sessions/sessions.test.ts diff --git a/tests/unit/sessions/sessions.test.ts b/tests/unit/sessions/sessions.test.ts new file mode 100644 index 0000000..90700db --- /dev/null +++ b/tests/unit/sessions/sessions.test.ts @@ -0,0 +1,180 @@ +import { createClient } from "@libsql/client"; +import { eq } from "drizzle-orm"; +import { drizzle } from "drizzle-orm/libsql"; +import { ulid } from "ulidx"; +import { beforeEach, describe, expect, test } from "vitest"; + +import { users } from "~/server/db/schema"; +import { Roles } from "~/server/web/roles"; + +// Create in-memory database for testing +function createTestDb() { + const client = createClient({ url: ":memory:" }); + const db = drizzle(client); + return { client, db }; +} + +// Create the users table schema +async function setupSchema(client: ReturnType) { + await client.execute(` + CREATE TABLE IF NOT EXISTS users ( + id TEXT PRIMARY KEY, + sub TEXT NOT NULL UNIQUE, + caps INTEGER NOT NULL DEFAULT 0, + onboarded INTEGER NOT NULL DEFAULT 0 + ) + `); +} + +describe("Session role assignment", () => { + let db: ReturnType; + let client: ReturnType; + + beforeEach(async () => { + const testDb = createTestDb(); + db = testDb.db; + client = testDb.client; + await setupSchema(client); + }); + + describe("reassignSubject upsert behavior", () => { + test("creates user record when subject does not exist", async () => { + const subject = "new-user-subject"; + const role = "admin"; + + // Verify user doesn't exist + const beforeInsert = await db.select().from(users).where(eq(users.sub, subject)); + expect(beforeInsert.length).toBe(0); + + // Perform upsert (simulating reassignSubject) + await db + .insert(users) + .values({ + id: ulid(), + sub: subject, + caps: Roles[role], + onboarded: false, + }) + .onConflictDoUpdate({ + target: users.sub, + set: { caps: Roles[role] }, + }); + + // Verify user was created with correct role + const afterInsert = await db.select().from(users).where(eq(users.sub, subject)); + expect(afterInsert.length).toBe(1); + expect(afterInsert[0].caps).toBe(Roles.admin); + }); + + test("updates existing user role without creating duplicate", async () => { + const subject = "existing-user"; + const initialRole = "member"; + const newRole = "admin"; + + // Create initial user + await db.insert(users).values({ + id: ulid(), + sub: subject, + caps: Roles[initialRole], + onboarded: true, + }); + + // Verify initial state + const beforeUpdate = await db.select().from(users).where(eq(users.sub, subject)); + expect(beforeUpdate.length).toBe(1); + expect(beforeUpdate[0].caps).toBe(Roles.member); + expect(beforeUpdate[0].onboarded).toBe(true); + + // Perform upsert (simulating reassignSubject) + await db + .insert(users) + .values({ + id: ulid(), + sub: subject, + caps: Roles[newRole], + onboarded: false, + }) + .onConflictDoUpdate({ + target: users.sub, + set: { caps: Roles[newRole] }, + }); + + // Verify role was updated, no duplicate created + const afterUpdate = await db.select().from(users).where(eq(users.sub, subject)); + expect(afterUpdate.length).toBe(1); + expect(afterUpdate[0].caps).toBe(Roles.admin); + // onboarded should remain true (not overwritten) + expect(afterUpdate[0].onboarded).toBe(true); + }); + + test("can assign all role types", async () => { + const roles = ["admin", "network_admin", "it_admin", "auditor", "member"] as const; + + for (const role of roles) { + const subject = `user-${role}`; + + await db + .insert(users) + .values({ + id: ulid(), + sub: subject, + caps: Roles[role], + onboarded: false, + }) + .onConflictDoUpdate({ + target: users.sub, + set: { caps: Roles[role] }, + }); + + const [user] = await db.select().from(users).where(eq(users.sub, subject)); + expect(user.caps).toBe(Roles[role]); + } + }); + }); + + describe("member role handling", () => { + test("member role has zero capabilities", async () => { + const subject = "member-user"; + + await db.insert(users).values({ + id: ulid(), + sub: subject, + caps: Roles.member, + onboarded: false, + }); + + const [user] = await db.select().from(users).where(eq(users.sub, subject)); + expect(user.caps).toBe(0); + }); + + test("upgrading from member to admin grants ui_access", async () => { + const subject = "upgrading-user"; + + // Start as member + await db.insert(users).values({ + id: ulid(), + sub: subject, + caps: Roles.member, + onboarded: false, + }); + + // Upgrade to admin + await db + .insert(users) + .values({ + id: ulid(), + sub: subject, + caps: Roles.admin, + onboarded: false, + }) + .onConflictDoUpdate({ + target: users.sub, + set: { caps: Roles.admin }, + }); + + const [user] = await db.select().from(users).where(eq(users.sub, subject)); + expect(user.caps).toBe(Roles.admin); + expect(user.caps).not.toBe(0); + }); + }); +});