131 lines
4.4 KiB
TypeScript
131 lines
4.4 KiB
TypeScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import dotenv from "dotenv";
|
|
import { createSession, hashPassword, verifyPassword } from "../lib/server/auth";
|
|
import { loginUser, registerUser } from "../lib/server/auth-service";
|
|
import getPool from "../lib/server/db";
|
|
import { cleanupTestData, cleanupTestDataFromPool } from "./test-helpers";
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
const envLoaded = dotenv.config({ path: path.resolve(__dirname, "../../../.env") });
|
|
|
|
const hasDb = Boolean(process.env.DATABASE_URL);
|
|
|
|
test("password hashing works", async () => {
|
|
const hash = await hashPassword("test-password");
|
|
assert.ok(await verifyPassword("test-password", hash));
|
|
assert.ok(!(await verifyPassword("wrong", hash)));
|
|
});
|
|
|
|
test("createSession inserts row", async t => {
|
|
if (!hasDb) {
|
|
t.skip("DATABASE_URL not set");
|
|
return;
|
|
}
|
|
if (envLoaded.error)
|
|
t.diagnostic(String(envLoaded.error));
|
|
|
|
const pool = getPool();
|
|
const client = await pool.connect();
|
|
const email = `test_${Date.now()}@example.com`;
|
|
try {
|
|
const { rows } = await client.query(
|
|
"insert into users(email, password_hash) values($1,$2) returning id",
|
|
[email, await hashPassword("test-password")]
|
|
);
|
|
const userId = rows[0].id as number;
|
|
const session = await createSession(userId);
|
|
const { rows: sessionRows } = await client.query(
|
|
"select id from sessions where user_id=$1 and expires_at > now()",
|
|
[userId]
|
|
);
|
|
assert.ok(session.token.length > 10);
|
|
assert.ok(sessionRows.length === 1);
|
|
} finally {
|
|
await cleanupTestData(client, { emails: [email] });
|
|
client.release();
|
|
}
|
|
});
|
|
|
|
test("createSession supports ttl override", async t => {
|
|
if (!hasDb) {
|
|
t.skip("DATABASE_URL not set");
|
|
return;
|
|
}
|
|
if (envLoaded.error)
|
|
t.diagnostic(String(envLoaded.error));
|
|
|
|
const pool = getPool();
|
|
const client = await pool.connect();
|
|
const email = `test_${Date.now()}_ttl@example.com`;
|
|
const ttlMs = 60 * 60 * 1000;
|
|
const nowMs = Date.now();
|
|
try {
|
|
const { rows } = await client.query(
|
|
"insert into users(email, password_hash) values($1,$2) returning id",
|
|
[email, await hashPassword("test-password")]
|
|
);
|
|
const userId = rows[0].id as number;
|
|
const session = await createSession(userId, { ttlMs });
|
|
const { rows: sessionRows } = await client.query(
|
|
"select expires_at from sessions where user_id=$1 order by created_at desc limit 1",
|
|
[userId]
|
|
);
|
|
const dbExpiresAt = new Date(sessionRows[0].expires_at).getTime();
|
|
const expectedMin = nowMs + ttlMs - 5000;
|
|
const expectedMax = nowMs + ttlMs + 5000;
|
|
assert.ok(session.expiresAt.getTime() >= expectedMin);
|
|
assert.ok(session.expiresAt.getTime() <= expectedMax);
|
|
assert.ok(dbExpiresAt >= expectedMin);
|
|
assert.ok(dbExpiresAt <= expectedMax);
|
|
} finally {
|
|
await cleanupTestData(client, { emails: [email] });
|
|
client.release();
|
|
}
|
|
});
|
|
|
|
test("loginUser respects remember flag", async t => {
|
|
if (!hasDb) {
|
|
t.skip("DATABASE_URL not set");
|
|
return;
|
|
}
|
|
if (envLoaded.error) t.diagnostic(String(envLoaded.error));
|
|
|
|
const email = `remember_${Date.now()}@example.com`;
|
|
const password = "test-password";
|
|
const result = await registerUser({ email, password, displayName: "" });
|
|
try {
|
|
const rememberTrue = await loginUser({ email, password, remember: true });
|
|
const rememberFalse = await loginUser({ email, password, remember: false });
|
|
assert.ok(rememberTrue.session.ttlMs > rememberFalse.session.ttlMs);
|
|
} finally {
|
|
const pool = getPool();
|
|
await cleanupTestDataFromPool(pool, { userIds: [result.user.id] });
|
|
}
|
|
});
|
|
|
|
test("registerUser rejects duplicate email", async t => {
|
|
if (!hasDb) {
|
|
t.skip("DATABASE_URL not set");
|
|
return;
|
|
}
|
|
if (envLoaded.error) t.diagnostic(String(envLoaded.error));
|
|
|
|
const pool = getPool();
|
|
const email = `dup_${Date.now()}@example.com`;
|
|
const password = "test-password";
|
|
const mixedCase = email.toUpperCase();
|
|
const result = await registerUser({ email, password, displayName: "" });
|
|
try {
|
|
await assert.rejects(
|
|
() => registerUser({ email: mixedCase, password, displayName: "" }),
|
|
{ message: "EMAIL_EXISTS" }
|
|
);
|
|
} finally {
|
|
await cleanupTestDataFromPool(pool, { userIds: [result.user.id] });
|
|
}
|
|
});
|