test: cover atomic source invites
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { after, test } from "node:test";
|
||||
import { createHash, randomUUID } from "node:crypto";
|
||||
import { rmSync } from "node:fs";
|
||||
|
||||
const databasePath = `/tmp/mebbling-invites-${randomUUID()}.db`;
|
||||
process.env.DATABASE_PATH = databasePath;
|
||||
delete process.env.HUB_BUILD;
|
||||
let database: typeof import("../lib/db").db | undefined;
|
||||
after(() => { database?.close(); rmSync(databasePath, { force: true }); rmSync(`${databasePath}-wal`, { force: true }); rmSync(`${databasePath}-shm`, { force: true }); });
|
||||
|
||||
test("consumes a source invitation exactly once and grants its role", async () => {
|
||||
const { db } = await import("../lib/db"); database = db;
|
||||
const { acceptSourceInvite } = await import("../lib/invites");
|
||||
const owner = Number(db.prepare("INSERT INTO users(username,password_hash) VALUES('owner','hash')").run().lastInsertRowid);
|
||||
const member = Number(db.prepare("INSERT INTO users(username,password_hash) VALUES('member','hash')").run().lastInsertRowid);
|
||||
const source = Number(db.prepare("INSERT INTO sources(user_id,name,base_url,token_encrypted) VALUES(?,?,?,?)").run(owner, "Source", "https://example.test", "encrypted").lastInsertRowid);
|
||||
const token = "single-use-token";
|
||||
db.prepare("INSERT INTO source_invites(source_id,token_hash,role,expires_at,created_by) VALUES(?,?,?,datetime('now','+1 day'),?)").run(source, createHash("sha256").update(token).digest("hex"), "editor", owner);
|
||||
assert.deepEqual(acceptSourceInvite(member, token), { id: 1, source_id: source, role: "editor" });
|
||||
assert.equal((db.prepare("SELECT role FROM source_members WHERE source_id=? AND user_id=?").get(source, member) as { role: string }).role, "editor");
|
||||
assert.throws(() => acceptSourceInvite(member, token), /已使用/);
|
||||
});
|
||||
Reference in New Issue
Block a user