···
1
-
import { describe, expect, test, beforeAll, afterAll, beforeEach } from "bun:test";
import db from "./db/schema";
import { hashPasswordClient } from "./lib/client-auth";
···
serverAvailable = response.ok || response.status === 404;
20
-
`\n⚠️ Test server not running on port ${TEST_PORT}. Start it with:\n PORT=${TEST_PORT} bun run src/index.ts\n Then run tests in another terminal.\n`
27
+
`\n⚠️ Test server not running on port ${TEST_PORT}. Start it with:\n PORT=${TEST_PORT} bun run src/index.ts\n Then run tests in another terminal.\n`,
···
// Helper to hash passwords like the client would
46
-
async function clientHashPassword(email: string, password: string): Promise<string> {
53
+
async function clientHashPassword(
56
+
): Promise<string> {
return await hashPasswordClient(password, email);
// Helper to extract session cookie
51
-
function extractSessionCookie(response: Response): string | null {
61
+
function extractSessionCookie(response: Response): string {
const setCookie = response.headers.get("set-cookie");
53
-
if (!setCookie) return null;
63
+
if (!setCookie) throw new Error("No set-cookie header found");
const match = setCookie.match(/session=([^;]+)/);
55
-
return match ? match[1] : null;
65
+
if (!match) throw new Error("No session cookie found in set-cookie header");
// Helper to make authenticated requests
···
function cleanupTestData() {
// Delete test users and their related data (cascade will handle most of it)
// Include 'newemail%' to catch users whose emails were updated during tests
77
-
db.run("DELETE FROM sessions WHERE user_id IN (SELECT id FROM users WHERE email LIKE 'test%' OR email LIKE 'admin@%' OR email LIKE 'newemail%')");
78
-
db.run("DELETE FROM passkeys WHERE user_id IN (SELECT id FROM users WHERE email LIKE 'test%' OR email LIKE 'admin@%' OR email LIKE 'newemail%')");
79
-
db.run("DELETE FROM transcriptions WHERE user_id IN (SELECT id FROM users WHERE email LIKE 'test%' OR email LIKE 'admin@%' OR email LIKE 'newemail%')");
80
-
db.run("DELETE FROM users WHERE email LIKE 'test%' OR email LIKE 'admin@%' OR email LIKE 'newemail%'");
89
+
"DELETE FROM sessions WHERE user_id IN (SELECT id FROM users WHERE email LIKE 'test%' OR email LIKE 'admin@%' OR email LIKE 'newemail%')",
92
+
"DELETE FROM passkeys WHERE user_id IN (SELECT id FROM users WHERE email LIKE 'test%' OR email LIKE 'admin@%' OR email LIKE 'newemail%')",
95
+
"DELETE FROM transcriptions WHERE user_id IN (SELECT id FROM users WHERE email LIKE 'test%' OR email LIKE 'admin@%' OR email LIKE 'newemail%')",
98
+
"DELETE FROM users WHERE email LIKE 'test%' OR email LIKE 'admin@%' OR email LIKE 'newemail%'",
// Clear ALL rate limit data to prevent accumulation across tests
// (IP-based rate limits don't contain test/admin in the key)
···
describe("API Endpoints - Authentication", () => {
describe("POST /api/auth/register", () => {
serverTest("should register a new user successfully", async () => {
113
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
132
+
const hashedPassword = await clientHashPassword(
134
+
TEST_USER.password,
const response = await fetch(`${BASE_URL}/api/auth/register`, {
···
expect(data.error).toBe("Email and password required");
146
-
serverTest("should reject registration with invalid password format", async () => {
147
-
const response = await fetch(`${BASE_URL}/api/auth/register`, {
149
-
headers: { "Content-Type": "application/json" },
150
-
body: JSON.stringify({
151
-
email: TEST_USER.email,
169
+
"should reject registration with invalid password format",
171
+
const response = await fetch(`${BASE_URL}/api/auth/register`, {
173
+
headers: { "Content-Type": "application/json" },
174
+
body: JSON.stringify({
175
+
email: TEST_USER.email,
156
-
expect(response.status).toBe(400);
157
-
const data = await response.json();
158
-
expect(data.error).toBe("Invalid password format");
180
+
expect(response.status).toBe(400);
181
+
const data = await response.json();
182
+
expect(data.error).toBe("Invalid password format");
serverTest("should reject duplicate email registration", async () => {
162
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
187
+
const hashedPassword = await clientHashPassword(
189
+
TEST_USER.password,
await fetch(`${BASE_URL}/api/auth/register`, {
···
serverTest("should enforce rate limiting on registration", async () => {
192
-
const hashedPassword = await clientHashPassword("test@example.com", "password");
220
+
const hashedPassword = await clientHashPassword(
221
+
"test@example.com",
// Make registration attempts until rate limit is hit (limit is 5 per hour)
let rateLimitHit = false;
···
describe("POST /api/auth/login", () => {
serverTest("should login successfully with valid credentials", async () => {
220
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
251
+
const hashedPassword = await clientHashPassword(
253
+
TEST_USER.password,
await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
serverTest("should reject login with invalid credentials", async () => {
250
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
284
+
const hashedPassword = await clientHashPassword(
286
+
TEST_USER.password,
await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
// Login with wrong password
261
-
const wrongPassword = await clientHashPassword(TEST_USER.email, "WrongPassword123!");
298
+
const wrongPassword = await clientHashPassword(
300
+
"WrongPassword123!",
const response = await fetch(`${BASE_URL}/api/auth/login`, {
headers: { "Content-Type": "application/json" },
···
serverTest("should enforce rate limiting on login attempts", async () => {
291
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
331
+
const hashedPassword = await clientHashPassword(
333
+
TEST_USER.password,
// Make 11 login attempts (limit is 10 per 15 minutes per IP)
let rateLimitHit = false;
···
describe("POST /api/auth/logout", () => {
serverTest("should logout successfully", async () => {
319
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
362
+
const hashedPassword = await clientHashPassword(
364
+
TEST_USER.password,
const loginResponse = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
password: hashedPassword,
328
-
const sessionCookie = extractSessionCookie(loginResponse)!;
374
+
const sessionCookie = extractSessionCookie(loginResponse);
331
-
const response = await authRequest(`${BASE_URL}/api/auth/logout`, sessionCookie, {
377
+
const response = await authRequest(
378
+
`${BASE_URL}/api/auth/logout`,
expect(response.status).toBe(200);
const data = await response.json();
···
describe("GET /api/auth/me", () => {
356
-
serverTest("should return current user info when authenticated", async () => {
358
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
359
-
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
361
-
headers: { "Content-Type": "application/json" },
362
-
body: JSON.stringify({
363
-
email: TEST_USER.email,
364
-
password: hashedPassword,
365
-
name: TEST_USER.name,
368
-
const sessionCookie = extractSessionCookie(registerResponse)!;
407
+
"should return current user info when authenticated",
410
+
const hashedPassword = await clientHashPassword(
412
+
TEST_USER.password,
414
+
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
416
+
headers: { "Content-Type": "application/json" },
417
+
body: JSON.stringify({
418
+
email: TEST_USER.email,
419
+
password: hashedPassword,
420
+
name: TEST_USER.name,
423
+
const sessionCookie = extractSessionCookie(registerResponse);
370
-
// Get current user
371
-
const response = await authRequest(`${BASE_URL}/api/auth/me`, sessionCookie);
425
+
// Get current user
426
+
const response = await authRequest(
427
+
`${BASE_URL}/api/auth/me`,
373
-
expect(response.status).toBe(200);
374
-
const data = await response.json();
375
-
expect(data.email).toBe(TEST_USER.email);
376
-
expect(data.name).toBe(TEST_USER.name);
377
-
expect(data.role).toBeDefined();
431
+
expect(response.status).toBe(200);
432
+
const data = await response.json();
433
+
expect(data.email).toBe(TEST_USER.email);
434
+
expect(data.name).toBe(TEST_USER.name);
435
+
expect(data.role).toBeDefined();
serverTest("should return 401 when not authenticated", async () => {
const response = await fetch(`${BASE_URL}/api/auth/me`);
···
serverTest("should return 401 with invalid session", async () => {
389
-
const response = await authRequest(`${BASE_URL}/api/auth/me`, "invalid-session");
448
+
const response = await authRequest(
449
+
`${BASE_URL}/api/auth/me`,
expect(response.status).toBe(401);
const data = await response.json();
···
describe("GET /api/sessions", () => {
serverTest("should return user sessions", async () => {
402
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
464
+
const hashedPassword = await clientHashPassword(
466
+
TEST_USER.password,
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
password: hashedPassword,
411
-
const sessionCookie = extractSessionCookie(registerResponse)!;
476
+
const sessionCookie = extractSessionCookie(registerResponse);
414
-
const response = await authRequest(`${BASE_URL}/api/sessions`, sessionCookie);
479
+
const response = await authRequest(
480
+
`${BASE_URL}/api/sessions`,
expect(response.status).toBe(200);
const data = await response.json();
···
describe("DELETE /api/sessions", () => {
serverTest("should delete specific session", async () => {
// Register user and create multiple sessions
435
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
503
+
const hashedPassword = await clientHashPassword(
505
+
TEST_USER.password,
const session1Response = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
password: hashedPassword,
444
-
const session1Cookie = extractSessionCookie(session1Response)!;
515
+
const session1Cookie = extractSessionCookie(session1Response);
const session2Response = await fetch(`${BASE_URL}/api/auth/login`, {
···
password: hashedPassword,
454
-
const session2Cookie = extractSessionCookie(session2Response)!;
525
+
const session2Cookie = extractSessionCookie(session2Response);
457
-
const sessionsResponse = await authRequest(`${BASE_URL}/api/sessions`, session1Cookie);
528
+
const sessionsResponse = await authRequest(
529
+
`${BASE_URL}/api/sessions`,
const sessionsData = await sessionsResponse.json();
const targetSessionId = sessionsData.sessions.find(
460
-
(s: any) => s.id === session2Cookie
534
+
(s: { id: string }) => s.id === session2Cookie,
464
-
const response = await authRequest(`${BASE_URL}/api/sessions`, session1Cookie, {
466
-
headers: { "Content-Type": "application/json" },
467
-
body: JSON.stringify({ sessionId: targetSessionId }),
538
+
const response = await authRequest(
539
+
`${BASE_URL}/api/sessions`,
543
+
headers: { "Content-Type": "application/json" },
544
+
body: JSON.stringify({ sessionId: targetSessionId }),
expect(response.status).toBe(200);
const data = await response.json();
expect(data.success).toBe(true);
// Verify session 2 is deleted
475
-
const verifyResponse = await authRequest(`${BASE_URL}/api/auth/me`, session2Cookie);
553
+
const verifyResponse = await authRequest(
554
+
`${BASE_URL}/api/auth/me`,
expect(verifyResponse.status).toBe(401);
serverTest("should not delete another user's session", async () => {
481
-
const hashedPassword1 = await clientHashPassword(TEST_USER.email, TEST_USER.password);
562
+
const hashedPassword1 = await clientHashPassword(
564
+
TEST_USER.password,
const user1Response = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
password: hashedPassword1,
490
-
const user1Cookie = extractSessionCookie(user1Response)!;
574
+
const user1Cookie = extractSessionCookie(user1Response);
492
-
const hashedPassword2 = await clientHashPassword(TEST_USER_2.email, TEST_USER_2.password);
576
+
const hashedPassword2 = await clientHashPassword(
578
+
TEST_USER_2.password,
const user2Response = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
password: hashedPassword2,
501
-
const user2Cookie = extractSessionCookie(user2Response)!;
588
+
const user2Cookie = extractSessionCookie(user2Response);
// Try to delete user2's session using user1's credentials
504
-
const response = await authRequest(`${BASE_URL}/api/sessions`, user1Cookie, {
506
-
headers: { "Content-Type": "application/json" },
507
-
body: JSON.stringify({ sessionId: user2Cookie }),
591
+
const response = await authRequest(
592
+
`${BASE_URL}/api/sessions`,
596
+
headers: { "Content-Type": "application/json" },
597
+
body: JSON.stringify({ sessionId: user2Cookie }),
expect(response.status).toBe(404);
···
describe("DELETE /api/user", () => {
serverTest("should delete user account", async () => {
519
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
610
+
const hashedPassword = await clientHashPassword(
612
+
TEST_USER.password,
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
password: hashedPassword,
528
-
const sessionCookie = extractSessionCookie(registerResponse)!;
622
+
const sessionCookie = extractSessionCookie(registerResponse);
531
-
const response = await authRequest(`${BASE_URL}/api/user`, sessionCookie, {
625
+
const response = await authRequest(
626
+
`${BASE_URL}/api/user`,
expect(response.status).toBe(200);
const data = await response.json();
expect(data.success).toBe(true);
// Verify user is deleted
540
-
const verifyResponse = await authRequest(`${BASE_URL}/api/auth/me`, sessionCookie);
638
+
const verifyResponse = await authRequest(
639
+
`${BASE_URL}/api/auth/me`,
expect(verifyResponse.status).toBe(401);
···
describe("PUT /api/user/email", () => {
serverTest("should update user email", async () => {
556
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
657
+
const hashedPassword = await clientHashPassword(
659
+
TEST_USER.password,
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
password: hashedPassword,
565
-
const sessionCookie = extractSessionCookie(registerResponse)!;
669
+
const sessionCookie = extractSessionCookie(registerResponse);
const newEmail = "newemail@example.com";
569
-
const response = await authRequest(`${BASE_URL}/api/user/email`, sessionCookie, {
571
-
headers: { "Content-Type": "application/json" },
572
-
body: JSON.stringify({ email: newEmail }),
673
+
const response = await authRequest(
674
+
`${BASE_URL}/api/user/email`,
678
+
headers: { "Content-Type": "application/json" },
679
+
body: JSON.stringify({ email: newEmail }),
expect(response.status).toBe(200);
const data = await response.json();
expect(data.success).toBe(true);
580
-
const meResponse = await authRequest(`${BASE_URL}/api/auth/me`, sessionCookie);
688
+
const meResponse = await authRequest(
689
+
`${BASE_URL}/api/auth/me`,
const meData = await meResponse.json();
expect(meData.email).toBe(newEmail);
serverTest("should reject duplicate email", async () => {
587
-
const hashedPassword1 = await clientHashPassword(TEST_USER.email, TEST_USER.password);
698
+
const hashedPassword1 = await clientHashPassword(
700
+
TEST_USER.password,
await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
597
-
const hashedPassword2 = await clientHashPassword(TEST_USER_2.email, TEST_USER_2.password);
711
+
const hashedPassword2 = await clientHashPassword(
713
+
TEST_USER_2.password,
const user2Response = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
password: hashedPassword2,
606
-
const user2Cookie = extractSessionCookie(user2Response)!;
723
+
const user2Cookie = extractSessionCookie(user2Response);
// Try to update user2's email to user1's email
609
-
const response = await authRequest(`${BASE_URL}/api/user/email`, user2Cookie, {
611
-
headers: { "Content-Type": "application/json" },
612
-
body: JSON.stringify({ email: TEST_USER.email }),
726
+
const response = await authRequest(
727
+
`${BASE_URL}/api/user/email`,
731
+
headers: { "Content-Type": "application/json" },
732
+
body: JSON.stringify({ email: TEST_USER.email }),
expect(response.status).toBe(400);
const data = await response.json();
···
describe("PUT /api/user/password", () => {
serverTest("should update user password", async () => {
624
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
745
+
const hashedPassword = await clientHashPassword(
747
+
TEST_USER.password,
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
password: hashedPassword,
633
-
const sessionCookie = extractSessionCookie(registerResponse)!;
757
+
const sessionCookie = extractSessionCookie(registerResponse);
636
-
const newPassword = await clientHashPassword(TEST_USER.email, "NewPassword123!");
637
-
const response = await authRequest(`${BASE_URL}/api/user/password`, sessionCookie, {
639
-
headers: { "Content-Type": "application/json" },
640
-
body: JSON.stringify({ password: newPassword }),
760
+
const newPassword = await clientHashPassword(
764
+
const response = await authRequest(
765
+
`${BASE_URL}/api/user/password`,
769
+
headers: { "Content-Type": "application/json" },
770
+
body: JSON.stringify({ password: newPassword }),
expect(response.status).toBe(200);
const data = await response.json();
···
serverTest("should reject invalid password format", async () => {
661
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
792
+
const hashedPassword = await clientHashPassword(
794
+
TEST_USER.password,
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
password: hashedPassword,
670
-
const sessionCookie = extractSessionCookie(registerResponse)!;
804
+
const sessionCookie = extractSessionCookie(registerResponse);
// Try to update with invalid format
673
-
const response = await authRequest(`${BASE_URL}/api/user/password`, sessionCookie, {
675
-
headers: { "Content-Type": "application/json" },
676
-
body: JSON.stringify({ password: "short" }),
807
+
const response = await authRequest(
808
+
`${BASE_URL}/api/user/password`,
812
+
headers: { "Content-Type": "application/json" },
813
+
body: JSON.stringify({ password: "short" }),
expect(response.status).toBe(400);
const data = await response.json();
···
describe("PUT /api/user/name", () => {
serverTest("should update user name", async () => {
688
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
826
+
const hashedPassword = await clientHashPassword(
828
+
TEST_USER.password,
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
698
-
const sessionCookie = extractSessionCookie(registerResponse)!;
839
+
const sessionCookie = extractSessionCookie(registerResponse);
const newName = "Updated Name";
702
-
const response = await authRequest(`${BASE_URL}/api/user/name`, sessionCookie, {
704
-
headers: { "Content-Type": "application/json" },
705
-
body: JSON.stringify({ name: newName }),
843
+
const response = await authRequest(
844
+
`${BASE_URL}/api/user/name`,
848
+
headers: { "Content-Type": "application/json" },
849
+
body: JSON.stringify({ name: newName }),
expect(response.status).toBe(200);
const data = await response.json();
expect(data.success).toBe(true);
713
-
const meResponse = await authRequest(`${BASE_URL}/api/auth/me`, sessionCookie);
858
+
const meResponse = await authRequest(
859
+
`${BASE_URL}/api/auth/me`,
const meData = await meResponse.json();
expect(meData.name).toBe(newName);
serverTest("should reject missing name", async () => {
720
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
868
+
const hashedPassword = await clientHashPassword(
870
+
TEST_USER.password,
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
password: hashedPassword,
729
-
const sessionCookie = extractSessionCookie(registerResponse)!;
880
+
const sessionCookie = extractSessionCookie(registerResponse);
731
-
const response = await authRequest(`${BASE_URL}/api/user/name`, sessionCookie, {
733
-
headers: { "Content-Type": "application/json" },
734
-
body: JSON.stringify({}),
882
+
const response = await authRequest(
883
+
`${BASE_URL}/api/user/name`,
887
+
headers: { "Content-Type": "application/json" },
888
+
body: JSON.stringify({}),
expect(response.status).toBe(400);
···
describe("PUT /api/user/avatar", () => {
serverTest("should update user avatar", async () => {
744
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
899
+
const hashedPassword = await clientHashPassword(
901
+
TEST_USER.password,
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
password: hashedPassword,
753
-
const sessionCookie = extractSessionCookie(registerResponse)!;
911
+
const sessionCookie = extractSessionCookie(registerResponse);
757
-
const response = await authRequest(`${BASE_URL}/api/user/avatar`, sessionCookie, {
759
-
headers: { "Content-Type": "application/json" },
760
-
body: JSON.stringify({ avatar: newAvatar }),
915
+
const response = await authRequest(
916
+
`${BASE_URL}/api/user/avatar`,
920
+
headers: { "Content-Type": "application/json" },
921
+
body: JSON.stringify({ avatar: newAvatar }),
expect(response.status).toBe(200);
const data = await response.json();
expect(data.success).toBe(true);
768
-
const meResponse = await authRequest(`${BASE_URL}/api/auth/me`, sessionCookie);
930
+
const meResponse = await authRequest(
931
+
`${BASE_URL}/api/auth/me`,
const meData = await meResponse.json();
expect(meData.avatar).toBe(newAvatar);
···
describe("API Endpoints - Transcriptions", () => {
describe("GET /api/transcriptions/health", () => {
777
-
serverTest("should return transcription service health status", async () => {
778
-
const response = await fetch(`${BASE_URL}/api/transcriptions/health`);
943
+
"should return transcription service health status",
945
+
const response = await fetch(`${BASE_URL}/api/transcriptions/health`);
780
-
expect(response.status).toBe(200);
781
-
const data = await response.json();
782
-
expect(data).toHaveProperty("available");
783
-
expect(typeof data.available).toBe("boolean");
947
+
expect(response.status).toBe(200);
948
+
const data = await response.json();
949
+
expect(data).toHaveProperty("available");
950
+
expect(typeof data.available).toBe("boolean");
describe("GET /api/transcriptions", () => {
serverTest("should return user transcriptions", async () => {
790
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
958
+
const hashedPassword = await clientHashPassword(
960
+
TEST_USER.password,
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
password: hashedPassword,
799
-
const sessionCookie = extractSessionCookie(registerResponse)!;
970
+
const sessionCookie = extractSessionCookie(registerResponse);
802
-
const response = await authRequest(`${BASE_URL}/api/transcriptions`, sessionCookie);
973
+
const response = await authRequest(
974
+
`${BASE_URL}/api/transcriptions`,
expect(response.status).toBe(200);
const data = await response.json();
···
describe("POST /api/transcriptions", () => {
serverTest("should upload audio file and start transcription", async () => {
820
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
994
+
const hashedPassword = await clientHashPassword(
996
+
TEST_USER.password,
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
password: hashedPassword,
829
-
const sessionCookie = extractSessionCookie(registerResponse)!;
1006
+
const sessionCookie = extractSessionCookie(registerResponse);
// Create a test audio file
const audioBlob = new Blob(["fake audio data"], { type: "audio/mp3" });
···
formData.append("class_name", "Test Class");
838
-
const response = await authRequest(`${BASE_URL}/api/transcriptions`, sessionCookie, {
1015
+
const response = await authRequest(
1016
+
`${BASE_URL}/api/transcriptions`,
expect(response.status).toBe(200);
const data = await response.json();
···
serverTest("should reject non-audio files", async () => {
851
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
1032
+
const hashedPassword = await clientHashPassword(
1034
+
TEST_USER.password,
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
password: hashedPassword,
860
-
const sessionCookie = extractSessionCookie(registerResponse)!;
1044
+
const sessionCookie = extractSessionCookie(registerResponse);
// Try to upload non-audio file
const textBlob = new Blob(["text file"], { type: "text/plain" });
const formData = new FormData();
formData.append("audio", textBlob, "test.txt");
867
-
const response = await authRequest(`${BASE_URL}/api/transcriptions`, sessionCookie, {
1051
+
const response = await authRequest(
1052
+
`${BASE_URL}/api/transcriptions`,
expect(response.status).toBe(400);
serverTest("should reject files exceeding size limit", async () => {
877
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
1065
+
const hashedPassword = await clientHashPassword(
1067
+
TEST_USER.password,
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
password: hashedPassword,
886
-
const sessionCookie = extractSessionCookie(registerResponse)!;
1077
+
const sessionCookie = extractSessionCookie(registerResponse);
// Create a file larger than 100MB (the actual limit)
889
-
const largeBlob = new Blob([new ArrayBuffer(101 * 1024 * 1024)], { type: "audio/mp3" });
1080
+
const largeBlob = new Blob([new ArrayBuffer(101 * 1024 * 1024)], {
1081
+
type: "audio/mp3",
const formData = new FormData();
formData.append("audio", largeBlob, "large.mp3");
893
-
const response = await authRequest(`${BASE_URL}/api/transcriptions`, sessionCookie, {
1086
+
const response = await authRequest(
1087
+
`${BASE_URL}/api/transcriptions`,
expect(response.status).toBe(400);
const data = await response.json();
···
if (!serverAvailable) return;
927
-
const adminHash = await clientHashPassword(TEST_ADMIN.email, TEST_ADMIN.password);
1124
+
const adminHash = await clientHashPassword(
1126
+
TEST_ADMIN.password,
const adminResponse = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
937
-
adminCookie = extractSessionCookie(adminResponse)!;
1137
+
adminCookie = extractSessionCookie(adminResponse);
// Manually set admin role in database
940
-
db.run("UPDATE users SET role = 'admin' WHERE email = ?", [TEST_ADMIN.email]);
1140
+
db.run("UPDATE users SET role = 'admin' WHERE email = ?", [
943
-
const userHash = await clientHashPassword(TEST_USER.email, TEST_USER.password);
1145
+
const userHash = await clientHashPassword(
1147
+
TEST_USER.password,
const userResponse = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
953
-
userCookie = extractSessionCookie(userResponse)!;
1158
+
userCookie = extractSessionCookie(userResponse);
956
-
const userIdResult = db.query<{ id: number }, [string]>(
957
-
"SELECT id FROM users WHERE email = ?"
958
-
).get(TEST_USER.email);
959
-
userId = userIdResult!.id;
1161
+
const userIdResult = db
1162
+
.query<{ id: number }, [string]>("SELECT id FROM users WHERE email = ?")
1163
+
.get(TEST_USER.email);
1164
+
userId = userIdResult?.id;
describe("GET /api/admin/users", () => {
serverTest("should return all users for admin", async () => {
964
-
const response = await authRequest(`${BASE_URL}/api/admin/users`, adminCookie);
1169
+
const response = await authRequest(
1170
+
`${BASE_URL}/api/admin/users`,
expect(response.status).toBe(200);
const data = await response.json();
···
serverTest("should reject non-admin users", async () => {
973
-
const response = await authRequest(`${BASE_URL}/api/admin/users`, userCookie);
1181
+
const response = await authRequest(
1182
+
`${BASE_URL}/api/admin/users`,
expect(response.status).toBe(403);
···
describe("GET /api/admin/transcriptions", () => {
serverTest("should return all transcriptions for admin", async () => {
987
-
const response = await authRequest(`${BASE_URL}/api/admin/transcriptions`, adminCookie);
1198
+
const response = await authRequest(
1199
+
`${BASE_URL}/api/admin/transcriptions`,
expect(response.status).toBe(200);
const data = await response.json();
···
serverTest("should reject non-admin users", async () => {
995
-
const response = await authRequest(`${BASE_URL}/api/admin/transcriptions`, userCookie);
1209
+
const response = await authRequest(
1210
+
`${BASE_URL}/api/admin/transcriptions`,
expect(response.status).toBe(403);
···
expect(response.status).toBe(200);
···
expect(data.success).toBe(true);
// Verify user is deleted
1016
-
const verifyResponse = await authRequest(`${BASE_URL}/api/auth/me`, userCookie);
1233
+
const verifyResponse = await authRequest(
1234
+
`${BASE_URL}/api/auth/me`,
expect(verifyResponse.status).toBe(401);
···
expect(response.status).toBe(403);
···
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ role: "admin" }),
expect(response.status).toBe(200);
···
expect(data.success).toBe(true);
1050
-
const meResponse = await authRequest(`${BASE_URL}/api/auth/me`, userCookie);
1270
+
const meResponse = await authRequest(
1271
+
`${BASE_URL}/api/auth/me`,
const meData = await meResponse.json();
expect(meData.role).toBe("admin");
···
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ role: "superadmin" }),
expect(response.status).toBe(400);
···
serverTest("should return user details for admin", async () => {
const response = await authRequest(
`${BASE_URL}/api/admin/users/${userId}/details`,
expect(response.status).toBe(200);
···
serverTest("should reject non-admin users", async () => {
const response = await authRequest(
`${BASE_URL}/api/admin/users/${userId}/details`,
expect(response.status).toBe(403);
···
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: newName }),
expect(response.status).toBe(200);
···
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "" }),
expect(response.status).toBe(400);
···
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: newEmail }),
expect(response.status).toBe(200);
···
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: TEST_ADMIN.email }),
expect(response.status).toBe(400);
···
serverTest("should return user sessions as admin", async () => {
const response = await authRequest(
`${BASE_URL}/api/admin/users/${userId}/sessions`,
expect(response.status).toBe(200);
···
expect(response.status).toBe(200);
···
expect(data.success).toBe(true);
// Verify sessions are deleted
1191
-
const verifyResponse = await authRequest(`${BASE_URL}/api/auth/me`, userCookie);
1414
+
const verifyResponse = await authRequest(
1415
+
`${BASE_URL}/api/auth/me`,
expect(verifyResponse.status).toBe(401);
···
if (!serverAvailable) return;
1204
-
const hashedPassword = await clientHashPassword(TEST_USER.email, TEST_USER.password);
1430
+
const hashedPassword = await clientHashPassword(
1432
+
TEST_USER.password,
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
···
password: hashedPassword,
1213
-
sessionCookie = extractSessionCookie(registerResponse)!;
1442
+
sessionCookie = extractSessionCookie(registerResponse);
describe("GET /api/passkeys", () => {
serverTest("should return user passkeys", async () => {
1218
-
const response = await authRequest(`${BASE_URL}/api/passkeys`, sessionCookie);
1447
+
const response = await authRequest(
1448
+
`${BASE_URL}/api/passkeys`,
expect(response.status).toBe(200);
const data = await response.json();
···
describe("POST /api/passkeys/register/options", () => {
1234
-
serverTest("should return registration options for authenticated user", async () => {
1235
-
const response = await authRequest(
1467
+
"should return registration options for authenticated user",
1469
+
const response = await authRequest(
1470
+
`${BASE_URL}/api/passkeys/register/options`,
1477
+
expect(response.status).toBe(200);
1478
+
const data = await response.json();
1479
+
expect(data).toHaveProperty("challenge");
1480
+
expect(data).toHaveProperty("rp");
1481
+
expect(data).toHaveProperty("user");
1485
+
serverTest("should require authentication", async () => {
1486
+
const response = await fetch(
`${BASE_URL}/api/passkeys/register/options`,
1243
-
expect(response.status).toBe(200);
1244
-
const data = await response.json();
1245
-
expect(data).toHaveProperty("challenge");
1246
-
expect(data).toHaveProperty("rp");
1247
-
expect(data).toHaveProperty("user");
1250
-
serverTest("should require authentication", async () => {
1251
-
const response = await fetch(`${BASE_URL}/api/passkeys/register/options`, {
expect(response.status).toBe(401);
describe("POST /api/passkeys/authenticate/options", () => {
serverTest("should return authentication options for email", async () => {
1261
-
const response = await fetch(`${BASE_URL}/api/passkeys/authenticate/options`, {
1263
-
headers: { "Content-Type": "application/json" },
1264
-
body: JSON.stringify({ email: TEST_USER.email }),
1499
+
const response = await fetch(
1500
+
`${BASE_URL}/api/passkeys/authenticate/options`,
1503
+
headers: { "Content-Type": "application/json" },
1504
+
body: JSON.stringify({ email: TEST_USER.email }),
expect(response.status).toBe(200);
const data = await response.json();
···
serverTest("should handle non-existent email", async () => {
1273
-
const response = await fetch(`${BASE_URL}/api/passkeys/authenticate/options`, {
1275
-
headers: { "Content-Type": "application/json" },
1276
-
body: JSON.stringify({ email: "nonexistent@example.com" }),
1514
+
const response = await fetch(
1515
+
`${BASE_URL}/api/passkeys/authenticate/options`,
1518
+
headers: { "Content-Type": "application/json" },
1519
+
body: JSON.stringify({ email: "nonexistent@example.com" }),
// Should still return options for privacy (don't leak user existence)
expect([200, 404]).toContain(response.status);