🪻 distributed transcription service thistle.dunkirk.sh
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

test: fix classes tests to use production db with cleanup

The classes tests were creating a separate test database file but the
functions being tested still used the production database, causing tests
to fail when production data existed.

Changed to match the pattern used in other tests (auth.test.ts, etc):
- Use production database
- Track created resources in beforeEach/afterEach
- Clean up all test data after each test
- Helper functions to create and track test users/classes

💖 Generated with Crush

Co-Authored-By: Crush <crush@charm.land>

+69 -94
+69 -94
src/lib/classes.test.ts
··· 1 - import { Database } from "bun:sqlite"; 2 1 import { afterEach, beforeEach, expect, test } from "bun:test"; 3 - import { unlinkSync } from "node:fs"; 2 + import db from "../db/schema"; 4 3 import { 5 4 createClass, 6 5 createMeetingTime, 6 + deleteClass, 7 7 enrollUserInClass, 8 8 getClassesForUser, 9 9 getMeetingTimesForClass, 10 10 isUserEnrolledInClass, 11 + removeUserFromClass, 11 12 } from "./classes"; 12 13 13 - const TEST_DB = "test-classes.db"; 14 - let db: Database; 14 + // Track created resources for cleanup 15 + let createdUserIds: number[] = []; 16 + let createdClassIds: string[] = []; 15 17 16 18 beforeEach(() => { 17 - db = new Database(TEST_DB); 18 - 19 - // Create minimal schema for testing 20 - db.run(` 21 - CREATE TABLE users ( 22 - id INTEGER PRIMARY KEY AUTOINCREMENT, 23 - email TEXT UNIQUE NOT NULL, 24 - password_hash TEXT, 25 - role TEXT NOT NULL DEFAULT 'user', 26 - created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) 27 - ); 28 - 29 - CREATE TABLE classes ( 30 - id TEXT PRIMARY KEY, 31 - course_code TEXT NOT NULL, 32 - name TEXT NOT NULL, 33 - professor TEXT NOT NULL, 34 - semester TEXT NOT NULL, 35 - year INTEGER NOT NULL, 36 - archived BOOLEAN DEFAULT 0, 37 - created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) 38 - ); 39 - 40 - CREATE TABLE class_members ( 41 - class_id TEXT NOT NULL, 42 - user_id INTEGER NOT NULL, 43 - enrolled_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), 44 - PRIMARY KEY (class_id, user_id), 45 - FOREIGN KEY (class_id) REFERENCES classes(id) ON DELETE CASCADE, 46 - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE 47 - ); 48 - 49 - CREATE TABLE meeting_times ( 50 - id TEXT PRIMARY KEY, 51 - class_id TEXT NOT NULL, 52 - label TEXT NOT NULL, 53 - created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), 54 - FOREIGN KEY (class_id) REFERENCES classes(id) ON DELETE CASCADE 55 - ); 56 - 57 - CREATE TABLE transcriptions ( 58 - id TEXT PRIMARY KEY, 59 - user_id INTEGER NOT NULL, 60 - class_id TEXT, 61 - meeting_time_id TEXT, 62 - filename TEXT NOT NULL, 63 - original_filename TEXT NOT NULL, 64 - status TEXT NOT NULL DEFAULT 'pending', 65 - progress INTEGER NOT NULL DEFAULT 0, 66 - created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), 67 - updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), 68 - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, 69 - FOREIGN KEY (class_id) REFERENCES classes(id) ON DELETE CASCADE, 70 - FOREIGN KEY (meeting_time_id) REFERENCES meeting_times(id) ON DELETE SET NULL 71 - ); 72 - `); 19 + createdUserIds = []; 20 + createdClassIds = []; 73 21 }); 74 22 75 23 afterEach(() => { 76 - db.close(); 77 - try { 78 - unlinkSync(TEST_DB); 79 - } catch { 80 - // File may not exist 24 + // Clean up classes (cascades to members and meeting times) 25 + for (const classId of createdClassIds) { 26 + try { 27 + deleteClass(classId); 28 + } catch { 29 + // May already be deleted 30 + } 31 + } 32 + 33 + // Clean up users 34 + for (const userId of createdUserIds) { 35 + try { 36 + db.run("DELETE FROM users WHERE id = ?", [userId]); 37 + } catch { 38 + // May already be deleted 39 + } 81 40 } 82 41 }); 83 42 43 + function createTestUser(email: string): number { 44 + db.run("INSERT INTO users (email, password_hash) VALUES (?, ?)", [ 45 + email, 46 + "hash", 47 + ]); 48 + const userId = db 49 + .query<{ id: number }, []>("SELECT last_insert_rowid() as id") 50 + .get()?.id; 51 + if (!userId) throw new Error("Failed to create user"); 52 + createdUserIds.push(userId); 53 + return userId; 54 + } 55 + 56 + function createTestClass(data: { 57 + course_code: string; 58 + name: string; 59 + professor: string; 60 + semester: string; 61 + year: number; 62 + }) { 63 + const cls = createClass(data); 64 + createdClassIds.push(cls.id); 65 + return cls; 66 + } 67 + 84 68 test("creates a class with all required fields", () => { 85 - const cls = createClass({ 69 + const cls = createTestClass({ 86 70 course_code: "CS 101", 87 71 name: "Intro to CS", 88 72 professor: "Dr. Smith", ··· 100 84 }); 101 85 102 86 test("enrolls user in class", () => { 103 - // Create user 104 - db.run("INSERT INTO users (email, password_hash) VALUES (?, ?)", [ 105 - "test@example.com", 106 - "hash", 107 - ]); 108 - const userId = db 109 - .query<{ id: number }, []>("SELECT last_insert_rowid() as id") 110 - .get()?.id; 111 - if (!userId) throw new Error("Failed to create user"); 87 + const userId = createTestUser("test@example.com"); 112 88 113 - // Create class 114 - const cls = createClass({ 89 + const cls = createTestClass({ 115 90 course_code: "CS 101", 116 91 name: "Intro to CS", 117 92 professor: "Dr. Smith", ··· 125 100 // Verify enrollment 126 101 const isEnrolled = isUserEnrolledInClass(userId, cls.id); 127 102 expect(isEnrolled).toBe(true); 103 + 104 + // Cleanup enrollment 105 + removeUserFromClass(userId, cls.id); 128 106 }); 129 107 130 108 test("gets classes for enrolled user", () => { 131 - // Create user 132 - db.run("INSERT INTO users (email, password_hash) VALUES (?, ?)", [ 133 - "test@example.com", 134 - "hash", 135 - ]); 136 - const userId = db 137 - .query<{ id: number }, []>("SELECT last_insert_rowid() as id") 138 - .get()?.id; 139 - if (!userId) throw new Error("Failed to create user"); 109 + const userId = createTestUser("test@example.com"); 140 110 141 111 // Create two classes 142 - const cls1 = createClass({ 112 + const cls1 = createTestClass({ 143 113 course_code: "CS 101", 144 114 name: "Intro to CS", 145 115 professor: "Dr. Smith", ··· 147 117 year: 2024, 148 118 }); 149 119 150 - const _cls2 = createClass({ 120 + const cls2 = createTestClass({ 151 121 course_code: "CS 102", 152 122 name: "Data Structures", 153 123 professor: "Dr. Jones", ··· 158 128 // Enroll user in only one class 159 129 enrollUserInClass(userId, cls1.id); 160 130 161 - // Get classes for user 131 + // Get classes for user (non-admin) 162 132 const classes = getClassesForUser(userId, false); 163 133 expect(classes.length).toBe(1); 164 134 expect(classes[0]?.id).toBe(cls1.id); 165 135 166 - // Admin should see all 136 + // Admin should see all classes (not just the 2 test classes, but all in DB) 167 137 const allClasses = getClassesForUser(userId, true); 168 - expect(allClasses.length).toBe(2); 138 + expect(allClasses.length).toBeGreaterThanOrEqual(2); 139 + expect(allClasses.some((c) => c.id === cls1.id)).toBe(true); 140 + expect(allClasses.some((c) => c.id === cls2.id)).toBe(true); 141 + 142 + // Cleanup enrollment 143 + removeUserFromClass(userId, cls1.id); 169 144 }); 170 145 171 146 test("creates and retrieves meeting times", () => { 172 - const cls = createClass({ 147 + const cls = createTestClass({ 173 148 course_code: "CS 101", 174 149 name: "Intro to CS", 175 150 professor: "Dr. Smith", ··· 177 152 year: 2024, 178 153 }); 179 154 180 - const _meeting1 = createMeetingTime(cls.id, "Monday Lecture"); 181 - const _meeting2 = createMeetingTime(cls.id, "Wednesday Lab"); 155 + createMeetingTime(cls.id, "Monday Lecture"); 156 + createMeetingTime(cls.id, "Wednesday Lab"); 182 157 183 158 const meetings = getMeetingTimesForClass(cls.id); 184 159 expect(meetings.length).toBe(2);