benjivers/notes Public Public: anyone can read this; nobody can change it.

Files 6f31b533b13d96cd4b38d39bc59da84ada17375b
95 lines · 4.8 KB · ~1.4k tok Raw History
1
/**
2
 * The app's own accounts: a person with no forge account signs up, gets a
3
 * vault of their own, and nobody — forge user, admin, or another account —
4
 * shares it. Passwords and session tokens are never stored as given.
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
  // A forge user whose id happens to be the same number, and an admin, see nothing of it.
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
  // Nothing stored is the password or the token itself.
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
  // A forge user has no password here to change.
94
  expect((await changePassword(db, { role: "user", id: "u1", name: "a" }, { current: "x", next: "yyyyyyyy" })).ok).toBe(false);
95
});
↑↓ navigate ↵ open esc close
↑↓ navigate ↵ open esc close

Keyboard shortcuts