54 lines
2.6 KiB
TypeScript
54 lines
2.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type { StorageAdapter } from "./saveRepository";
|
|
import { AccountRepository } from "./accountRepository";
|
|
|
|
function memoryStorage(): StorageAdapter {
|
|
const data = new Map<string, string>();
|
|
return {
|
|
getItem: (key) => data.get(key) ?? null,
|
|
setItem: (key, value) => { data.set(key, value); },
|
|
};
|
|
}
|
|
|
|
const testHasher = async (password: string, salt: string) => {
|
|
const checksum = [...password].reduce((total, character) => total + character.charCodeAt(0), 0);
|
|
return `derived:${salt}:${checksum}`;
|
|
};
|
|
|
|
describe("AccountRepository", () => {
|
|
it("requires both a username and password", async () => {
|
|
const repository = new AccountRepository(memoryStorage(), testHasher, () => "salt");
|
|
|
|
await expect(repository.create("", "secret")).resolves.toEqual({ ok: false, reason: "missing-credentials" });
|
|
await expect(repository.create("healer", "")).resolves.toEqual({ ok: false, reason: "missing-credentials" });
|
|
await expect(repository.authenticate("healer", "")).resolves.toEqual({ ok: false, reason: "missing-credentials" });
|
|
});
|
|
|
|
it("requires account creation before sign-in", async () => {
|
|
const repository = new AccountRepository(memoryStorage(), testHasher, () => "salt");
|
|
|
|
await expect(repository.authenticate("new-healer", "secret")).resolves.toEqual({ ok: false, reason: "account-not-found" });
|
|
await expect(repository.create("new-healer", "secret")).resolves.toEqual({ ok: true, username: "new-healer" });
|
|
await expect(repository.authenticate("new-healer", "secret")).resolves.toEqual({ ok: true, username: "new-healer" });
|
|
});
|
|
|
|
it("rejects an incorrect password and duplicate account names", async () => {
|
|
const repository = new AccountRepository(memoryStorage(), testHasher, () => "salt");
|
|
await repository.create("Wayfinder", "correct");
|
|
|
|
await expect(repository.authenticate("wayfinder", "wrong")).resolves.toEqual({ ok: false, reason: "invalid-password" });
|
|
await expect(repository.create(" wayfinder ", "another")).resolves.toEqual({ ok: false, reason: "account-exists" });
|
|
});
|
|
|
|
it("persists only a derived password verifier", async () => {
|
|
const storage = memoryStorage();
|
|
const repository = new AccountRepository(storage, testHasher, () => "unique-salt");
|
|
await repository.create("healer", "plaintext-secret");
|
|
|
|
const persisted = storage.getItem("i-want-to-heal:accounts:v1") ?? "";
|
|
expect(persisted).toContain("derived:unique-salt:");
|
|
expect(persisted).not.toContain("plaintext-secret");
|
|
expect(JSON.parse(persisted).healer).not.toHaveProperty("password");
|
|
});
|
|
});
|