馃 distributed transcription service thistle.dunkirk.sh
1import { describe, test, expect, beforeEach, afterEach } from "bun:test"; 2import db from "../db/schema"; 3import { 4 createUser, 5 createEmailVerificationToken, 6 verifyEmailToken, 7 isEmailVerified, 8 createPasswordResetToken, 9 verifyPasswordResetToken, 10 consumePasswordResetToken, 11} from "./auth"; 12 13describe("Email Verification", () => { 14 let userId: number; 15 const testEmail = `test-verify-${Date.now()}@example.com`; 16 17 beforeEach(async () => { 18 // Create test user 19 const user = await createUser(testEmail, "a".repeat(64), "Test User"); 20 userId = user.id; 21 }); 22 23 afterEach(() => { 24 // Cleanup 25 db.run("DELETE FROM users WHERE email = ?", [testEmail]); 26 db.run("DELETE FROM email_verification_tokens WHERE user_id = ?", [ 27 userId, 28 ]); 29 }); 30 31 test("creates verification token", () => { 32 const token = createEmailVerificationToken(userId); 33 expect(token).toBeDefined(); 34 expect(typeof token).toBe("string"); 35 expect(token.length).toBeGreaterThan(0); 36 }); 37 38 test("verifies valid token", () => { 39 const token = createEmailVerificationToken(userId); 40 const result = verifyEmailToken(token); 41 42 expect(result).not.toBeNull(); 43 expect(result?.userId).toBe(userId); 44 expect(result?.email).toBe(testEmail); 45 expect(isEmailVerified(userId)).toBe(true); 46 }); 47 48 test("rejects invalid token", () => { 49 const result = verifyEmailToken("invalid-token-12345"); 50 expect(result).toBeNull(); 51 expect(isEmailVerified(userId)).toBe(false); 52 }); 53 54 test("token is one-time use", () => { 55 const token = createEmailVerificationToken(userId); 56 57 // First use succeeds 58 const firstResult = verifyEmailToken(token); 59 expect(firstResult).not.toBeNull(); 60 61 // Second use fails 62 const secondResult = verifyEmailToken(token); 63 expect(secondResult).toBeNull(); 64 }); 65 66 test("rejects expired token", () => { 67 const token = createEmailVerificationToken(userId); 68 69 // Manually expire the token 70 db.run( 71 "UPDATE email_verification_tokens SET expires_at = ? WHERE token = ?", 72 [Math.floor(Date.now() / 1000) - 100, token], 73 ); 74 75 const result = verifyEmailToken(token); 76 expect(result).toBeNull(); 77 }); 78 79 test("replaces existing token when creating new one", () => { 80 const token1 = createEmailVerificationToken(userId); 81 const token2 = createEmailVerificationToken(userId); 82 83 // First token should be invalidated 84 expect(verifyEmailToken(token1)).toBeNull(); 85 86 // Second token should work 87 expect(verifyEmailToken(token2)).not.toBeNull(); 88 }); 89}); 90 91describe("Password Reset", () => { 92 let userId: number; 93 const testEmail = `test-reset-${Date.now()}@example.com`; 94 95 beforeEach(async () => { 96 const user = await createUser(testEmail, "a".repeat(64), "Test User"); 97 userId = user.id; 98 }); 99 100 afterEach(() => { 101 db.run("DELETE FROM users WHERE email = ?", [testEmail]); 102 db.run("DELETE FROM password_reset_tokens WHERE user_id = ?", [userId]); 103 }); 104 105 test("creates reset token", () => { 106 const token = createPasswordResetToken(userId); 107 expect(token).toBeDefined(); 108 expect(typeof token).toBe("string"); 109 expect(token.length).toBeGreaterThan(0); 110 }); 111 112 test("verifies valid reset token", () => { 113 const token = createPasswordResetToken(userId); 114 const verifiedUserId = verifyPasswordResetToken(token); 115 116 expect(verifiedUserId).toBe(userId); 117 }); 118 119 test("rejects invalid reset token", () => { 120 const verifiedUserId = verifyPasswordResetToken("invalid-token-12345"); 121 expect(verifiedUserId).toBeNull(); 122 }); 123 124 test("consumes reset token", () => { 125 const token = createPasswordResetToken(userId); 126 127 // Token works before consumption 128 expect(verifyPasswordResetToken(token)).toBe(userId); 129 130 // Consume token 131 consumePasswordResetToken(token); 132 133 // Token no longer works 134 expect(verifyPasswordResetToken(token)).toBeNull(); 135 }); 136 137 test("rejects expired reset token", () => { 138 const token = createPasswordResetToken(userId); 139 140 // Manually expire the token 141 db.run( 142 "UPDATE password_reset_tokens SET expires_at = ? WHERE token = ?", 143 [Math.floor(Date.now() / 1000) - 100, token], 144 ); 145 146 const verifiedUserId = verifyPasswordResetToken(token); 147 expect(verifiedUserId).toBeNull(); 148 }); 149 150 test("replaces existing reset token when creating new one", () => { 151 const token1 = createPasswordResetToken(userId); 152 const token2 = createPasswordResetToken(userId); 153 154 // First token should be invalidated 155 expect(verifyPasswordResetToken(token1)).toBeNull(); 156 157 // Second token should work 158 expect(verifyPasswordResetToken(token2)).toBe(userId); 159 }); 160});