Skip to content

Commit c8a81ce

Browse files
committed
Merge branch 'main' into release/1.6
2 parents 4b84cd3 + 6d492fa commit c8a81ce

13 files changed

+2673
-0
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { analyzeQuery } from "../src/utils/analyzeQuery";
2+
import { describe, test, expect, jest } from "@jest/globals";
3+
import {
4+
NAMESPACE_KEYWORDS,
5+
ASSISTANT_TERMS,
6+
DEPARTMENT_CODES,
7+
} from "../src/constants/promptKeywords";
8+
9+
// Mock the constants if needed
10+
jest.mock("../src/constants/promptKeywords", () => ({
11+
NAMESPACE_KEYWORDS: {
12+
courses_v3: ["course", "class", "description"],
13+
offerings: ["offering", "schedule", "timetable"],
14+
prerequisites: ["prerequisite", "prereq"],
15+
corequisites: ["corequisite", "coreq"],
16+
departments: ["department", "faculty"],
17+
programs: ["program", "major", "minor"],
18+
},
19+
ASSISTANT_TERMS: ["you", "your", "morpheus", "assistant"],
20+
DEPARTMENT_CODES: ["cs", "math", "eng"],
21+
GENERAL_ACADEMIC_TERMS: ["academic", "study", "education"],
22+
}));
23+
24+
describe("analyzeQuery", () => {
25+
test("should return no search required for assistant-related queries", () => {
26+
const result = analyzeQuery("Can you help me with something?");
27+
expect(result).toEqual({
28+
requiresSearch: false,
29+
relevantNamespaces: [],
30+
});
31+
});
32+
33+
test("should detect course-related keywords and return appropriate namespaces", () => {
34+
const result = analyzeQuery("Tell me about this course");
35+
expect(result.requiresSearch).toBe(true);
36+
expect(result.relevantNamespaces).toContain("courses_v3");
37+
});
38+
39+
test("should detect course codes and include relevant namespaces", () => {
40+
const result = analyzeQuery("What is CSC108 about?");
41+
expect(result.requiresSearch).toBe(true);
42+
expect(result.relevantNamespaces).toContain("courses_v3");
43+
expect(result.relevantNamespaces).toContain("offerings");
44+
expect(result.relevantNamespaces).toContain("prerequisites");
45+
});
46+
47+
test("should detect department codes and include relevant namespaces", () => {
48+
const result = analyzeQuery("What math courses are available?");
49+
expect(result.requiresSearch).toBe(true);
50+
expect(result.relevantNamespaces).toContain("departments");
51+
expect(result.relevantNamespaces).toContain("courses_v3");
52+
});
53+
54+
test("should detect offering-related keywords", () => {
55+
const result = analyzeQuery("What is the schedule for winter semester?");
56+
expect(result.requiresSearch).toBe(true);
57+
expect(result.relevantNamespaces).toContain("offerings");
58+
});
59+
60+
test("should detect prerequisite-related keywords", () => {
61+
const result = analyzeQuery("What are the prerequisites for this class?");
62+
expect(result.requiresSearch).toBe(true);
63+
expect(result.relevantNamespaces).toContain("prerequisites");
64+
});
65+
66+
test("should detect corequisite-related keywords", () => {
67+
const result = analyzeQuery("Are there any corequisites for this course?");
68+
expect(result.requiresSearch).toBe(true);
69+
expect(result.relevantNamespaces).toContain("corequisites");
70+
});
71+
72+
test("should return all namespaces when search is required but no specific namespaces identified", () => {
73+
// Assuming GENERAL_ACADEMIC_TERMS includes 'academic'
74+
const result = analyzeQuery("I need academic information");
75+
expect(result.requiresSearch).toBe(true);
76+
expect(result.relevantNamespaces).toEqual([
77+
"courses_v3",
78+
"offerings",
79+
"prerequisites",
80+
"corequisites",
81+
"departments",
82+
"programs",
83+
]);
84+
});
85+
86+
test("should be case insensitive", () => {
87+
const result = analyzeQuery("TELL ME ABOUT THIS COURSE");
88+
expect(result.requiresSearch).toBe(true);
89+
expect(result.relevantNamespaces).toContain("courses_v3");
90+
});
91+
92+
test("should detect multiple namespaces in a single query", () => {
93+
const result = analyzeQuery(
94+
"What are the prerequisites and schedule for CSC108?",
95+
);
96+
expect(result.requiresSearch).toBe(true);
97+
expect(result.relevantNamespaces).toContain("prerequisites");
98+
expect(result.relevantNamespaces).toContain("offerings");
99+
expect(result.relevantNamespaces).toContain("courses_v3");
100+
});
101+
102+
test("should correctly identify course codes with different formats", () => {
103+
const formats = [
104+
"CSC108", // Standard format
105+
"CSC108H", // With suffix
106+
"CSCA08", // Four letters
107+
"MAT224", // Different department
108+
"ECO100Y", // Another format
109+
];
110+
111+
formats.forEach((code) => {
112+
const result = analyzeQuery(`Tell me about ${code}`);
113+
expect(result.requiresSearch).toBe(true);
114+
expect(result.relevantNamespaces).toContain("courses_v3");
115+
expect(result.relevantNamespaces).toContain("offerings");
116+
expect(result.relevantNamespaces).toContain("prerequisites");
117+
});
118+
});
119+
});
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import request from "supertest";
2+
import { describe, expect, it, test } from "@jest/globals";
3+
import app from "../src/index";
4+
5+
describe("Authentication API", () => {
6+
// The unit tests below are currently commented out because they require a database connection.
7+
// They will be uncommented out once all the necessary mocks are in place.
8+
9+
// describe('POST /auth/login', () => {
10+
// it('should return 200 and a token for valid credentials', async () => {
11+
// const response = await request(app)
12+
// .post('/auth/login')
13+
// .send({ username: 'validUser', password: 'validPassword' });
14+
// expect(response.status).toBe(200);
15+
// expect(response.body).toHaveProperty('token');
16+
// });
17+
// it('should return 401 for invalid credentials', async () => {
18+
// const response = await request(app)
19+
// .post('/auth/login')
20+
// .send({ username: 'invalidUser', password: 'wrongPassword' });
21+
// expect(response.status).toBe(401);
22+
// expect(response.body).toHaveProperty('error', 'Invalid credentials');
23+
// });
24+
// it('should return 400 if username or password is missing', async () => {
25+
// const response = await request(app)
26+
// .post('/auth/login')
27+
// .send({ username: 'validUser' });
28+
// expect(response.status).toBe(400);
29+
// expect(response.body).toHaveProperty('error', 'Username and password are required');
30+
// });
31+
// });
32+
// describe('POST /auth/register', () => {
33+
// it('should return 201 and create a new user for valid input', async () => {
34+
// const response = await request(app)
35+
// .post('/auth/register')
36+
// .send({ username: 'newUser', password: 'newPassword' });
37+
// expect(response.status).toBe(201);
38+
// expect(response.body).toHaveProperty('message', 'User registered successfully');
39+
// });
40+
// it('should return 400 if username is already taken', async () => {
41+
// await request(app)
42+
// .post('/auth/register')
43+
// .send({ username: 'existingUser', password: 'password123' });
44+
// const response = await request(app)
45+
// .post('/auth/register')
46+
// .send({ username: 'existingUser', password: 'password123' });
47+
// expect(response.status).toBe(400);
48+
// expect(response.body).toHaveProperty('error', 'Username is already taken');
49+
// });
50+
// it('should return 400 if username or password is missing', async () => {
51+
// const response = await request(app)
52+
// .post('/auth/register')
53+
// .send({ username: '' });
54+
// expect(response.status).toBe(400);
55+
// expect(response.body).toHaveProperty('error', 'Username and password are required');
56+
// });
57+
// });
58+
// describe('GET /auth/profile', () => {
59+
// it('should return 200 and user profile for valid token', async () => {
60+
// const loginResponse = await request(app)
61+
// .post('/auth/login')
62+
// .send({ username: 'validUser', password: 'validPassword' });
63+
// const token = loginResponse.body.token;
64+
// const response = await request(app)
65+
// .get('/auth/profile')
66+
// .set('Authorization', `Bearer ${token}`);
67+
// expect(response.status).toBe(200);
68+
// expect(response.body).toHaveProperty('username', 'validUser');
69+
// });
70+
// it('should return 401 if token is missing or invalid', async () => {
71+
// const response = await request(app)
72+
// .get('/auth/profile')
73+
// .set('Authorization', 'Bearer invalidToken');
74+
// expect(response.status).toBe(401);
75+
// expect(response.body).toHaveProperty('error', 'Unauthorized');
76+
// });
77+
// });
78+
it("template test", () => {
79+
expect(2 + 3).toEqual(5);
80+
});
81+
});
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import { describe, expect, it, test } from "@jest/globals";
2+
3+
import { Offering } from "../src/types/generatorTypes";
4+
import {
5+
canInsert,
6+
canInsertList,
7+
createOffering,
8+
} from "../src/utils/generatorHelpers";
9+
10+
describe("canInsert function", () => {
11+
const offering1: Offering = createOffering({
12+
id: 1,
13+
course_id: 101,
14+
day: "MO",
15+
start: "09:00:00",
16+
end: "10:00:00",
17+
});
18+
const offering2: Offering = createOffering({
19+
id: 2,
20+
course_id: 102,
21+
day: "MO",
22+
start: "10:00:00",
23+
end: "11:00:00",
24+
});
25+
const offering3: Offering = createOffering({
26+
id: 3,
27+
course_id: 103,
28+
day: "MO",
29+
start: "11:00:00",
30+
end: "12:00:00",
31+
});
32+
33+
it("should return true if there is no overlap with existing offerings", async () => {
34+
const toInsert: Offering = createOffering({
35+
id: 4,
36+
course_id: 104,
37+
day: "MO",
38+
start: "12:00:00",
39+
end: "13:00:00",
40+
});
41+
const curList: Offering[] = [offering1, offering2, offering3];
42+
43+
const result = await canInsert(toInsert, curList);
44+
45+
expect(result).toBe(true); // No overlap, should return true
46+
});
47+
48+
it("should return false if there is an overlap with an existing offering", async () => {
49+
const toInsert: Offering = createOffering({
50+
id: 4,
51+
course_id: 104,
52+
day: "MO",
53+
start: "09:30:00",
54+
end: "10:30:00",
55+
});
56+
const curList: Offering[] = [offering1, offering2, offering3];
57+
58+
const result = await canInsert(toInsert, curList);
59+
60+
expect(result).toBe(false); // There is an overlap with offering1, should return false
61+
});
62+
63+
it("should return true if the new offering starts after the last one ends", async () => {
64+
const toInsert: Offering = createOffering({
65+
id: 4,
66+
course_id: 104,
67+
day: "MO",
68+
start: "13:00:00",
69+
end: "14:00:00",
70+
});
71+
const curList: Offering[] = [offering1, offering2, offering3];
72+
73+
const result = await canInsert(toInsert, curList);
74+
75+
expect(result).toBe(true); // No overlap, should return true
76+
});
77+
78+
it("should return true if the new offering ends before the first one starts", async () => {
79+
const toInsert: Offering = createOffering({
80+
id: 4,
81+
course_id: 104,
82+
day: "MO",
83+
start: "07:00:00",
84+
end: "08:00:00",
85+
});
86+
const curList: Offering[] = [offering1, offering2, offering3];
87+
88+
const result = await canInsert(toInsert, curList);
89+
90+
expect(result).toBe(true); // No overlap, should return true
91+
});
92+
93+
it("should return false if the new offering is completely inside an existing one", async () => {
94+
const toInsert: Offering = createOffering({
95+
id: 4,
96+
course_id: 104,
97+
day: "MO",
98+
start: "09:30:00",
99+
end: "09:45:00",
100+
});
101+
const curList: Offering[] = [offering1, offering2, offering3];
102+
103+
const result = await canInsert(toInsert, curList);
104+
105+
expect(result).toBe(false); // Overlaps with offering1, should return false
106+
});
107+
108+
it("should return true if the day is different (no overlap)", async () => {
109+
const toInsert: Offering = createOffering({
110+
id: 4,
111+
course_id: 104,
112+
day: "TU",
113+
start: "09:00:00",
114+
end: "10:00:00",
115+
});
116+
const curList: Offering[] = [offering1, offering2, offering3];
117+
118+
const result = await canInsert(toInsert, curList);
119+
120+
expect(result).toBe(true); // Different day, no overlap
121+
});
122+
123+
it("special case", async () => {
124+
const toInsert: Offering = createOffering({
125+
id: 1069,
126+
course_id: 1271,
127+
day: "TH",
128+
start: "05:00:00",
129+
end: "17:00:00",
130+
});
131+
const offering11: Offering = createOffering({
132+
id: 414,
133+
course_id: 337,
134+
day: "TU",
135+
start: "15:00:00",
136+
end: "16:00:00",
137+
});
138+
const offering12: Offering = createOffering({
139+
id: 415,
140+
course_id: 337,
141+
day: "TH",
142+
start: "15:00:00",
143+
end: "17:00:00",
144+
});
145+
const offering13: Offering = createOffering({
146+
id: 1052,
147+
course_id: 1271,
148+
day: "TU",
149+
start: "10:00:00",
150+
end: "11:00:00",
151+
});
152+
const offering14: Offering = createOffering({
153+
id: 1053,
154+
course_id: 1271,
155+
day: "TU",
156+
start: "09:00:00",
157+
end: "11:00:00",
158+
});
159+
const curList: Offering[] = [
160+
offering11,
161+
offering12,
162+
offering13,
163+
offering14,
164+
];
165+
166+
const result = await canInsertList([toInsert], curList);
167+
168+
expect(result).toBe(false); // Special bug-causing case
169+
});
170+
});

0 commit comments

Comments
 (0)