@studious-lms/server 1.4.9 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/dist/index.js +15 -3
  2. package/dist/index.js.map +1 -1
  3. package/dist/lib/config/cors.d.ts.map +1 -1
  4. package/dist/lib/config/cors.js +2 -3
  5. package/dist/lib/config/cors.js.map +1 -1
  6. package/dist/lib/fileUpload.d.ts.map +1 -1
  7. package/dist/lib/fileUpload.js +4 -5
  8. package/dist/lib/fileUpload.js.map +1 -1
  9. package/dist/lib/pusher.d.ts +7 -1
  10. package/dist/lib/pusher.d.ts.map +1 -1
  11. package/dist/lib/pusher.js +9 -3
  12. package/dist/lib/pusher.js.map +1 -1
  13. package/dist/models/attendance.d.ts +16 -7
  14. package/dist/models/attendance.d.ts.map +1 -1
  15. package/dist/models/attendance.js +17 -2
  16. package/dist/models/attendance.js.map +1 -1
  17. package/dist/models/auth.d.ts +1 -0
  18. package/dist/models/auth.d.ts.map +1 -1
  19. package/dist/models/auth.js +3 -2
  20. package/dist/models/auth.js.map +1 -1
  21. package/dist/models/comment.d.ts +0 -5
  22. package/dist/models/comment.d.ts.map +1 -1
  23. package/dist/models/conversation.d.ts +17 -16
  24. package/dist/models/conversation.d.ts.map +1 -1
  25. package/dist/models/labChat.d.ts +2 -1
  26. package/dist/models/labChat.d.ts.map +1 -1
  27. package/dist/models/message.d.ts +6 -3
  28. package/dist/models/message.d.ts.map +1 -1
  29. package/dist/models/school.d.ts +0 -22
  30. package/dist/models/school.d.ts.map +1 -1
  31. package/dist/models/school.js +2 -14
  32. package/dist/models/school.js.map +1 -1
  33. package/dist/models/worksheet.d.ts +76 -19
  34. package/dist/models/worksheet.d.ts.map +1 -1
  35. package/dist/models/worksheet.js +2 -2
  36. package/dist/models/worksheet.js.map +1 -1
  37. package/dist/pipelines/aiLabChat.d.ts +20 -9
  38. package/dist/pipelines/aiLabChat.d.ts.map +1 -1
  39. package/dist/pipelines/aiLabChat.js +63 -75
  40. package/dist/pipelines/aiLabChat.js.map +1 -1
  41. package/dist/pipelines/aiNewtonChat.d.ts +13 -4
  42. package/dist/pipelines/aiNewtonChat.d.ts.map +1 -1
  43. package/dist/pipelines/aiNewtonChat.js +39 -27
  44. package/dist/pipelines/aiNewtonChat.js.map +1 -1
  45. package/dist/pipelines/gradeWorksheet.d.ts +5 -3
  46. package/dist/pipelines/gradeWorksheet.d.ts.map +1 -1
  47. package/dist/pipelines/gradeWorksheet.js +60 -24
  48. package/dist/pipelines/gradeWorksheet.js.map +1 -1
  49. package/dist/pipelines/pdfToWorksheet.d.ts +34 -2
  50. package/dist/pipelines/pdfToWorksheet.d.ts.map +1 -1
  51. package/dist/pipelines/pdfToWorksheet.js +205 -32
  52. package/dist/pipelines/pdfToWorksheet.js.map +1 -1
  53. package/dist/routers/_app.d.ts +106 -48
  54. package/dist/routers/_app.d.ts.map +1 -1
  55. package/dist/routers/announcement.d.ts +0 -4
  56. package/dist/routers/announcement.d.ts.map +1 -1
  57. package/dist/routers/assignment.d.ts +0 -8
  58. package/dist/routers/assignment.d.ts.map +1 -1
  59. package/dist/routers/assignment.js +64 -4
  60. package/dist/routers/assignment.js.map +1 -1
  61. package/dist/routers/auth.d.ts +1 -0
  62. package/dist/routers/auth.d.ts.map +1 -1
  63. package/dist/routers/comment.d.ts +0 -3
  64. package/dist/routers/comment.d.ts.map +1 -1
  65. package/dist/routers/conversation.d.ts +2 -1
  66. package/dist/routers/conversation.d.ts.map +1 -1
  67. package/dist/routers/labChat.d.ts +3 -1
  68. package/dist/routers/labChat.d.ts.map +1 -1
  69. package/dist/routers/message.d.ts +3 -0
  70. package/dist/routers/message.d.ts.map +1 -1
  71. package/dist/routers/newtonChat.d.ts +1 -0
  72. package/dist/routers/newtonChat.d.ts.map +1 -1
  73. package/dist/routers/school.d.ts +6 -0
  74. package/dist/routers/school.d.ts.map +1 -1
  75. package/dist/routers/school.js +7 -2
  76. package/dist/routers/school.js.map +1 -1
  77. package/dist/routers/worksheet.d.ts +37 -7
  78. package/dist/routers/worksheet.d.ts.map +1 -1
  79. package/dist/routers/worksheet.js +16 -6
  80. package/dist/routers/worksheet.js.map +1 -1
  81. package/dist/services/announcement.js +4 -4
  82. package/dist/services/announcement.js.map +1 -1
  83. package/dist/services/assignment.d.ts.map +1 -1
  84. package/dist/services/assignment.js +16 -6
  85. package/dist/services/assignment.js.map +1 -1
  86. package/dist/services/attendance.d.ts.map +1 -1
  87. package/dist/services/attendance.js +68 -4
  88. package/dist/services/attendance.js.map +1 -1
  89. package/dist/services/auth.d.ts +1 -0
  90. package/dist/services/auth.d.ts.map +1 -1
  91. package/dist/services/comment.d.ts +0 -3
  92. package/dist/services/comment.d.ts.map +1 -1
  93. package/dist/services/conversation.d.ts +2 -1
  94. package/dist/services/conversation.d.ts.map +1 -1
  95. package/dist/services/labChat.d.ts +10 -2
  96. package/dist/services/labChat.d.ts.map +1 -1
  97. package/dist/services/labChat.js +103 -44
  98. package/dist/services/labChat.js.map +1 -1
  99. package/dist/services/message.d.ts +3 -0
  100. package/dist/services/message.d.ts.map +1 -1
  101. package/dist/services/message.js +20 -2
  102. package/dist/services/message.js.map +1 -1
  103. package/dist/services/newtonChat.d.ts +1 -0
  104. package/dist/services/newtonChat.d.ts.map +1 -1
  105. package/dist/services/newtonChat.js +40 -17
  106. package/dist/services/newtonChat.js.map +1 -1
  107. package/dist/services/school.d.ts +6 -0
  108. package/dist/services/school.d.ts.map +1 -1
  109. package/dist/services/school.js +71 -6
  110. package/dist/services/school.js.map +1 -1
  111. package/dist/services/worksheet.d.ts +48 -11
  112. package/dist/services/worksheet.d.ts.map +1 -1
  113. package/dist/services/worksheet.js +73 -29
  114. package/dist/services/worksheet.js.map +1 -1
  115. package/dist/utils/generateUsername.d.ts +8 -0
  116. package/dist/utils/generateUsername.d.ts.map +1 -0
  117. package/dist/utils/generateUsername.js +103 -0
  118. package/dist/utils/generateUsername.js.map +1 -0
  119. package/dist/utils/inference.d.ts +50 -1
  120. package/dist/utils/inference.d.ts.map +1 -1
  121. package/dist/utils/inference.js +167 -2
  122. package/dist/utils/inference.js.map +1 -1
  123. package/dist/workers/connection.d.ts +3 -0
  124. package/dist/workers/connection.d.ts.map +1 -0
  125. package/dist/workers/connection.js +33 -0
  126. package/dist/workers/connection.js.map +1 -0
  127. package/dist/workers/index.d.ts +10 -0
  128. package/dist/workers/index.d.ts.map +1 -0
  129. package/dist/workers/index.js +63 -0
  130. package/dist/workers/index.js.map +1 -0
  131. package/dist/workers/processors/aiLabChat.d.ts +7 -0
  132. package/dist/workers/processors/aiLabChat.d.ts.map +1 -0
  133. package/dist/workers/processors/aiLabChat.js +52 -0
  134. package/dist/workers/processors/aiLabChat.js.map +1 -0
  135. package/dist/workers/processors/aiNewtonChat.d.ts +7 -0
  136. package/dist/workers/processors/aiNewtonChat.d.ts.map +1 -0
  137. package/dist/workers/processors/aiNewtonChat.js +57 -0
  138. package/dist/workers/processors/aiNewtonChat.js.map +1 -0
  139. package/dist/workers/processors/createRunJobWorker.d.ts +46 -0
  140. package/dist/workers/processors/createRunJobWorker.d.ts.map +1 -0
  141. package/dist/workers/processors/createRunJobWorker.js +118 -0
  142. package/dist/workers/processors/createRunJobWorker.js.map +1 -0
  143. package/dist/workers/processors/gradeWorksheet.d.ts +6 -0
  144. package/dist/workers/processors/gradeWorksheet.d.ts.map +1 -0
  145. package/dist/workers/processors/gradeWorksheet.js +19 -0
  146. package/dist/workers/processors/gradeWorksheet.js.map +1 -0
  147. package/dist/workers/processors/pdfToWorksheet.d.ts +8 -0
  148. package/dist/workers/processors/pdfToWorksheet.d.ts.map +1 -0
  149. package/dist/workers/processors/pdfToWorksheet.js +20 -0
  150. package/dist/workers/processors/pdfToWorksheet.js.map +1 -0
  151. package/dist/workers/processors/studentProgressChat.d.ts +8 -0
  152. package/dist/workers/processors/studentProgressChat.d.ts.map +1 -0
  153. package/dist/workers/processors/studentProgressChat.js +27 -0
  154. package/dist/workers/processors/studentProgressChat.js.map +1 -0
  155. package/dist/workers/queues.d.ts +85 -0
  156. package/dist/workers/queues.d.ts.map +1 -0
  157. package/dist/workers/queues.js +119 -0
  158. package/dist/workers/queues.js.map +1 -0
  159. package/package.json +6 -3
  160. package/prisma/migrations/20260506162500_consolidate_run_job_and_unify_job_status/migration.sql +166 -0
  161. package/prisma/schema.prisma +465 -387
  162. package/src/index.ts +12 -1
  163. package/src/lib/config/cors.ts +0 -1
  164. package/src/lib/fileUpload.ts +2 -4
  165. package/src/lib/pusher.ts +7 -1
  166. package/src/models/attendance.ts +15 -0
  167. package/src/models/auth.ts +1 -0
  168. package/src/models/school.ts +0 -14
  169. package/src/models/worksheet.ts +2 -2
  170. package/src/pipelines/aiLabChat.ts +71 -79
  171. package/src/pipelines/aiNewtonChat.ts +47 -31
  172. package/src/pipelines/gradeWorksheet.ts +63 -25
  173. package/src/pipelines/pdfToWorksheet.ts +272 -34
  174. package/src/routers/assignment.ts +74 -3
  175. package/src/routers/school.ts +5 -0
  176. package/src/routers/worksheet.ts +25 -4
  177. package/src/services/announcement.ts +2 -2
  178. package/src/services/assignment.ts +18 -4
  179. package/src/services/attendance.ts +87 -2
  180. package/src/services/labChat.ts +108 -48
  181. package/src/services/message.ts +19 -0
  182. package/src/services/newtonChat.ts +46 -25
  183. package/src/services/school.ts +89 -6
  184. package/src/services/worksheet.ts +87 -28
  185. package/src/utils/generateUsername.ts +108 -0
  186. package/src/utils/inference.ts +209 -1
  187. package/src/workers/connection.ts +37 -0
  188. package/src/workers/index.ts +71 -0
  189. package/src/workers/processors/aiLabChat.ts +65 -0
  190. package/src/workers/processors/aiNewtonChat.ts +75 -0
  191. package/src/workers/processors/createRunJobWorker.ts +164 -0
  192. package/src/workers/processors/gradeWorksheet.ts +16 -0
  193. package/src/workers/processors/pdfToWorksheet.ts +17 -0
  194. package/src/workers/processors/studentProgressChat.ts +28 -0
  195. package/src/workers/queues.ts +174 -0
  196. package/tests/{globalSetup.ts → integration/globalSetup.ts} +4 -4
  197. package/tests/{helpers.ts → integration/helpers.ts} +2 -2
  198. package/tests/{pipelines → integration/pipelines}/pdfToWorksheet.test.ts +55 -43
  199. package/tests/{routers → integration/routers}/attendance.test.ts +61 -2
  200. package/tests/{routers → integration/routers}/auth.test.ts +3 -3
  201. package/tests/{routers → integration/routers}/class.test.ts +3 -17
  202. package/tests/{routers → integration/routers}/conversation.test.ts +1 -1
  203. package/tests/{routers → integration/routers}/event.test.ts +1 -1
  204. package/tests/{routers → integration/routers}/message.test.ts +1 -1
  205. package/tests/{routers → integration/routers}/newtonChat.test.ts +1 -1
  206. package/tests/{routers → integration/routers}/studentProgress.test.ts +1 -1
  207. package/tests/{user.test.ts → integration/routers/user.test.ts} +2 -2
  208. package/tests/integration/routers/worksheet.test.ts +448 -0
  209. package/tests/{setup.ts → integration/setup.ts} +4 -4
  210. package/tests/{lib → unit/lib}/cors.test.ts +1 -1
  211. package/tests/{middleware → unit/middleware}/security.test.ts +1 -1
  212. package/tests/{pipelines → unit/pipelines}/aiLabChat.test.ts +1 -1
  213. package/tests/{lib → unit/pipelines}/aiLabChatContract.test.ts +1 -1
  214. package/tests/{utils/aiLabChatPrompt.test.ts → unit/pipelines/labChatPrompt.test.ts} +2 -2
  215. package/tests/{utils → unit/services}/studentProgress.test.ts +5 -5
  216. package/tests/{utils → unit/utils}/aiUser.test.ts +1 -1
  217. package/tests/{utils → unit/utils}/generateInviteCode.test.ts +1 -1
  218. package/tests/{utils → unit/utils}/inference.test.ts +6 -6
  219. package/tests/{utils → unit/utils}/logger.test.ts +1 -1
  220. package/tests/{utils → unit/utils}/prismaErrorHandler.test.ts +1 -1
  221. package/tests/{utils → unit/utils}/prismaWrapper.test.ts +1 -1
  222. package/vitest.config.ts +11 -3
  223. package/vitest.unit.config.ts +10 -10
  224. package/tests/routers/worksheet.test.ts +0 -194
  225. package/tests/startup.test.ts +0 -5
  226. package/tests/worksheet.test.ts +0 -181
  227. /package/tests/{routers → integration/routers}/agenda.test.ts +0 -0
  228. /package/tests/{routers → integration/routers}/announcement.test.ts +0 -0
  229. /package/tests/{routers → integration/routers}/assignment.test.ts +0 -0
  230. /package/tests/{routers → integration/routers}/comment.test.ts +0 -0
  231. /package/tests/{routers → integration/routers}/folder.test.ts +0 -0
  232. /package/tests/{routers → integration/routers}/labChat.test.ts +0 -0
  233. /package/tests/{routers → integration/routers}/marketing.test.ts +0 -0
  234. /package/tests/{routers → integration/routers}/notification.test.ts +0 -0
  235. /package/tests/{routers → integration/routers}/search.test.ts +0 -0
  236. /package/tests/{routers → integration/routers}/section.test.ts +0 -0
  237. /package/tests/{server → unit/middleware}/rateLimit.test.ts +0 -0
