mega-plant/lib/auth-service.test.ts

88 lines
4.6 KiB
TypeScript
Raw Normal View History

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));
});