0ae59f3550
Introduces RECAP_MODE=multi alongside single-mode self-host: - Tenant auth + accounts (magic-link via System SMTP), per-tenant credit pool, anonymous trial minting with per-IP/-64 caps - Self-serve Pro/Max purchase: inline Lightning (BTCPay) + card (Zaprite), prepaid 30-day periods, expiry-reminder emails - Core-decoupling: relay owns cloud tier/expiry keyed by Recaps user-id - SQLite (better-sqlite3) schema for multi-mode; filesystem unchanged for single - StartOS actions/versions through 0.2.155
80 lines
2.7 KiB
JavaScript
80 lines
2.7 KiB
JavaScript
// Tests for the ephemeral sessions the subscription background processor
|
|
// mints to run /api/process AS each item's owner (per-tenant subscriptions,
|
|
// step 4). The mechanism reuses the real sessions table, so verifying the
|
|
// row it writes is valid + non-expired (and gets cleaned up) is enough to
|
|
// trust the existing cookie → tenant-auth → req.user chain.
|
|
|
|
import { test, describe, before, after } from "node:test";
|
|
import { strict as assert } from "node:assert";
|
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import path from "node:path";
|
|
|
|
import { initDb, getDb, closeDb } from "../db.js";
|
|
import {
|
|
mintInternalSession,
|
|
deleteInternalSession,
|
|
adminUserId,
|
|
} from "../auth-routes.js";
|
|
|
|
let dataDir;
|
|
|
|
function makeUser({ id, email, isAdmin = 0 }) {
|
|
getDb()
|
|
.prepare(
|
|
`INSERT INTO users (id, email, created_at, synthetic_install_id, is_admin)
|
|
VALUES (?, ?, ?, ?, ?)`,
|
|
)
|
|
.run(id, email, Date.now(), `inst-${id}`, isAdmin);
|
|
}
|
|
|
|
before(async () => {
|
|
dataDir = mkdtempSync(path.join(tmpdir(), "recap-sess-"));
|
|
await initDb({ dataDir });
|
|
});
|
|
|
|
after(() => {
|
|
closeDb();
|
|
rmSync(dataDir, { recursive: true, force: true });
|
|
});
|
|
|
|
describe("mintInternalSession / deleteInternalSession", () => {
|
|
test("creates a valid, non-expired session row for the user", () => {
|
|
makeUser({ id: "u-tenant", email: "tenant@example.com" });
|
|
const token = mintInternalSession("u-tenant");
|
|
assert.ok(typeof token === "string" && token.length > 20);
|
|
|
|
// Looks up exactly like tenant-auth does: by id, must be unexpired.
|
|
const row = getDb()
|
|
.prepare("SELECT * FROM sessions WHERE id = ? AND expires_at > ?")
|
|
.get(token, Date.now());
|
|
assert.ok(row, "session row exists and is not expired");
|
|
assert.equal(row.user_id, "u-tenant");
|
|
assert.ok(row.expires_at > Date.now(), "expires in the future");
|
|
});
|
|
|
|
test("deleteInternalSession removes the row (no lingering identity)", () => {
|
|
makeUser({ id: "u-temp", email: "temp@example.com" });
|
|
const token = mintInternalSession("u-temp");
|
|
assert.ok(getDb().prepare("SELECT 1 FROM sessions WHERE id = ?").get(token));
|
|
deleteInternalSession(token);
|
|
assert.equal(
|
|
getDb().prepare("SELECT 1 FROM sessions WHERE id = ?").get(token),
|
|
undefined,
|
|
);
|
|
});
|
|
|
|
test("deleteInternalSession tolerates null / unknown tokens", () => {
|
|
// Should not throw.
|
|
deleteInternalSession(null);
|
|
deleteInternalSession("does-not-exist");
|
|
});
|
|
});
|
|
|
|
describe("adminUserId", () => {
|
|
test("returns the operator (is_admin = 1) user id", () => {
|
|
makeUser({ id: "u-admin", email: "admin@example.com", isAdmin: 1 });
|
|
assert.equal(adminUserId(), "u-admin");
|
|
});
|
|
});
|