Files
recap/server/test/internal-sessions.test.js
Keysat 0ae59f3550 Add multi-tenant cloud mode: self-serve purchase, credit metering, core-decoupling
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
2026-06-13 14:25:05 -05:00

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");
});
});