@@ -6,23 +6,23 @@ import { resolve } from 'path';
6
6
  config({ path: resolve(process.cwd(), '.env.test'), override: true });
7
7
 
8
8
  /**
9
- * Unit test config - no DB, no globalSetup.
10
- * Use: npm run test:unit
9
+ * Unit test config — no DB, no globalSetup, no shared tRPC callers.
10
+ * Files here mock every external boundary (prisma, openai, pusher, etc.).
11
+ *
12
+ * Vitest's default already parallelises across files via workers; tests
13
+ * within a single file run serially because many use shared `vi.hoisted`
14
+ * mocks or console spies that would race under in-file concurrency.
15
+ *
16
+ * Use: npm run test:unit.
11
17
  */
12
18
  export default defineConfig({
13
19
  test: {
14
20
  globals: true,
15
21
  environment: 'node',
16
- include: [
17
- 'tests/utils/**/*.test.ts',
18
- 'tests/server/**/*.test.ts',
19
- 'tests/middleware/**/*.test.ts',
20
- 'tests/lib/**/*.test.ts',
21
- 'tests/pipelines/**/*.test.ts',
22
- 'tests/routers/**/*.test.ts',
23
- ],
22
+ include: ['tests/unit/**/*.test.ts'],
24
23
  env: {
25
24
  NODE_ENV: 'test',
25
+ OPENAI_API_KEY: 'test-openai-key',
26
26
  },
27
27
  },
28
28
  });
