914b7e41f3
Add assertion for `connect.sid` cookie in login helper and refine user deletion in `executive-meetings-visibility.test.mjs` to only remove users from the current test run.
125 lines
4.2 KiB
JavaScript
125 lines
4.2 KiB
JavaScript
import { test, before, after } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import pg from "pg";
|
|
|
|
const API_BASE = process.env.TEST_API_BASE ?? "http://localhost:8080";
|
|
const DATABASE_URL = process.env.DATABASE_URL;
|
|
if (!DATABASE_URL) {
|
|
throw new Error("DATABASE_URL must be set to run these tests");
|
|
}
|
|
|
|
const TEST_PASSWORD = "TestPass123!";
|
|
const TEST_PASSWORD_HASH =
|
|
"$2b$10$Bs636ukPMyz01nKrsi.5m.JlDXSN22AVCvn8cgPWWDbo5yJRQX2vu";
|
|
|
|
const pool = new pg.Pool({ connectionString: DATABASE_URL });
|
|
|
|
// Per-run uniqueness: every row this test creates is tagged with this stamp
|
|
// so the after-hook can clean up (and a defensive sweep can mop up any
|
|
// leftovers from a prior aborted run).
|
|
const STAMP = `${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
|
const USERNAME_PREFIX = `emvis_${STAMP}_`;
|
|
|
|
let receiverUserId;
|
|
let receiverUsername;
|
|
let receiverCookie;
|
|
|
|
async function login(username, password) {
|
|
const res = await fetch(`${API_BASE}/api/auth/login`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ username, password }),
|
|
});
|
|
assert.equal(res.status, 200, `login expected 200, got ${res.status}`);
|
|
const setCookie = res.headers.get("set-cookie");
|
|
assert.ok(setCookie, "login response missing set-cookie header");
|
|
const cookie = setCookie
|
|
.split(",")
|
|
.map((c) => c.split(";")[0].trim())
|
|
.find((c) => c.startsWith("connect.sid="));
|
|
assert.ok(cookie, "no connect.sid cookie returned by login");
|
|
return cookie;
|
|
}
|
|
|
|
async function listApps(cookie) {
|
|
const res = await fetch(`${API_BASE}/api/apps`, {
|
|
headers: { cookie },
|
|
});
|
|
assert.equal(res.status, 200, `apps expected 200, got ${res.status}`);
|
|
return res.json();
|
|
}
|
|
|
|
before(async () => {
|
|
// Defensive sweep: only remove leftovers from this exact run's stamp,
|
|
// which is fresh per process. Avoids touching unrelated fixtures that
|
|
// happen to share the `emvis_` family in a shared test DB.
|
|
await pool.query(
|
|
`DELETE FROM users WHERE username LIKE $1`,
|
|
[`${USERNAME_PREFIX}%`],
|
|
);
|
|
|
|
receiverUsername = `${USERNAME_PREFIX}receiver`;
|
|
const u = await pool.query(
|
|
`INSERT INTO users (username, email, password_hash, display_name_en,
|
|
preferred_language, is_active)
|
|
VALUES ($1, $2, $3, 'EM Visibility Test', 'en', true)
|
|
RETURNING id`,
|
|
[receiverUsername, `${receiverUsername}@ex.com`, TEST_PASSWORD_HASH],
|
|
);
|
|
receiverUserId = u.rows[0].id;
|
|
|
|
// Give them only the 'order_receiver' role — explicitly NOT any
|
|
// executive_* role. They should also not be in any admin group.
|
|
await pool.query(
|
|
`INSERT INTO user_roles (user_id, role_id)
|
|
SELECT $1, id FROM roles WHERE name = 'order_receiver'`,
|
|
[receiverUserId],
|
|
);
|
|
|
|
receiverCookie = await login(receiverUsername, TEST_PASSWORD);
|
|
});
|
|
|
|
after(async () => {
|
|
if (receiverUserId) {
|
|
await pool.query(
|
|
`DELETE FROM user_roles WHERE user_id = $1`,
|
|
[receiverUserId],
|
|
);
|
|
await pool.query(
|
|
`DELETE FROM user_groups WHERE user_id = $1`,
|
|
[receiverUserId],
|
|
);
|
|
await pool.query(`DELETE FROM users WHERE id = $1`, [receiverUserId]);
|
|
}
|
|
await pool.end();
|
|
});
|
|
|
|
test("user with only order_receiver role does NOT see the executive-meetings app", async () => {
|
|
const apps = await listApps(receiverCookie);
|
|
const slugs = apps.map((a) => a.slug);
|
|
assert.ok(
|
|
!slugs.includes("executive-meetings"),
|
|
`expected executive-meetings NOT in apps for non-executive user; got slugs: ${slugs.join(", ")}`,
|
|
);
|
|
});
|
|
|
|
test("after granting executive_coordinator, the same user DOES see executive-meetings", async () => {
|
|
await pool.query(
|
|
`INSERT INTO user_roles (user_id, role_id)
|
|
SELECT $1, id FROM roles WHERE name = 'executive_coordinator'
|
|
ON CONFLICT DO NOTHING`,
|
|
[receiverUserId],
|
|
);
|
|
|
|
// Re-login so the new role is reflected in the session's permission cache
|
|
// (some setups cache derived permissions on the session object).
|
|
receiverCookie = await login(receiverUsername, TEST_PASSWORD);
|
|
|
|
const apps = await listApps(receiverCookie);
|
|
const slugs = apps.map((a) => a.slug);
|
|
assert.ok(
|
|
slugs.includes("executive-meetings"),
|
|
`expected executive-meetings in apps after granting executive_coordinator; got slugs: ${slugs.join(", ")}`,
|
|
);
|
|
});
|