import { test, before, after } from "node:test"; import assert from "node:assert/strict"; import PocketBase from "pocketbase"; import { authenticateUser, sessionFromToken, changeUserPassword, addUser, listUsers, setUserActive, deleteUser } from "./auth-service"; const integration = process.env.MEGA_AUTH_INTEGRATION === "1"; const admin = new PocketBase(process.env.NEXT_PUBLIC_PB_URL || "http://127.0.0.1:8090"); const email = `mega-login-qa-${Date.now()}@example.invalid`; let userId = ""; let extraId = ""; before(async () => { if (!integration) return; await admin.collection("_superusers").authWithPassword(process.env.PB_ADMIN!, process.env.PB_PASS!); userId = (await admin.collection("users").create({ name: "Temporary login test", email, password: "mega2026", passwordConfirm: "mega2026" })).id; }); after(async () => { if (!integration) return; for (const id of [extraId, userId].filter(Boolean)) await admin.collection("users").delete(id); }); test("temporary PocketBase user must replace starter password before returning login grants access", { skip: !integration }, async () => { await assert.rejects(authenticateUser(email, "wrong-password")); assert.equal(await sessionFromToken("forged-token"), null); const first = await authenticateUser(email, "mega2026"); assert.equal(first.user.id, userId); assert.equal(first.passwordChanged, false); const oldToken = first.pb.authStore.token; const tampered = oldToken.split("."); tampered[2] = "invalidsignature"; assert.equal(await sessionFromToken(tampered.join(".")), null); const changed = await changeUserPassword(first, "abcdefgh"); assert.equal(changed.passwordChanged, true); assert.equal(await sessionFromToken(oldToken), null); await assert.rejects(authenticateUser(email, "mega2026")); const returning = await authenticateUser(email, "abcdefgh"); assert.equal(returning.passwordChanged, true); assert.equal((await sessionFromToken(returning.pb.authStore.token))?.user.id, userId); }); test("add user assigns starter password and duplicate email cannot replace existing user", { skip: !integration }, async () => { const extraEmail = `new-${email}`; const added = await addUser("New test person", extraEmail); extraId = added.id; assert.equal(added.name, "New test person"); assert.equal((await authenticateUser(extraEmail, "mega2026")).passwordChanged, false); await assert.rejects(addUser("Duplicate", extraEmail)); assert.equal((await admin.collection("users").getOne(extraId)).name, "New test person"); const token = (await authenticateUser(extraEmail, "mega2026")).pb.authStore.token; await admin.collection("users").delete(extraId); extraId = ""; assert.equal(await sessionFromToken(token), null); }); test("inactive users lose app access immediately and reactivation preserves their password", { skip: !integration }, async () => { const target = await addUser("Access test", `access-${email}`); extraId = target.id; const first = await authenticateUser(target.email, "mega2026"); await setUserActive(userId, target.id, false); assert.equal(await sessionFromToken(first.pb.authStore.token), null); await assert.rejects(authenticateUser(target.email, "mega2026"), /inactive/i); await setUserActive(userId, target.id, true); assert.equal((await authenticateUser(target.email, "mega2026")).passwordChanged, false); const signedIn = await changeUserPassword(first, "simplepass"); const token = signedIn.pb.authStore.token; await setUserActive(userId, target.id, false); assert.equal(await sessionFromToken(token), null); await assert.rejects(authenticateUser(target.email, "simplepass"), /inactive/i); const inactive = (await listUsers()).find((user) => user.id === target.id); assert.equal(inactive?.active, false); assert.equal(inactive?.passwordChanged, true); await setUserActive(userId, target.id, true); assert.equal((await authenticateUser(target.email, "simplepass")).passwordChanged, true); assert.equal((await listUsers()).find((user) => user.id === target.id)?.active, true); await deleteUser(userId, target.id); extraId = ""; assert.equal(await sessionFromToken(token), null); assert.equal((await listUsers()).some((user) => user.id === target.id), false); assert.equal((await admin.collection("user_onboarding").getList(1, 1, { filter: admin.filter("user = {:id}", { id: target.id }) })).totalItems, 0); }); test("users cannot deactivate or delete themselves", { skip: !integration }, async () => { await assert.rejects(setUserActive(userId, userId, false), /own account/i); await assert.rejects(deleteUser(userId, userId), /own account/i); assert.ok(await sessionFromToken((await authenticateUser(email, "abcdefgh")).pb.authStore.token)); });