···
console.log("✓ Test server stopped and test database cleaned up");
email: "test@example.com",
···
// All tests run against a fresh database, no cleanup needed
describe("API Endpoints - Authentication", () => {
···
expect(response.status).toBe(200);
-
// Extract session before consuming response body
-
const sessionCookie = extractSessionCookie(response);
const data = await response.json();
expect(data.user).toBeDefined();
expect(data.user.email).toBe(TEST_USER.email);
-
expect(sessionCookie).toBeTruthy();
test("should reject registration with missing email", async () => {
···
test("should enforce rate limiting on registration", async () => {
const hashedPassword = await clientHashPassword(
-
// Make registration attempts until rate limit is hit (limit is 5 per hour)
-
let rateLimitHit = false;
-
for (let i = 0; i < 10; i++) {
-
const response = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: `test${i}@example.com`,
-
password: hashedPassword,
-
if (response.status === 429) {
-
// Verify that rate limiting was triggered
-
expect(rateLimitHit).toBe(true);
-
describe("POST /api/auth/login", () => {
-
test("should login successfully with valid credentials", async () => {
-
const hashedPassword = await clientHashPassword(
await fetch(`${BASE_URL}/api/auth/register`, {
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
password: hashedPassword,
-
const response = await fetch(`${BASE_URL}/api/auth/login`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
expect(response.status).toBe(200);
-
const data = await response.json();
-
expect(data.user).toBeDefined();
-
expect(data.user.email).toBe(TEST_USER.email);
-
expect(extractSessionCookie(response)).toBeTruthy();
-
test("should reject login with invalid credentials", async () => {
-
const hashedPassword = await clientHashPassword(
-
await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
// Login with wrong password
-
const wrongPassword = await clientHashPassword(
-
const response = await fetch(`${BASE_URL}/api/auth/login`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: wrongPassword,
-
expect(response.status).toBe(401);
-
const data = await response.json();
-
expect(data.error).toBe("Invalid email or password");
-
test("should reject login with missing fields", async () => {
-
const response = await fetch(`${BASE_URL}/api/auth/login`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
expect(response.status).toBe(400);
-
const data = await response.json();
-
expect(data.error).toBe("Email and password required");
-
test("should enforce rate limiting on login attempts", async () => {
-
const hashedPassword = await clientHashPassword(
-
// Make 11 login attempts (limit is 10 per 15 minutes per IP)
let rateLimitHit = false;
-
for (let i = 0; i < 11; i++) {
-
const response = await fetch(`${BASE_URL}/api/auth/login`, {
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
password: hashedPassword,
···
-
describe("POST /api/auth/logout", () => {
-
test("should logout successfully", async () => {
-
const hashedPassword = await clientHashPassword(
-
const loginResponse = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
const sessionCookie = extractSessionCookie(loginResponse);
-
const response = await authRequest(
-
`${BASE_URL}/api/auth/logout`,
-
expect(response.status).toBe(200);
-
const data = await response.json();
-
expect(data.success).toBe(true);
-
// Verify cookie is cleared
-
const setCookie = response.headers.get("set-cookie");
-
expect(setCookie).toContain("Max-Age=0");
-
test("should logout even without valid session", async () => {
-
const response = await fetch(`${BASE_URL}/api/auth/logout`, {
-
expect(response.status).toBe(200);
-
const data = await response.json();
-
expect(data.success).toBe(true);
-
describe("GET /api/auth/me", () => {
-
"should return current user info when authenticated",
-
const hashedPassword = await clientHashPassword(
-
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
const sessionCookie = extractSessionCookie(registerResponse);
-
const response = await authRequest(
-
`${BASE_URL}/api/auth/me`,
-
expect(response.status).toBe(200);
-
const data = await response.json();
-
expect(data.email).toBe(TEST_USER.email);
-
expect(data.name).toBe(TEST_USER.name);
-
expect(data.role).toBeDefined();
-
test("should return 401 when not authenticated", async () => {
-
const response = await fetch(`${BASE_URL}/api/auth/me`);
-
expect(response.status).toBe(401);
-
const data = await response.json();
-
expect(data.error).toBe("Not authenticated");
-
test("should return 401 with invalid session", async () => {
-
const response = await authRequest(
-
`${BASE_URL}/api/auth/me`,
-
expect(response.status).toBe(401);
-
const data = await response.json();
-
expect(data.error).toBe("Invalid session");
-
describe("API Endpoints - Session Management", () => {
-
describe("GET /api/sessions", () => {
-
test("should return user sessions", async () => {
-
const hashedPassword = await clientHashPassword(
-
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
const sessionCookie = extractSessionCookie(registerResponse);
-
const response = await authRequest(
-
`${BASE_URL}/api/sessions`,
-
expect(response.status).toBe(200);
-
const data = await response.json();
-
expect(data.sessions).toBeDefined();
-
expect(data.sessions.length).toBeGreaterThan(0);
-
expect(data.sessions[0]).toHaveProperty("id");
-
expect(data.sessions[0]).toHaveProperty("ip_address");
-
expect(data.sessions[0]).toHaveProperty("user_agent");
-
test("should require authentication", async () => {
-
const response = await fetch(`${BASE_URL}/api/sessions`);
-
expect(response.status).toBe(401);
-
describe("DELETE /api/sessions", () => {
-
test("should delete specific session", async () => {
-
// Register user and create multiple sessions
-
const hashedPassword = await clientHashPassword(
-
const session1Response = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
const session1Cookie = extractSessionCookie(session1Response);
-
const session2Response = await fetch(`${BASE_URL}/api/auth/login`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
const session2Cookie = extractSessionCookie(session2Response);
-
const sessionsResponse = await authRequest(
-
`${BASE_URL}/api/sessions`,
-
const sessionsData = await sessionsResponse.json();
-
const targetSessionId = sessionsData.sessions.find(
-
(s: { id: string }) => s.id === session2Cookie,
-
const response = await authRequest(
-
`${BASE_URL}/api/sessions`,
-
headers: { "Content-Type": "application/json" },
-
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
-
const verifyResponse = await authRequest(
-
`${BASE_URL}/api/auth/me`,
-
expect(verifyResponse.status).toBe(401);
-
test("should not delete another user's session", async () => {
-
const hashedPassword1 = await clientHashPassword(
-
const user1Response = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword1,
-
const user1Cookie = extractSessionCookie(user1Response);
-
const hashedPassword2 = await clientHashPassword(
-
const user2Response = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER_2.email,
-
password: hashedPassword2,
-
const user2Cookie = extractSessionCookie(user2Response);
-
// Try to delete user2's session using user1's credentials
-
const response = await authRequest(
-
`${BASE_URL}/api/sessions`,
-
headers: { "Content-Type": "application/json" },
-
body: JSON.stringify({ sessionId: user2Cookie }),
-
expect(response.status).toBe(404);
-
test("should not delete current session", async () => {
-
const hashedPassword = await clientHashPassword(
-
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
const sessionCookie = extractSessionCookie(registerResponse);
// Try to delete own current session
const response = await authRequest(
···
describe("API Endpoints - User Management", () => {
describe("DELETE /api/user", () => {
test("should delete user account", async () => {
-
const hashedPassword = await clientHashPassword(
-
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
const sessionCookie = extractSessionCookie(registerResponse);
const response = await authRequest(
···
describe("PUT /api/user/email", () => {
test("should update user email", async () => {
-
const hashedPassword = await clientHashPassword(
-
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
const sessionCookie = extractSessionCookie(registerResponse);
const newEmail = "newemail@example.com";
const response = await authRequest(
`${BASE_URL}/api/user/email`,
···
const data = await response.json();
expect(data.success).toBe(true);
const meResponse = await authRequest(
`${BASE_URL}/api/auth/me`,
···
test("should reject duplicate email", async () => {
-
const hashedPassword1 = await clientHashPassword(
-
await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword1,
-
const hashedPassword2 = await clientHashPassword(
-
const user2Response = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER_2.email,
-
password: hashedPassword2,
-
const user2Cookie = extractSessionCookie(user2Response);
// Try to update user2's email to user1's email
const response = await authRequest(
···
describe("PUT /api/user/password", () => {
test("should update user password", async () => {
-
const hashedPassword = await clientHashPassword(
-
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
const sessionCookie = extractSessionCookie(registerResponse);
const newPassword = await clientHashPassword(
···
test("should reject invalid password format", async () => {
-
const hashedPassword = await clientHashPassword(
-
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
const sessionCookie = extractSessionCookie(registerResponse);
// Try to update with invalid format
const response = await authRequest(
···
describe("PUT /api/user/name", () => {
test("should update user name", async () => {
-
const hashedPassword = await clientHashPassword(
-
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
const sessionCookie = extractSessionCookie(registerResponse);
const newName = "Updated Name";
···
test("should reject missing name", async () => {
-
const hashedPassword = await clientHashPassword(
-
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
const sessionCookie = extractSessionCookie(registerResponse);
const response = await authRequest(
`${BASE_URL}/api/user/name`,
···
describe("PUT /api/user/avatar", () => {
test("should update user avatar", async () => {
-
const hashedPassword = await clientHashPassword(
-
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
const sessionCookie = extractSessionCookie(registerResponse);
···
describe("API Endpoints - Transcriptions", () => {
describe("GET /api/transcriptions", () => {
test("should return user transcriptions", async () => {
-
const hashedPassword = await clientHashPassword(
-
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
const sessionCookie = extractSessionCookie(registerResponse);
const response = await authRequest(
···
describe("POST /api/transcriptions", () => {
test("should upload audio file and start transcription", async () => {
-
const hashedPassword = await clientHashPassword(
-
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
const sessionCookie = extractSessionCookie(registerResponse);
// Create a test audio file
const audioBlob = new Blob(["fake audio data"], { type: "audio/mp3" });
···
test("should reject non-audio files", async () => {
-
const hashedPassword = await clientHashPassword(
-
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
const sessionCookie = extractSessionCookie(registerResponse);
// Try to upload non-audio file
const textBlob = new Blob(["text file"], { type: "text/plain" });
···
test("should reject files exceeding size limit", async () => {
-
const hashedPassword = await clientHashPassword(
-
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
const sessionCookie = extractSessionCookie(registerResponse);
// Create a file larger than 100MB (the actual limit)
const largeBlob = new Blob([new ArrayBuffer(101 * 1024 * 1024)], {
···
-
const adminHash = await clientHashPassword(
-
const adminResponse = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_ADMIN.email,
-
adminCookie = extractSessionCookie(adminResponse);
// Manually set admin role in database
db.run("UPDATE users SET role = 'admin' WHERE email = ?", [
-
const userHash = await clientHashPassword(
-
const userResponse = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
userCookie = extractSessionCookie(userResponse);
.query<{ id: number }, [string]>("SELECT id FROM users WHERE email = ?")
userId = userIdResult?.id;
describe("GET /api/admin/users", () => {
···
let sessionCookie: string;
-
const hashedPassword = await clientHashPassword(
-
const registerResponse = await fetch(`${BASE_URL}/api/auth/register`, {
-
headers: { "Content-Type": "application/json" },
-
email: TEST_USER.email,
-
password: hashedPassword,
-
sessionCookie = extractSessionCookie(registerResponse);
describe("GET /api/passkeys", () => {