馃 distributed transcription service thistle.dunkirk.sh
1import { expect, test } from "bun:test"; 2import db from "../db/schema"; 3import { 4 consumeEmailChangeToken, 5 createEmailChangeToken, 6 createUser, 7 getUserByEmail, 8 updateUserEmail, 9 verifyEmailChangeToken, 10} from "./auth"; 11 12test("email change token lifecycle", async () => { 13 // Create a test user with unique email 14 const timestamp = Date.now(); 15 const user = await createUser( 16 `test-email-change-${timestamp}@example.com`, 17 "password123", 18 "Test User", 19 ); 20 21 // Create an email change token 22 const newEmail = `new-email-${timestamp}@example.com`; 23 const token = createEmailChangeToken(user.id, newEmail); 24 25 expect(token).toBeTruthy(); 26 expect(token.length).toBeGreaterThan(0); 27 28 // Verify the token 29 const result = verifyEmailChangeToken(token); 30 expect(result).toBeTruthy(); 31 expect(result?.userId).toBe(user.id); 32 expect(result?.newEmail).toBe(newEmail); 33 34 // Update the email 35 if (result) { 36 updateUserEmail(result.userId, result.newEmail); 37 } 38 39 // Consume the token 40 consumeEmailChangeToken(token); 41 42 // Verify the email was updated 43 const updatedUser = getUserByEmail(newEmail); 44 expect(updatedUser).toBeTruthy(); 45 expect(updatedUser?.id).toBe(user.id); 46 expect(updatedUser?.email).toBe(newEmail); 47 48 // Verify the token can't be used again 49 const result2 = verifyEmailChangeToken(token); 50 expect(result2).toBeNull(); 51 52 // Clean up 53 db.run("DELETE FROM users WHERE id = ?", [user.id]); 54}); 55 56test("email change token expires", async () => { 57 // Create a test user with unique email 58 const timestamp = Date.now(); 59 const user = await createUser( 60 `test-expire-${timestamp}@example.com`, 61 "password123", 62 "Test User", 63 ); 64 65 // Create an email change token 66 const newEmail = `new-expire-${timestamp}@example.com`; 67 const token = createEmailChangeToken(user.id, newEmail); 68 69 // Manually expire the token 70 db.run("UPDATE email_change_tokens SET expires_at = ? WHERE token = ?", [ 71 Math.floor(Date.now() / 1000) - 1, 72 token, 73 ]); 74 75 // Verify the token is expired 76 const result = verifyEmailChangeToken(token); 77 expect(result).toBeNull(); 78 79 // Clean up 80 db.run("DELETE FROM users WHERE id = ?", [user.id]); 81}); 82 83test("only one email change token per user", async () => { 84 // Create a test user with unique email 85 const timestamp = Date.now(); 86 const user = await createUser( 87 `test-single-token-${timestamp}@example.com`, 88 "password123", 89 "Test User", 90 ); 91 92 // Create first token 93 const token1 = createEmailChangeToken( 94 user.id, 95 `email1-${timestamp}@example.com`, 96 ); 97 98 // Create second token (should delete first) 99 const token2 = createEmailChangeToken( 100 user.id, 101 `email2-${timestamp}@example.com`, 102 ); 103 104 // First token should be invalid 105 const result1 = verifyEmailChangeToken(token1); 106 expect(result1).toBeNull(); 107 108 // Second token should work 109 const result2 = verifyEmailChangeToken(token2); 110 expect(result2).toBeTruthy(); 111 expect(result2?.newEmail).toBe(`email2-${timestamp}@example.com`); 112 113 // Clean up 114 db.run("DELETE FROM users WHERE id = ?", [user.id]); 115 db.run("DELETE FROM email_change_tokens WHERE user_id = ?", [user.id]); 116});