← back to Ventura Claw Leads
tests/auth.test.js
86 lines
// Auth lib unit tests — bcrypt round-trip + claim token lifecycle.
// Uses the live local PG (PGHOST=/tmp), seeds a temp business, cleans up.
require('dotenv').config({ path: require('path').resolve(__dirname, '..', '.env') });
process.env.SESSION_SECRET = process.env.SESSION_SECRET || 'test-secret';
process.env.NODE_ENV = 'test';
const test = require('node:test');
const assert = require('node:assert/strict');
const auth = require('../lib/auth');
const db = require('../lib/db');
let testBusinessId;
test.before(async () => {
const r = await db.one(
`INSERT INTO businesses (slug, business_name, vertical, status, source)
VALUES ('__test_auth__', 'Auth Test Co', 'creative', 'active', 'test')
ON CONFLICT (slug) DO UPDATE SET business_name = EXCLUDED.business_name
RETURNING id`
);
testBusinessId = r.id;
});
test.after(async () => {
await db.query(`DELETE FROM businesses WHERE slug = '__test_auth__'`);
await db.pool.end();
});
test('bcrypt password round-trip', async () => {
const hash = await auth.hashPassword('correct-horse-battery-staple');
assert.ok(hash.startsWith('$2'), 'bcrypt prefix present');
assert.equal(await auth.verifyPassword('correct-horse-battery-staple', hash), true);
assert.equal(await auth.verifyPassword('wrong-password', hash), false);
assert.equal(await auth.verifyPassword('correct-horse-battery-staple', null), false);
});
test('email shape validator', () => {
assert.equal(auth.isEmailShape('a@b.co'), true);
assert.equal(auth.isEmailShape('Mixed.Case+Tag@Sub.Example.com'), true);
assert.equal(auth.isEmailShape('no-at-sign'), false);
assert.equal(auth.isEmailShape(''), false);
assert.equal(auth.isEmailShape('a@b'), false);
});
test('email normalization', () => {
assert.equal(auth.normalizeEmail(' Foo@BAR.com '), 'foo@bar.com');
assert.equal(auth.normalizeEmail(null), '');
});
test('claim token: mint, consume, mark consumed, double-consume blocked', async () => {
const { token, expiresAt } = await auth.issueClaimToken({
businessId: testBusinessId,
email: 'tok-test@example.com',
ipHash: 'abc'
});
assert.equal(token.length, 32);
assert.ok(new Date(expiresAt) > new Date(), 'expiresAt is in the future');
const r1 = await auth.consumeClaimToken(token);
assert.equal(r1.ok, true);
assert.equal(r1.claim.business_id, testBusinessId);
await auth.markClaimConsumed(token);
const r2 = await auth.consumeClaimToken(token);
assert.equal(r2.ok, false);
assert.equal(r2.reason, 'already_used');
// Cleanup
await db.query(`DELETE FROM claim_tokens WHERE token = $1`, [token]);
});
test('claim token: invalid token returns ok:false', async () => {
const r = await auth.consumeClaimToken('not-a-real-token-32-chars-abcde');
assert.equal(r.ok, false);
assert.equal(r.reason, 'invalid');
});
test('createUser idempotent on email conflict', async () => {
const a = await auth.createUser({ email: 'create-test@example.com', businessId: testBusinessId, displayName: 'A' });
assert.ok(a.id);
const b = await auth.createUser({ email: 'create-test@example.com', businessId: testBusinessId, displayName: 'B' });
assert.equal(b.id, a.id, 'second createUser returns same row');
await db.query(`DELETE FROM business_users WHERE email = 'create-test@example.com'`);
});