| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
import { expect, test } from "bun:test"; |
| 7 |
import { mkdtempSync } from "node:fs"; |
| 8 |
import { tmpdir } from "node:os"; |
| 9 |
import { join } from "node:path"; |
| 10 |
import { |
| 11 |
changePassword, |
| 12 |
deleteAccount, |
| 13 |
isAccount, |
| 14 |
logIn, |
| 15 |
logOut, |
| 16 |
sessionWho, |
| 17 |
signUp, |
| 18 |
} from "./accounts.ts"; |
| 19 |
import { addNote, getNote, listNotes } from "./actions.ts"; |
| 20 |
import { openDb } from "./db.ts"; |
| 21 |
import type { Who } from "./who.ts"; |
| 22 |
|
| 23 |
const db = openDb(mkdtempSync(join(tmpdir(), "app-accounts-"))); |
| 24 |
const must = <T>(r: { ok: true; value: T } | { ok: false; message: string }): T => { |
| 25 |
if (!r.ok) throw new Error(r.message); |
| 26 |
return r.value; |
| 27 |
}; |
| 28 |
|
| 29 |
test("sign up makes a person with a vault of their own", async () => { |
| 30 |
const { account, token } = must(await signUp(db, { email: " [email protected] ", password: "correct horse", name: "Sam" }, "")); |
| 31 |
expect(account.email).toBe("[email protected]"); |
| 32 |
const sam = sessionWho(db, token); |
| 33 |
expect(sam).toEqual({ role: "user", id: `acct:${account.id}`, name: "Sam" }); |
| 34 |
expect(isAccount(sam)).toBe(true); |
| 35 |
const note = must(addNote(db, sam, { title: "Sam's", body: "hidden" })); |
| 36 |
|
| 37 |
const forge: Who = { role: "user", id: String(account.id), name: "forge" }; |
| 38 |
const admin: Who = { role: "admin", id: "u0", name: "root" }; |
| 39 |
expect(must(listNotes(db, forge))).toEqual([]); |
| 40 |
expect(must(listNotes(db, admin))).toEqual([]); |
| 41 |
expect(getNote(db, admin, { id: note.id }).ok).toBe(false); |
| 42 |
|
| 43 |
const stored = JSON.stringify(db.query("SELECT * FROM account").all()) + JSON.stringify(db.query("SELECT * FROM session").all()); |
| 44 |
expect(stored).not.toContain("correct horse"); |
| 45 |
expect(stored).not.toContain(token); |
| 46 |
}); |
| 47 |
|
| 48 |
test("sign up refuses a bad email, a short password, a taken email, or a wrong code", async () => { |
| 49 |
for (const [input, code] of [ |
| 50 |
[{ email: "not-an-email", password: "longenough" }, ""], |
| 51 |
[{ email: "[email protected]", password: "short" }, ""], |
| 52 |
[{ email: "[email protected]", password: "longenough" }, ""], |
| 53 |
[{ email: "[email protected]", password: "longenough", code: "nope" }, "secret"], |
| 54 |
] as const) { |
| 55 |
const r = await signUp(db, input, code); |
| 56 |
expect([input.email, r.ok]).toEqual([input.email, false]); |
| 57 |
} |
| 58 |
expect((await signUp(db, { email: "[email protected]", password: "longenough", code: "secret" }, "secret")).ok).toBe(true); |
| 59 |
}); |
| 60 |
|
| 61 |
test("log in takes the right password only, and log out ends that session", async () => { |
| 62 |
expect((await logIn(db, { email: "[email protected]", password: "wrong horse" })).ok).toBe(false); |
| 63 |
expect((await logIn(db, { email: "[email protected]", password: "correct horse" })).ok).toBe(false); |
| 64 |
const { token } = must(await logIn(db, { email: "[email protected]", password: "correct horse" })); |
| 65 |
expect(sessionWho(db, token).role).toBe("user"); |
| 66 |
logOut(db, token); |
| 67 |
expect(sessionWho(db, token)).toEqual({ role: "anon" }); |
| 68 |
expect(sessionWho(db, "x".repeat(43))).toEqual({ role: "anon" }); |
| 69 |
expect(sessionWho(db, undefined)).toEqual({ role: "anon" }); |
| 70 |
}); |
| 71 |
|
| 72 |
test("an expired session opens nothing", async () => { |
| 73 |
const { token } = must(await logIn(db, { email: "[email protected]", password: "correct horse" })); |
| 74 |
db.query("UPDATE session SET expires_at = '2000-01-01T00:00:00.000Z'").run(); |
| 75 |
expect(sessionWho(db, token)).toEqual({ role: "anon" }); |
| 76 |
}); |
| 77 |
|
| 78 |
test("a new password ends every other session; deleting takes the vault with it", async () => { |
| 79 |
const first = must(await logIn(db, { email: "[email protected]", password: "correct horse" })); |
| 80 |
const second = must(await logIn(db, { email: "[email protected]", password: "correct horse" })); |
| 81 |
const sam = sessionWho(db, first.token); |
| 82 |
expect((await changePassword(db, sam, { current: "wrong", next: "battery staple" })).ok).toBe(false); |
| 83 |
const changed = must(await changePassword(db, sam, { current: "correct horse", next: "battery staple" })); |
| 84 |
expect(sessionWho(db, first.token).role).toBe("anon"); |
| 85 |
expect(sessionWho(db, second.token).role).toBe("anon"); |
| 86 |
expect(sessionWho(db, changed.token).role).toBe("user"); |
| 87 |
expect((await logIn(db, { email: "[email protected]", password: "battery staple" })).ok).toBe(true); |
| 88 |
expect((await deleteAccount(db, sam, { password: "correct horse" })).ok).toBe(false); |
| 89 |
const gone = must(await deleteAccount(db, sam, { password: "battery staple" })); |
| 90 |
expect(gone.notes).toBe(1); |
| 91 |
expect(sessionWho(db, changed.token).role).toBe("anon"); |
| 92 |
expect((await logIn(db, { email: "[email protected]", password: "battery staple" })).ok).toBe(false); |
| 93 |
|
| 94 |
expect((await changePassword(db, { role: "user", id: "u1", name: "a" }, { current: "x", next: "yyyyyyyy" })).ok).toBe(false); |
| 95 |
}); |