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