const { describe, it, after, beforeEach } = require('node:test'); const assert = require('node:assert'); const request = require('supertest'); // Setup must be required first const { cleanTestData } = require('./setup'); // Mock the email service before requiring app const emailService = require('../src/services/email'); let lastSentEmail = null; emailService.sendMagicLinkEmail = async (email, url) => { lastSentEmail = { email, url }; return { MessageId: 'test-message-id' }; }; // Mock Stripe service const stripeService = require('../src/services/stripe'); stripeService.checkPremiumStatus = async () => ({ premium: false, expiresAt: null }); const { app } = require('../src/index'); const storage = require('../src/storage'); describe('Auth Routes', () => { beforeEach(() => { cleanTestData(); storage.ensureDirectories(); lastSentEmail = null; emailService.validateEmail = async () => ({ valid: true }); }); after(() => { cleanTestData(); }); describe('POST /auth/send-magic-link', () => { it('should send magic link for valid email', async () => { const res = await request(app) .post('/auth/send-magic-link') .send({ email: 'test@example.com' }); assert.strictEqual(res.status, 200); assert.ok(res.body.request_id); assert.strictEqual(lastSentEmail.email, 'test@example.com'); assert.ok(lastSentEmail.url.includes(res.body.request_id)); }); it('should reject invalid email format', async () => { const res = await request(app) .post('/auth/send-magic-link') .send({ email: 'not-an-email' }); assert.strictEqual(res.status, 400); assert.ok(res.body.error.includes('Invalid email')); }); it('should reject missing email', async () => { const res = await request(app) .post('/auth/send-magic-link') .send({}); assert.strictEqual(res.status, 400); }); it('should reject email with low confidence from SES insights', async () => { emailService.validateEmail = async () => ({ valid: false, reason: 'Low confidence' }); const res = await request(app) .post('/auth/send-magic-link') .send({ email: 'bad@disposable.com' }); assert.strictEqual(res.status, 400); assert.ok(res.body.error.includes('invalid')); }); it('should rate limit after too many requests', async () => { const email = 'ratelimit@example.com'; // Make max allowed requests for (let i = 0; i < 5; i++) { await request(app) .post('/auth/send-magic-link') .send({ email }); } // Next should be rate limited const res = await request(app) .post('/auth/send-magic-link') .send({ email }); assert.strictEqual(res.status, 429); assert.ok(res.headers['retry-after']); }); }); describe('GET /auth/verify', () => { it('should verify valid magic link and show success page', async () => { // Create auth request first const sendRes = await request(app) .post('/auth/send-magic-link') .send({ email: 'verify@example.com' }); const requestId = sendRes.body.request_id; const res = await request(app) .get(`/auth/verify?token=${requestId}`); assert.strictEqual(res.status, 200); assert.ok(res.text.includes("You're signed in")); // Auth request should now be verified const authRequest = storage.getAuthRequest(requestId); assert.strictEqual(authRequest.status, 'verified'); assert.ok(authRequest.session_token); }); it('should reject missing token', async () => { const res = await request(app).get('/auth/verify'); assert.strictEqual(res.status, 400); assert.ok(res.text.includes('Missing token')); }); it('should reject invalid token', async () => { const res = await request(app) .get('/auth/verify?token=invalid-token'); assert.strictEqual(res.status, 404); assert.ok(res.text.includes('expired or invalid')); }); }); describe('GET /auth/poll', () => { it('should return pending for unverified request', async () => { const sendRes = await request(app) .post('/auth/send-magic-link') .send({ email: 'poll@example.com' }); const requestId = sendRes.body.request_id; const res = await request(app) .get(`/auth/poll?request_id=${requestId}`); assert.strictEqual(res.status, 200); assert.strictEqual(res.body.status, 'pending'); }); it('should return verified with token after magic link clicked', async () => { // Create and verify request const sendRes = await request(app) .post('/auth/send-magic-link') .send({ email: 'poll2@example.com' }); const requestId = sendRes.body.request_id; // Click magic link await request(app).get(`/auth/verify?token=${requestId}`); // Poll should return verified const res = await request(app) .get(`/auth/poll?request_id=${requestId}`); assert.strictEqual(res.status, 200); assert.strictEqual(res.body.status, 'verified'); assert.ok(res.body.session_token); assert.strictEqual(res.body.email, 'poll2@example.com'); }); it('should delete auth request after returning verified', async () => { const sendRes = await request(app) .post('/auth/send-magic-link') .send({ email: 'poll3@example.com' }); const requestId = sendRes.body.request_id; await request(app).get(`/auth/verify?token=${requestId}`); await request(app).get(`/auth/poll?request_id=${requestId}`); // Second poll should return 404 const res = await request(app) .get(`/auth/poll?request_id=${requestId}`); assert.strictEqual(res.status, 404); }); it('should reject invalid request_id', async () => { const res = await request(app) .get('/auth/poll?request_id=invalid'); assert.strictEqual(res.status, 404); }); }); });