···
1
-
import { Database } from "bun:sqlite";
import { afterEach, beforeEach, expect, test } from "bun:test";
3
-
import { unlinkSync } from "node:fs";
2
+
import db from "../db/schema";
11
+
removeUserFromClass,
13
-
const TEST_DB = "test-classes.db";
14
+
// Track created resources for cleanup
15
+
let createdUserIds: number[] = [];
16
+
let createdClassIds: string[] = [];
17
-
db = new Database(TEST_DB);
19
-
// Create minimal schema for testing
21
-
CREATE TABLE users (
22
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
23
-
email TEXT UNIQUE NOT NULL,
25
-
role TEXT NOT NULL DEFAULT 'user',
26
-
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
29
-
CREATE TABLE classes (
30
-
id TEXT PRIMARY KEY,
31
-
course_code 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'))
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
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
57
-
CREATE TABLE transcriptions (
58
-
id TEXT PRIMARY KEY,
59
-
user_id INTEGER NOT NULL,
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
19
+
createdUserIds = [];
20
+
createdClassIds = [];
78
-
unlinkSync(TEST_DB);
80
-
// File may not exist
24
+
// Clean up classes (cascades to members and meeting times)
25
+
for (const classId of createdClassIds) {
27
+
deleteClass(classId);
29
+
// May already be deleted
34
+
for (const userId of createdUserIds) {
36
+
db.run("DELETE FROM users WHERE id = ?", [userId]);
38
+
// May already be deleted
43
+
function createTestUser(email: string): number {
44
+
db.run("INSERT INTO users (email, password_hash) VALUES (?, ?)", [
49
+
.query<{ id: number }, []>("SELECT last_insert_rowid() as id")
51
+
if (!userId) throw new Error("Failed to create user");
52
+
createdUserIds.push(userId);
56
+
function createTestClass(data: {
57
+
course_code: string;
63
+
const cls = createClass(data);
64
+
createdClassIds.push(cls.id);
test("creates a class with all required fields", () => {
85
-
const cls = createClass({
69
+
const cls = createTestClass({
···
test("enrolls user in class", () => {
104
-
db.run("INSERT INTO users (email, password_hash) VALUES (?, ?)", [
105
-
"test@example.com",
109
-
.query<{ id: number }, []>("SELECT last_insert_rowid() as id")
111
-
if (!userId) throw new Error("Failed to create user");
87
+
const userId = createTestUser("test@example.com");
114
-
const cls = createClass({
89
+
const cls = createTestClass({
···
const isEnrolled = isUserEnrolledInClass(userId, cls.id);
expect(isEnrolled).toBe(true);
104
+
// Cleanup enrollment
105
+
removeUserFromClass(userId, cls.id);
test("gets classes for enrolled user", () => {
132
-
db.run("INSERT INTO users (email, password_hash) VALUES (?, ?)", [
133
-
"test@example.com",
137
-
.query<{ id: number }, []>("SELECT last_insert_rowid() as id")
139
-
if (!userId) throw new Error("Failed to create user");
109
+
const userId = createTestUser("test@example.com");
142
-
const cls1 = createClass({
112
+
const cls1 = createTestClass({
···
150
-
const _cls2 = createClass({
120
+
const cls2 = createTestClass({
···
// Enroll user in only one class
enrollUserInClass(userId, cls1.id);
161
-
// Get classes for user
131
+
// Get classes for user (non-admin)
const classes = getClassesForUser(userId, false);
expect(classes.length).toBe(1);
expect(classes[0]?.id).toBe(cls1.id);
166
-
// Admin should see all
136
+
// Admin should see all classes (not just the 2 test classes, but all in DB)
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);
142
+
// Cleanup enrollment
143
+
removeUserFromClass(userId, cls1.id);
test("creates and retrieves meeting times", () => {
172
-
const cls = createClass({
147
+
const cls = createTestClass({
···
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");
const meetings = getMeetingTimesForClass(cls.id);
expect(meetings.length).toBe(2);