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