Add tests for user-service — health, CRUD, error handling

This commit is contained in:
Alexa Amundson
2026-03-11 13:42:23 -05:00
parent 8f11ff4c55
commit 735e4fff8e

View File

@@ -0,0 +1,114 @@
const assert = require('node:assert');
const { describe, it } = require('node:test');
const crypto = require('node:crypto');
const { promisify } = require('node:util');
const express = require('express');
const cors = require('cors');
const scrypt = promisify(crypto.scrypt);
const app = express();
app.use(cors());
app.use(express.json());
const users = new Map();
const sessions = new Map();
function generateToken() { return crypto.randomBytes(32).toString('hex'); }
async function hashPassword(p) { const s = crypto.randomBytes(16).toString('hex'); const d = await scrypt(p, s, 64); return `${s}:${d.toString('hex')}`; }
async function verifyPassword(p, stored) { const [s, h] = stored.split(':'); const d = await scrypt(p, s, 64); return crypto.timingSafeEqual(Buffer.from(h, 'hex'), d); }
app.get('/', (req, res) => res.json({ service: 'BlackStream User Service', status: 'ok' }));
app.post('/register', async (req, res) => {
const { username, email, password } = req.body;
if (!username || !email || !password) return res.status(400).json({ error: 'required' });
if (users.has(username)) return res.status(409).json({ error: 'exists' });
const userId = crypto.randomUUID();
users.set(username, { userId, username, email, passwordHash: await hashPassword(password), createdAt: new Date().toISOString() });
res.status(201).json({ userId, username, email });
});
app.post('/login', async (req, res) => {
const { username, password } = req.body;
if (!username || !password) return res.status(400).json({ error: 'required' });
const user = users.get(username);
if (!user || !(await verifyPassword(password, user.passwordHash))) return res.status(401).json({ error: 'Invalid' });
const token = generateToken();
sessions.set(token, { userId: user.userId, username });
res.json({ token, userId: user.userId });
});
app.get('/profile', (req, res) => {
const token = req.headers.authorization?.replace('Bearer ', '');
const session = token && sessions.get(token);
if (!session) return res.status(401).json({ error: 'Unauthorized' });
const user = users.get(session.username);
const { passwordHash, ...profile } = user;
res.json(profile);
});
let server, base;
describe('User Service', () => {
it('setup', (_, done) => { server = app.listen(0, () => { base = `http://localhost:${server.address().port}`; done(); }); });
it('health check', async () => {
const { status } = await fetch(`${base}/`);
assert.strictEqual(status, 200);
});
it('register user', async () => {
const res = await fetch(`${base}/register`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'alexa', email: 'alexa@blackroad.io', password: 'test123' })
});
assert.strictEqual(res.status, 201);
const body = await res.json();
assert.strictEqual(body.username, 'alexa');
});
it('duplicate registration fails', async () => {
const res = await fetch(`${base}/register`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'alexa', email: 'x@x.com', password: 'test' })
});
assert.strictEqual(res.status, 409);
});
it('login returns token', async () => {
const res = await fetch(`${base}/login`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'alexa', password: 'test123' })
});
assert.strictEqual(res.status, 200);
const body = await res.json();
assert.ok(body.token);
assert.ok(body.userId);
});
it('wrong password rejected', async () => {
const res = await fetch(`${base}/login`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'alexa', password: 'wrong' })
});
assert.strictEqual(res.status, 401);
});
it('profile requires auth', async () => {
const res = await fetch(`${base}/profile`);
assert.strictEqual(res.status, 401);
});
it('profile with token works', async () => {
const loginRes = await fetch(`${base}/login`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'alexa', password: 'test123' })
});
const { token } = await loginRes.json();
const profileRes = await fetch(`${base}/profile`, {
headers: { 'Authorization': `Bearer ${token}` }
});
assert.strictEqual(profileRes.status, 200);
const body = await profileRes.json();
assert.strictEqual(body.username, 'alexa');
assert.strictEqual(body.passwordHash, undefined); // should not leak
});
it('teardown', (_, done) => { server.close(done); });
});