@@ -1,194 +0,0 @@
1
- import { beforeEach, describe, expect, test } from "vitest";
2
- import { user1Caller, user2Caller, user3Caller } from "../setup";
3
-
4
- const expectTRPCError = async (promise: Promise<unknown>, code: string) => {
5
- await expect(promise).rejects.toMatchObject({ code });
6
- };
7
-
8
- describe("Worksheet Router Security", () => {
9
- let testClass: any;
10
- let worksheet: any;
11
- let question: any;
12
- let multipleChoiceQuestion: any;
13
- let submission: any;
14
- let worksheetResponse: any;
15
- let progressId: string;
16
-
17
- beforeEach(async () => {
18
- testClass = await user1Caller.class.create({
19
- name: `Worksheet Security ${Date.now()}`,
20
- subject: "Physics",
21
- section: "12th Grade",
22
- });
23
-
24
- const user2Profile = await user2Caller.user.getProfile();
25
- await user1Caller.class.addStudent({
26
- classId: testClass.id,
27
- studentId: user2Profile.id,
28
- });
29
-
30
- worksheet = await user1Caller.worksheet.create({
31
- classId: testClass.id,
32
- name: "Security Worksheet",
33
- });
34
-
35
- question = await user1Caller.worksheet.addQuestion({
36
- worksheetId: worksheet.id,
37
- question: "What is force?",
38
- answer: "Mass times acceleration",
39
- points: 5,
40
- type: "SHORT_ANSWER",
41
- });
42
-
43
- multipleChoiceQuestion = await user1Caller.worksheet.addQuestion({
44
- worksheetId: worksheet.id,
45
- question: "What is the SI unit of force?",
46
- answer: "newton",
47
- points: 1,
48
- type: "MULTIPLE_CHOICE",
49
- options: [
50
- { id: "a", text: "Joule", isCorrect: false },
51
- { id: "b", text: "Newton", isCorrect: true },
52
- ],
53
- });
54
-
55
- const dueDate = new Date();
56
- dueDate.setDate(dueDate.getDate() + 7);
57
-
58
- const assignment = await user1Caller.assignment.create({
59
- classId: testClass.id,
60
- title: "Worksheet Assignment",
61
- instructions: "Complete the worksheet.",
62
- dueDate: dueDate.toISOString(),
63
- acceptWorksheet: true,
64
- gradeWithAI: true,
65
- worksheetIds: [worksheet.id],
66
- type: "HOMEWORK",
67
- });
68
-
69
- submission = await user2Caller.assignment.getSubmission({
70
- classId: testClass.id,
71
- assignmentId: assignment.id,
72
- });
73
-
74
- worksheetResponse = await user2Caller.worksheet.getWorksheetSubmission({
75
- worksheetId: worksheet.id,
76
- submissionId: submission.id,
77
- });
78
-
79
- const answered = await user2Caller.worksheet.answerQuestion({
80
- worksheetResponseId: worksheetResponse.id,
81
- questionId: question.id,
82
- response: "Mass times acceleration",
83
- });
84
-
85
- progressId = answered.responses[0].id;
86
- });
87
-
88
- test("redacts worksheet answers and correctness metadata for students", async () => {
89
- const studentWorksheet = await user2Caller.worksheet.getWorksheet({
90
- worksheetId: worksheet.id,
91
- });
92
-
93
- const shortAnswerQuestion = studentWorksheet.questions.find(
94
- (item: any) => item.id === question.id,
95
- );
96
- expect(shortAnswerQuestion.answer).toBe("");
97
- expect(shortAnswerQuestion.markScheme).toBeNull();
98
-
99
- const studentMcq = studentWorksheet.questions.find(
100
- (item: any) => item.id === multipleChoiceQuestion.id,
101
- );
102
- expect(studentMcq.answer).toBe("");
103
- expect(studentMcq.options).toEqual([
104
- { id: "a", text: "Joule" },
105
- { id: "b", text: "Newton" },
106
- ]);
107
- });
108
-
109
- test("blocks cross-class worksheet reads by id", async () => {
110
- await expectTRPCError(
111
- user3Caller.worksheet.getWorksheet({ worksheetId: worksheet.id }),
112
- "FORBIDDEN",
113
- );
114
- });
115
-
116
- test("blocks cross-class worksheet submission reads by id", async () => {
117
- await expectTRPCError(
118
- user3Caller.worksheet.getWorksheetSubmission({
119
- worksheetId: worksheet.id,
120
- submissionId: submission.id,
121
- }),
122
- "FORBIDDEN",
123
- );
124
- });
125
-
126
- test("blocks cross-class worksheet answer mutations", async () => {
127
- await expectTRPCError(
128
- user3Caller.worksheet.answerQuestion({
129
- worksheetResponseId: worksheetResponse.id,
130
- questionId: question.id,
131
- response: "Unauthorized answer",
132
- }),
133
- "FORBIDDEN",
134
- );
135
- });
136
-
137
- test("blocks students from teacher worksheet mutations", async () => {
138
- await expectTRPCError(
139
- user2Caller.worksheet.updateWorksheet({
140
- worksheetId: worksheet.id,
141
- name: "Student update",
142
- }),
143
- "FORBIDDEN",
144
- );
145
-
146
- await expectTRPCError(
147
- user2Caller.worksheet.addQuestion({
148
- worksheetId: worksheet.id,
149
- question: "Student-created question",
150
- answer: "Nope",
151
- type: "SHORT_ANSWER",
152
- }),
153
- "FORBIDDEN",
154
- );
155
- });
156
-
157
- test("blocks students from grading and feedback actions", async () => {
158
- await expectTRPCError(
159
- user2Caller.worksheet.gradeAnswer({
160
- questionId: question.id,
161
- responseId: progressId,
162
- studentWorksheetResponseId: worksheetResponse.id,
163
- isCorrect: true,
164
- feedback: "Nice work",
165
- points: 5,
166
- }),
167
- "FORBIDDEN",
168
- );
169
-
170
- await expectTRPCError(
171
- user2Caller.worksheet.addComment({
172
- responseId: progressId,
173
- comment: "Teacher feedback",
174
- }),
175
- "FORBIDDEN",
176
- );
177
-
178
- await expectTRPCError(
179
- user2Caller.worksheet.cancelGrading({
180
- worksheetResponseId: worksheetResponse.id,
181
- progressId,
182
- }),
183
- "FORBIDDEN",
184
- );
185
-
186
- await expectTRPCError(
187
- user2Caller.worksheet.regradeQuestion({
188
- worksheetResponseId: worksheetResponse.id,
189
- progressId,
190
- }),
191
- "FORBIDDEN",
192
- );
193
- });
194
- });
@@ -1,5 +0,0 @@
1
- import { expect, test } from "vitest";
2
-
3
- test('setup works', () => {
4
- expect(1).toBe(1);
5
- });
@@ -1,181 +0,0 @@
1
- import { test, expect, describe, beforeAll } from 'vitest';
2
- import { user1Caller } from './setup';
3
-
4
- describe('Worksheet Router', () => {
5
- let testClass: any;
6
- let worksheetId: string;
7
- let questionId: string;
8
- let secondQuestionId: string;
9
-
10
- beforeAll(async () => {
11
- testClass = await user1Caller.class.create({
12
- name: 'Worksheet Test Class',
13
- subject: 'Physics',
14
- section: '12th Grade',
15
- });
16
- });
17
-
18
- describe('create', () => {
19
- test('should create a worksheet', async () => {
20
- const worksheet = await user1Caller.worksheet.create({
21
- classId: testClass.id,
22
- name: 'Chapter 1 Quiz',
23
- });
24
-
25
- expect(worksheet).toBeDefined();
26
- expect(worksheet.name).toBe('Chapter 1 Quiz');
27
- expect(worksheet.classId).toBe(testClass.id);
28
- worksheetId = worksheet.id;
29
- });
30
- });
31
-
32
- describe('listWorksheets', () => {
33
- test('should list worksheets for a class', async () => {
34
- const worksheets = await user1Caller.worksheet.listWorksheets({
35
- classId: testClass.id,
36
- });
37
-
38
- expect(Array.isArray(worksheets)).toBe(true);
39
- expect(worksheets.length).toBeGreaterThanOrEqual(1);
40
- expect(worksheets.some((w) => w.id === worksheetId)).toBe(true);
41
- });
42
- });
43
-
44
- describe('getWorksheet', () => {
45
- test('should get a worksheet by ID', async () => {
46
- const worksheet = await user1Caller.worksheet.getWorksheet({
47
- worksheetId,
48
- });
49
-
50
- expect(worksheet).toBeDefined();
51
- expect(worksheet.id).toBe(worksheetId);
52
- expect(worksheet.name).toBe('Chapter 1 Quiz');
53
- });
54
-
55
- test('should fail for non-existent worksheet', async () => {
56
- await expect(
57
- user1Caller.worksheet.getWorksheet({ worksheetId: 'nonexistent' }),
58
- ).rejects.toThrow();
59
- });
60
- });
61
-
62
- describe('updateWorksheet', () => {
63
- test('should update worksheet name', async () => {
64
- const updated = await user1Caller.worksheet.updateWorksheet({
65
- worksheetId,
66
- name: 'Chapter 1 Test',
67
- });
68
-
69
- expect(updated.name).toBe('Chapter 1 Test');
70
- });
71
- });
72
-
73
- describe('addQuestion', () => {
74
- test('should add a short answer question', async () => {
75
- const question = await user1Caller.worksheet.addQuestion({
76
- worksheetId,
77
- question: 'What is Newton\'s second law?',
78
- answer: 'F = ma',
79
- points: 5,
80
- type: 'SHORT_ANSWER',
81
- });
82
-
83
- expect(question).toBeDefined();
84
- expect(question.question).toBe('What is Newton\'s second law?');
85
- expect(question.answer).toBe('F = ma');
86
- expect(question.points).toBe(5);
87
- questionId = question.id;
88
- });
89
-
90
- test('should add a multiple choice question', async () => {
91
- const question = await user1Caller.worksheet.addQuestion({
92
- worksheetId,
93
- question: 'What is the SI unit of force?',
94
- answer: 'Newton',
95
- points: 3,
96
- type: 'MULTIPLE_CHOICE',
97
- options: [
98
- { id: 'a', text: 'Joule', isCorrect: false },
99
- { id: 'b', text: 'Newton', isCorrect: true },
100
- { id: 'c', text: 'Watt', isCorrect: false },
101
- { id: 'd', text: 'Pascal', isCorrect: false },
102
- ],
103
- });
104
-
105
- expect(question).toBeDefined();
106
- expect(question.type).toBe('MULTIPLE_CHOICE');
107
- secondQuestionId = question.id;
108
- });
109
-
110
- test('should fail for non-existent worksheet', async () => {
111
- await expect(
112
- user1Caller.worksheet.addQuestion({
113
- worksheetId: 'nonexistent',
114
- question: 'Test',
115
- answer: 'Test',
116
- type: 'SHORT_ANSWER',
117
- }),
118
- ).rejects.toThrow();
119
- });
120
- });
121
-
122
- describe('updateQuestion', () => {
123
- test('should update a question', async () => {
124
- const updated = await user1Caller.worksheet.updateQuestion({
125
- worksheetId,
126
- questionId,
127
- question: 'State Newton\'s second law of motion.',
128
- points: 10,
129
- });
130
-
131
- expect(updated.question).toBe('State Newton\'s second law of motion.');
132
- expect(updated.points).toBe(10);
133
- });
134
- });
135
-
136
- describe('reorderQuestions', () => {
137
- test('should reorder questions', async () => {
138
- const result = await user1Caller.worksheet.reorderQuestions({
139
- worksheetId,
140
- movedId: secondQuestionId,
141
- position: 'before',
142
- targetId: questionId,
143
- });
144
-
145
- expect(Array.isArray(result)).toBe(true);
146
- expect(result.length).toBe(2);
147
- const ids = result.map((r) => r.id);
148
- expect(ids).toContain(questionId);
149
- expect(ids).toContain(secondQuestionId);
150
- });
151
- });
152
-
153
- describe('deleteQuestion', () => {
154
- test('should delete a question', async () => {
155
- const result = await user1Caller.worksheet.deleteQuestion({
156
- worksheetId,
157
- questionId: secondQuestionId,
158
- });
159
-
160
- expect(result.id).toBe(secondQuestionId);
161
-
162
- const worksheet = await user1Caller.worksheet.getWorksheet({ worksheetId });
163
- expect(worksheet.questions.length).toBe(1);
164
- });
165
- });
166
-
167
- describe('deleteWorksheet', () => {
168
- test('should delete a worksheet', async () => {
169
- const toDelete = await user1Caller.worksheet.create({
170
- classId: testClass.id,
171
- name: 'Temporary Worksheet',
172
- });
173
-
174
- const result = await user1Caller.worksheet.deleteWorksheet({
175
- worksheetId: toDelete.id,
176
- });
177
-
178
- expect(result.id).toBe(toDelete.id);
179
- });
180
- });
181
- });