@studious-lms/server 1.2.53 → 1.3.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.
- package/.coderabbit.yaml +9 -0
- package/.env.example +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +102 -8
- package/dist/index.js.map +1 -1
- package/dist/lib/config/env.d.ts +21 -0
- package/dist/lib/config/env.d.ts.map +1 -1
- package/dist/lib/config/env.js +8 -2
- package/dist/lib/config/env.js.map +1 -1
- package/dist/lib/fileUpload.d.ts.map +1 -1
- package/dist/lib/fileUpload.js +2 -2
- package/dist/lib/fileUpload.js.map +1 -1
- package/dist/lib/googleCloudStorage.d.ts +6 -0
- package/dist/lib/googleCloudStorage.d.ts.map +1 -1
- package/dist/lib/googleCloudStorage.js +19 -2
- package/dist/lib/googleCloudStorage.js.map +1 -1
- package/dist/lib/pusher.d.ts +4 -1
- package/dist/lib/pusher.d.ts.map +1 -1
- package/dist/lib/pusher.js +6 -3
- package/dist/lib/pusher.js.map +1 -1
- package/dist/lib/redis.d.ts +5 -0
- package/dist/lib/redis.d.ts.map +1 -0
- package/dist/lib/redis.js +53 -0
- package/dist/lib/redis.js.map +1 -0
- package/dist/lib/thumbnailGenerator.d.ts +0 -21
- package/dist/lib/thumbnailGenerator.d.ts.map +1 -1
- package/dist/lib/thumbnailGenerator.js +157 -160
- package/dist/lib/thumbnailGenerator.js.map +1 -1
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +33 -95
- package/dist/middleware/auth.js.map +1 -1
- package/dist/models/agenda.d.ts +97 -0
- package/dist/models/agenda.d.ts.map +1 -0
- package/dist/models/agenda.js +40 -0
- package/dist/models/agenda.js.map +1 -0
- package/dist/models/announcement.d.ts +223 -0
- package/dist/models/announcement.d.ts.map +1 -0
- package/dist/models/announcement.js +120 -0
- package/dist/models/announcement.js.map +1 -0
- package/dist/models/assignment.d.ts +1292 -0
- package/dist/models/assignment.d.ts.map +1 -0
- package/dist/models/assignment.js +309 -0
- package/dist/models/assignment.js.map +1 -0
- package/dist/models/attendance.d.ts +180 -0
- package/dist/models/attendance.d.ts.map +1 -0
- package/dist/models/attendance.js +188 -0
- package/dist/models/attendance.js.map +1 -0
- package/dist/models/auth.d.ts +153 -0
- package/dist/models/auth.d.ts.map +1 -0
- package/dist/models/auth.js +217 -0
- package/dist/models/auth.js.map +1 -0
- package/dist/models/class.d.ts +439 -0
- package/dist/models/class.d.ts.map +1 -0
- package/dist/models/class.js +546 -0
- package/dist/models/class.js.map +1 -0
- package/dist/models/comment.d.ts +171 -0
- package/dist/models/comment.d.ts.map +1 -0
- package/dist/models/comment.js +138 -0
- package/dist/models/comment.js.map +1 -0
- package/dist/models/conversation.d.ts +164 -0
- package/dist/models/conversation.d.ts.map +1 -0
- package/dist/models/conversation.js +175 -0
- package/dist/models/conversation.js.map +1 -0
- package/dist/models/event.d.ts +295 -0
- package/dist/models/event.d.ts.map +1 -0
- package/dist/models/event.js +145 -0
- package/dist/models/event.js.map +1 -0
- package/dist/models/file.d.ts +536 -0
- package/dist/models/file.d.ts.map +1 -0
- package/dist/models/file.js +126 -0
- package/dist/models/file.js.map +1 -0
- package/dist/models/folder.d.ts +295 -0
- package/dist/models/folder.d.ts.map +1 -0
- package/dist/models/folder.js +202 -0
- package/dist/models/folder.js.map +1 -0
- package/dist/models/labChat.d.ts +243 -0
- package/dist/models/labChat.d.ts.map +1 -0
- package/dist/models/labChat.js +204 -0
- package/dist/models/labChat.js.map +1 -0
- package/dist/models/marketing.d.ts +72 -0
- package/dist/models/marketing.d.ts.map +1 -0
- package/dist/models/marketing.js +26 -0
- package/dist/models/marketing.js.map +1 -0
- package/dist/models/message.d.ts +100 -0
- package/dist/models/message.d.ts.map +1 -0
- package/dist/models/message.js +131 -0
- package/dist/models/message.js.map +1 -0
- package/dist/models/newtonChat.d.ts +72 -0
- package/dist/models/newtonChat.d.ts.map +1 -0
- package/dist/models/newtonChat.js +61 -0
- package/dist/models/newtonChat.js.map +1 -0
- package/dist/models/notification.d.ts +65 -0
- package/dist/models/notification.d.ts.map +1 -0
- package/dist/models/notification.js +46 -0
- package/dist/models/notification.js.map +1 -0
- package/dist/models/section.d.ts +102 -0
- package/dist/models/section.d.ts.map +1 -0
- package/dist/models/section.js +83 -0
- package/dist/models/section.js.map +1 -0
- package/dist/models/user.d.ts +39 -0
- package/dist/models/user.d.ts.map +1 -0
- package/dist/models/user.js +38 -0
- package/dist/models/user.js.map +1 -0
- package/dist/models/worksheet.d.ts +460 -0
- package/dist/models/worksheet.d.ts.map +1 -0
- package/dist/models/worksheet.js +200 -0
- package/dist/models/worksheet.js.map +1 -0
- package/dist/pipelines/aiLabChat.d.ts +21 -0
- package/dist/pipelines/aiLabChat.d.ts.map +1 -0
- package/dist/pipelines/aiLabChat.js +460 -0
- package/dist/pipelines/aiLabChat.js.map +1 -0
- package/dist/pipelines/aiNewtonChat.d.ts +30 -0
- package/dist/pipelines/aiNewtonChat.d.ts.map +1 -0
- package/dist/pipelines/aiNewtonChat.js +289 -0
- package/dist/pipelines/aiNewtonChat.js.map +1 -0
- package/dist/pipelines/gradeWorksheet.d.ts +30 -0
- package/dist/pipelines/gradeWorksheet.d.ts.map +1 -0
- package/dist/pipelines/gradeWorksheet.js +252 -0
- package/dist/pipelines/gradeWorksheet.js.map +1 -0
- package/dist/routers/_app.d.ts +1393 -1267
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/agenda.d.ts +22 -22
- package/dist/routers/agenda.d.ts.map +1 -1
- package/dist/routers/agenda.js +4 -65
- package/dist/routers/agenda.js.map +1 -1
- package/dist/routers/announcement.d.ts +16 -16
- package/dist/routers/announcement.d.ts.map +1 -1
- package/dist/routers/announcement.js +37 -446
- package/dist/routers/announcement.js.map +1 -1
- package/dist/routers/assignment.d.ts +300 -378
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/assignment.js +78 -1868
- package/dist/routers/assignment.js.map +1 -1
- package/dist/routers/attendance.d.ts +19 -9
- package/dist/routers/attendance.d.ts.map +1 -1
- package/dist/routers/attendance.js +7 -264
- package/dist/routers/attendance.js.map +1 -1
- package/dist/routers/auth.d.ts +2 -2
- package/dist/routers/auth.d.ts.map +1 -1
- package/dist/routers/auth.js +29 -354
- package/dist/routers/auth.js.map +1 -1
- package/dist/routers/class.d.ts +139 -68
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/class.js +82 -1052
- package/dist/routers/class.js.map +1 -1
- package/dist/routers/comment.d.ts +6 -42
- package/dist/routers/comment.d.ts.map +1 -1
- package/dist/routers/comment.js +24 -244
- package/dist/routers/comment.js.map +1 -1
- package/dist/routers/conversation.d.ts +45 -7
- package/dist/routers/conversation.d.ts.map +1 -1
- package/dist/routers/conversation.js +19 -327
- package/dist/routers/conversation.js.map +1 -1
- package/dist/routers/event.d.ts +36 -36
- package/dist/routers/event.d.ts.map +1 -1
- package/dist/routers/event.js +13 -433
- package/dist/routers/event.js.map +1 -1
- package/dist/routers/file.d.ts +2 -2
- package/dist/routers/file.d.ts.map +1 -1
- package/dist/routers/file.js +9 -323
- package/dist/routers/file.js.map +1 -1
- package/dist/routers/folder.d.ts +21 -14
- package/dist/routers/folder.d.ts.map +1 -1
- package/dist/routers/folder.js +34 -745
- package/dist/routers/folder.js.map +1 -1
- package/dist/routers/labChat.d.ts +11 -10
- package/dist/routers/labChat.d.ts.map +1 -1
- package/dist/routers/labChat.js +19 -570
- package/dist/routers/labChat.js.map +1 -1
- package/dist/routers/marketing.d.ts +1 -1
- package/dist/routers/marketing.d.ts.map +1 -1
- package/dist/routers/marketing.js +7 -56
- package/dist/routers/marketing.js.map +1 -1
- package/dist/routers/message.d.ts +2 -2
- package/dist/routers/message.d.ts.map +1 -1
- package/dist/routers/message.js +27 -522
- package/dist/routers/message.js.map +1 -1
- package/dist/routers/newtonChat.d.ts +1 -1
- package/dist/routers/newtonChat.d.ts.map +1 -1
- package/dist/routers/newtonChat.js +7 -246
- package/dist/routers/newtonChat.js.map +1 -1
- package/dist/routers/notifications.d.ts +4 -4
- package/dist/routers/notifications.d.ts.map +1 -1
- package/dist/routers/notifications.js +18 -83
- package/dist/routers/notifications.js.map +1 -1
- package/dist/routers/section.d.ts +4 -4
- package/dist/routers/section.d.ts.map +1 -1
- package/dist/routers/section.js +14 -286
- package/dist/routers/section.js.map +1 -1
- package/dist/routers/user.d.ts +1 -1
- package/dist/routers/user.d.ts.map +1 -1
- package/dist/routers/user.js +32 -207
- package/dist/routers/user.js.map +1 -1
- package/dist/routers/worksheet.d.ts +51 -38
- package/dist/routers/worksheet.d.ts.map +1 -1
- package/dist/routers/worksheet.js +79 -394
- package/dist/routers/worksheet.js.map +1 -1
- package/dist/seedDatabase.d.ts +1 -1
- package/dist/server/pipelines/gradeWorksheet.d.ts +6 -6
- package/dist/server/pipelines/gradeWorksheet.d.ts.map +1 -1
- package/dist/server/pipelines/gradeWorksheet.js +12 -5
- package/dist/server/pipelines/gradeWorksheet.js.map +1 -1
- package/dist/services/agenda.d.ts +100 -0
- package/dist/services/agenda.d.ts.map +1 -0
- package/dist/services/agenda.js +21 -0
- package/dist/services/agenda.js.map +1 -0
- package/dist/services/announcement.d.ts +135 -0
- package/dist/services/announcement.d.ts.map +1 -0
- package/dist/services/announcement.js +223 -0
- package/dist/services/announcement.js.map +1 -0
- package/dist/services/assignment.d.ts +1462 -0
- package/dist/services/assignment.d.ts.map +1 -0
- package/dist/services/assignment.js +898 -0
- package/dist/services/assignment.js.map +1 -0
- package/dist/services/attendance.d.ts +93 -0
- package/dist/services/attendance.d.ts.map +1 -0
- package/dist/services/attendance.js +61 -0
- package/dist/services/attendance.js.map +1 -0
- package/dist/services/auth.d.ts +68 -0
- package/dist/services/auth.d.ts.map +1 -0
- package/dist/services/auth.js +218 -0
- package/dist/services/auth.js.map +1 -0
- package/dist/services/class.d.ts +621 -0
- package/dist/services/class.d.ts.map +1 -0
- package/dist/services/class.js +474 -0
- package/dist/services/class.js.map +1 -0
- package/dist/services/comment.d.ts +100 -0
- package/dist/services/comment.d.ts.map +1 -0
- package/dist/services/comment.js +83 -0
- package/dist/services/comment.js.map +1 -0
- package/dist/services/conversation.d.ts +159 -0
- package/dist/services/conversation.d.ts.map +1 -0
- package/dist/services/conversation.js +138 -0
- package/dist/services/conversation.js.map +1 -0
- package/dist/services/event.d.ts +216 -0
- package/dist/services/event.d.ts.map +1 -0
- package/dist/services/event.js +168 -0
- package/dist/services/event.js.map +1 -0
- package/dist/services/file.d.ts +74 -0
- package/dist/services/file.d.ts.map +1 -0
- package/dist/services/file.js +133 -0
- package/dist/services/file.js.map +1 -0
- package/dist/services/folder.d.ts +239 -0
- package/dist/services/folder.d.ts.map +1 -0
- package/dist/services/folder.js +248 -0
- package/dist/services/folder.js.map +1 -0
- package/dist/services/labChat.d.ts +165 -0
- package/dist/services/labChat.d.ts.map +1 -0
- package/dist/services/labChat.js +289 -0
- package/dist/services/labChat.js.map +1 -0
- package/dist/services/marketing.d.ts +50 -0
- package/dist/services/marketing.d.ts.map +1 -0
- package/dist/services/marketing.js +32 -0
- package/dist/services/marketing.js.map +1 -0
- package/dist/services/message.d.ts +95 -0
- package/dist/services/message.d.ts.map +1 -0
- package/dist/services/message.js +350 -0
- package/dist/services/message.js.map +1 -0
- package/dist/services/newtonChat.d.ts +22 -0
- package/dist/services/newtonChat.d.ts.map +1 -0
- package/dist/services/newtonChat.js +174 -0
- package/dist/services/newtonChat.js.map +1 -0
- package/dist/services/notification.d.ts +65 -0
- package/dist/services/notification.d.ts.map +1 -0
- package/dist/services/notification.js +33 -0
- package/dist/services/notification.js.map +1 -0
- package/dist/services/section.d.ts +53 -0
- package/dist/services/section.d.ts.map +1 -0
- package/dist/services/section.js +199 -0
- package/dist/services/section.js.map +1 -0
- package/dist/services/user.d.ts +48 -0
- package/dist/services/user.d.ts.map +1 -0
- package/dist/services/user.js +141 -0
- package/dist/services/user.js.map +1 -0
- package/dist/services/worksheet.d.ts +239 -0
- package/dist/services/worksheet.d.ts.map +1 -0
- package/dist/services/worksheet.js +235 -0
- package/dist/services/worksheet.js.map +1 -0
- package/dist/utils/aiUser.d.ts +1 -3
- package/dist/utils/aiUser.d.ts.map +1 -1
- package/dist/utils/aiUser.js +6 -5
- package/dist/utils/aiUser.js.map +1 -1
- package/dist/utils/email.d.ts +3 -0
- package/dist/utils/email.d.ts.map +1 -1
- package/dist/utils/email.js +7 -4
- package/dist/utils/email.js.map +1 -1
- package/dist/utils/generateInviteCode.d.ts +1 -2
- package/dist/utils/generateInviteCode.d.ts.map +1 -1
- package/dist/utils/generateInviteCode.js +3 -4
- package/dist/utils/generateInviteCode.js.map +1 -1
- package/dist/utils/inference.d.ts +3 -0
- package/dist/utils/inference.d.ts.map +1 -1
- package/dist/utils/inference.js +7 -4
- package/dist/utils/inference.js.map +1 -1
- package/dist/utils/logger.d.ts +3 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +5 -2
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/prismaErrorHandler.d.ts.map +1 -1
- package/dist/utils/prismaErrorHandler.js +5 -2
- package/dist/utils/prismaErrorHandler.js.map +1 -1
- package/dist/utils/prismaWrapper.d.ts +1 -0
- package/dist/utils/prismaWrapper.d.ts.map +1 -1
- package/dist/utils/prismaWrapper.js +6 -2
- package/dist/utils/prismaWrapper.js.map +1 -1
- package/docker-compose.yml +5 -0
- package/package.json +4 -3
- package/src/index.ts +119 -12
- package/src/lib/config/env.ts +6 -0
- package/src/lib/fileUpload.ts +0 -1
- package/src/lib/googleCloudStorage.ts +17 -0
- package/src/lib/pusher.ts +5 -1
- package/src/lib/redis.ts +56 -0
- package/src/lib/thumbnailGenerator.ts +170 -168
- package/src/middleware/auth.ts +80 -137
- package/src/models/agenda.ts +46 -0
- package/src/models/announcement.ts +134 -0
- package/src/models/assignment.ts +322 -0
- package/src/models/attendance.ts +208 -0
- package/src/models/auth.ts +247 -0
- package/src/models/class.ts +598 -0
- package/src/models/comment.ts +152 -0
- package/src/models/conversation.ts +200 -0
- package/src/models/event.ts +177 -0
- package/src/models/file.ts +129 -0
- package/src/models/folder.ts +225 -0
- package/src/models/labChat.ts +213 -0
- package/src/models/marketing.ts +45 -0
- package/src/models/message.ts +153 -0
- package/src/models/newtonChat.ts +70 -0
- package/src/models/notification.ts +54 -0
- package/src/models/section.ts +98 -0
- package/src/models/user.ts +47 -0
- package/src/models/worksheet.ts +294 -0
- package/src/{server/pipelines → pipelines}/aiLabChat.ts +11 -7
- package/src/{server/pipelines → pipelines}/aiNewtonChat.ts +9 -5
- package/src/{server/pipelines → pipelines}/gradeWorksheet.ts +25 -14
- package/src/routers/agenda.ts +3 -66
- package/src/routers/announcement.ts +54 -495
- package/src/routers/assignment.ts +126 -2018
- package/src/routers/attendance.ts +15 -276
- package/src/routers/auth.ts +79 -442
- package/src/routers/class.ts +263 -1187
- package/src/routers/comment.ts +61 -288
- package/src/routers/conversation.ts +51 -360
- package/src/routers/event.ts +50 -481
- package/src/routers/file.ts +45 -368
- package/src/routers/folder.ts +107 -836
- package/src/routers/labChat.ts +29 -605
- package/src/routers/marketing.ts +35 -77
- package/src/routers/message.ts +45 -571
- package/src/routers/newtonChat.ts +17 -278
- package/src/routers/notifications.ts +32 -82
- package/src/routers/section.ts +46 -330
- package/src/routers/user.ts +49 -227
- package/src/routers/worksheet.ts +215 -503
- package/src/services/agenda.ts +21 -0
- package/src/services/announcement.ts +290 -0
- package/src/services/assignment.ts +1198 -0
- package/src/services/attendance.ts +85 -0
- package/src/services/auth.ts +277 -0
- package/src/services/class.ts +622 -0
- package/src/services/comment.ts +106 -0
- package/src/services/conversation.ts +213 -0
- package/src/services/event.ts +231 -0
- package/src/services/file.ts +167 -0
- package/src/services/folder.ts +316 -0
- package/src/services/labChat.ts +352 -0
- package/src/services/marketing.ts +57 -0
- package/src/services/message.ts +461 -0
- package/src/services/newtonChat.ts +222 -0
- package/src/services/notification.ts +50 -0
- package/src/services/section.ts +283 -0
- package/src/services/user.ts +172 -0
- package/src/services/worksheet.ts +358 -0
- package/src/utils/aiUser.ts +4 -3
- package/src/utils/email.ts +5 -3
- package/src/utils/generateInviteCode.ts +1 -3
- package/src/utils/inference.ts +5 -2
- package/src/utils/logger.ts +3 -1
- package/src/utils/prismaErrorHandler.ts +3 -0
- package/src/utils/prismaWrapper.ts +4 -0
- package/tests/globalSetup.ts +62 -0
- package/tests/helpers.ts +22 -0
- package/tests/middleware/security.test.ts +42 -0
- package/tests/routers/agenda.test.ts +138 -0
- package/tests/routers/announcement.test.ts +490 -0
- package/tests/routers/assignment.test.ts +837 -0
- package/tests/{attendance.test.ts → routers/attendance.test.ts} +6 -14
- package/tests/routers/auth.test.ts +171 -0
- package/tests/{class.test.ts → routers/class.test.ts} +131 -85
- package/tests/routers/comment.test.ts +126 -0
- package/tests/routers/conversation.test.ts +145 -0
- package/tests/{event.test.ts → routers/event.test.ts} +93 -32
- package/tests/routers/folder.test.ts +178 -0
- package/tests/routers/labChat.test.ts +115 -0
- package/tests/routers/marketing.test.ts +59 -0
- package/tests/routers/message.test.ts +123 -0
- package/tests/routers/notification.test.ts +69 -0
- package/tests/{section.test.ts → routers/section.test.ts} +5 -13
- package/tests/server/rateLimit.test.ts +73 -0
- package/tests/setup.ts +18 -92
- package/tests/user.test.ts +9 -31
- package/tests/utils/aiUser.test.ts +22 -0
- package/tests/utils/generateInviteCode.test.ts +24 -0
- package/tests/utils/logger.test.ts +74 -0
- package/tests/utils/prismaErrorHandler.test.ts +101 -0
- package/tests/utils/prismaWrapper.test.ts +82 -0
- package/tests/worksheet.test.ts +181 -0
- package/vitest.config.ts +6 -3
- package/vitest.unit.config.ts +21 -0
- package/TODO.md +0 -2
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/clover.xml +0 -12110
- package/coverage/coverage-final.json +0 -44
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -221
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/server/index.html +0 -116
- package/coverage/server/src/exportType.ts.html +0 -109
- package/coverage/server/src/index.html +0 -161
- package/coverage/server/src/index.ts.html +0 -1702
- package/coverage/server/src/instrument.ts.html +0 -130
- package/coverage/server/src/lib/config/env.ts.html +0 -448
- package/coverage/server/src/lib/config/index.html +0 -116
- package/coverage/server/src/lib/fileUpload.ts.html +0 -1138
- package/coverage/server/src/lib/googleCloudStorage.ts.html +0 -334
- package/coverage/server/src/lib/index.html +0 -206
- package/coverage/server/src/lib/jsonConversion.ts.html +0 -2323
- package/coverage/server/src/lib/jsonStyles.ts.html +0 -193
- package/coverage/server/src/lib/notificationHandler.ts.html +0 -193
- package/coverage/server/src/lib/pusher.ts.html +0 -121
- package/coverage/server/src/lib/thumbnailGenerator.ts.html +0 -592
- package/coverage/server/src/middleware/auth.ts.html +0 -646
- package/coverage/server/src/middleware/index.html +0 -146
- package/coverage/server/src/middleware/logging.ts.html +0 -244
- package/coverage/server/src/middleware/security.ts.html +0 -271
- package/coverage/server/src/routers/_app.ts.html +0 -232
- package/coverage/server/src/routers/agenda.ts.html +0 -319
- package/coverage/server/src/routers/announcement.ts.html +0 -3481
- package/coverage/server/src/routers/assignment.ts.html +0 -7633
- package/coverage/server/src/routers/attendance.ts.html +0 -1030
- package/coverage/server/src/routers/auth.ts.html +0 -1081
- package/coverage/server/src/routers/class.ts.html +0 -3535
- package/coverage/server/src/routers/comment.ts.html +0 -991
- package/coverage/server/src/routers/conversation.ts.html +0 -982
- package/coverage/server/src/routers/event.ts.html +0 -1609
- package/coverage/server/src/routers/file.ts.html +0 -1144
- package/coverage/server/src/routers/folder.ts.html +0 -2797
- package/coverage/server/src/routers/index.html +0 -386
- package/coverage/server/src/routers/labChat.ts.html +0 -3073
- package/coverage/server/src/routers/marketing.ts.html +0 -340
- package/coverage/server/src/routers/message.ts.html +0 -1912
- package/coverage/server/src/routers/notifications.ts.html +0 -364
- package/coverage/server/src/routers/section.ts.html +0 -1120
- package/coverage/server/src/routers/user.ts.html +0 -862
- package/coverage/server/src/routers/worksheet.ts.html +0 -1729
- package/coverage/server/src/trpc.ts.html +0 -397
- package/coverage/server/src/types/index.html +0 -116
- package/coverage/server/src/types/trpc.ts.html +0 -127
- package/coverage/server/src/utils/aiUser.ts.html +0 -280
- package/coverage/server/src/utils/email.ts.html +0 -121
- package/coverage/server/src/utils/generateInviteCode.ts.html +0 -106
- package/coverage/server/src/utils/index.html +0 -206
- package/coverage/server/src/utils/inference.ts.html +0 -709
- package/coverage/server/src/utils/logger.ts.html +0 -664
- package/coverage/server/src/utils/prismaErrorHandler.ts.html +0 -907
- package/coverage/server/src/utils/prismaWrapper.ts.html +0 -355
- package/coverage/server/vitest.config.ts.html +0 -196
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -210
- package/src/lib/notificationHandler.ts +0 -36
- package/tests/announcement.test.ts +0 -164
- package/tests/assignment.test.ts +0 -296
- package/tests/auth.test.ts +0 -48
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { test, expect, describe, beforeEach } from 'vitest';
|
|
2
|
-
import { user1Caller, user2Caller } from '
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { prisma } from '../src/lib/prisma';
|
|
2
|
+
import { user1Caller, user2Caller } from '../setup';
|
|
3
|
+
import { expectUnauth } from '../helpers';
|
|
4
|
+
import { prisma } from '../../src/lib/prisma';
|
|
6
5
|
|
|
7
6
|
describe('Attendance Router', () => {
|
|
8
7
|
let testClass: any;
|
|
@@ -147,22 +146,15 @@ describe('Attendance Router', () => {
|
|
|
147
146
|
})).rejects.toThrow();
|
|
148
147
|
});
|
|
149
148
|
|
|
150
|
-
test('should fail without authentication',
|
|
151
|
-
|
|
152
|
-
req: { headers: {} } as any,
|
|
153
|
-
res: {} as any,
|
|
154
|
-
});
|
|
155
|
-
const router = appRouter.createCaller(invalidCaller);
|
|
156
|
-
|
|
157
|
-
await expect(router.attendance.update({
|
|
149
|
+
test('should fail without authentication', () =>
|
|
150
|
+
expectUnauth((c) => c.attendance.update({
|
|
158
151
|
classId: testClass.id,
|
|
159
152
|
attendance: {
|
|
160
153
|
present: [],
|
|
161
154
|
late: [],
|
|
162
155
|
absent: [],
|
|
163
156
|
},
|
|
164
|
-
}))
|
|
165
|
-
});
|
|
157
|
+
})));
|
|
166
158
|
});
|
|
167
159
|
});
|
|
168
160
|
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// @note: some sloppy impl
|
|
2
|
+
|
|
3
|
+
import { test, expect, describe } from 'vitest';
|
|
4
|
+
import { appRouter } from '../../src/routers/_app';
|
|
5
|
+
import { createTRPCContext } from '../../src/trpc';
|
|
6
|
+
import { prisma } from '../../src/lib/prisma';
|
|
7
|
+
import { caller, login1, login2, login3, session1, session2, session3, user1Caller, user2Caller, user3Caller, verification1, verification2, verification3 } from '../setup';
|
|
8
|
+
import { expectUnauth } from '../helpers';
|
|
9
|
+
|
|
10
|
+
describe('Auth Router', () => {
|
|
11
|
+
test('check returns user when authenticated', async () => {
|
|
12
|
+
const result = await user1Caller.auth.check();
|
|
13
|
+
expect(result.user).toBeDefined();
|
|
14
|
+
expect(result.user.id).toBeDefined();
|
|
15
|
+
expect(result.user.username).toBe('testuser1');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('check fails when unauthenticated', () =>
|
|
19
|
+
expectUnauth((c) => c.auth.check()));
|
|
20
|
+
|
|
21
|
+
test('resendVerificationEmail succeeds for existing user', async () => {
|
|
22
|
+
await caller.auth.register({
|
|
23
|
+
username: 'resendtest',
|
|
24
|
+
email: 'resend@studious.sh',
|
|
25
|
+
password: 'password_is_1234',
|
|
26
|
+
confirmPassword: 'password_is_1234',
|
|
27
|
+
});
|
|
28
|
+
const result = await caller.auth.resendVerificationEmail({
|
|
29
|
+
email: 'resend@studious.sh',
|
|
30
|
+
});
|
|
31
|
+
expect(result).toBeDefined();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('requestPasswordReset succeeds', async () => {
|
|
35
|
+
const result = await caller.auth.requestPasswordReset({
|
|
36
|
+
email: 'test@studious.sh',
|
|
37
|
+
});
|
|
38
|
+
expect(result).toBeDefined();
|
|
39
|
+
expect(result.success).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('resetPassword works with valid token', async () => {
|
|
43
|
+
await caller.auth.requestPasswordReset({ email: 'test@studious.sh' });
|
|
44
|
+
// Reset tokens expire within 2hr; login sessions expire in 30 days
|
|
45
|
+
const twoHoursFromNow = new Date(Date.now() + 2 * 60 * 60 * 1000);
|
|
46
|
+
const resetSession = await prisma.session.findFirst({
|
|
47
|
+
where: {
|
|
48
|
+
userId: login1.user.id,
|
|
49
|
+
classId: null,
|
|
50
|
+
expiresAt: { lte: twoHoursFromNow },
|
|
51
|
+
createdAt: { gte: new Date(Date.now() - 60_000) }, // created in last minute
|
|
52
|
+
},
|
|
53
|
+
orderBy: { createdAt: 'desc' },
|
|
54
|
+
});
|
|
55
|
+
if (!resetSession) throw new Error('No reset token found');
|
|
56
|
+
|
|
57
|
+
const result = await caller.auth.resetPassword({
|
|
58
|
+
token: resetSession.id,
|
|
59
|
+
password: 'new_password_123',
|
|
60
|
+
confirmPassword: 'new_password_123',
|
|
61
|
+
});
|
|
62
|
+
expect(result).toBeDefined();
|
|
63
|
+
|
|
64
|
+
const login = await caller.auth.login({
|
|
65
|
+
username: 'testuser1',
|
|
66
|
+
password: 'new_password_123',
|
|
67
|
+
});
|
|
68
|
+
expect(login.token).toBeDefined();
|
|
69
|
+
|
|
70
|
+
await caller.auth.requestPasswordReset({ email: 'test@studious.sh' });
|
|
71
|
+
const restoreSession = await prisma.session.findFirst({
|
|
72
|
+
where: {
|
|
73
|
+
userId: login1.user.id,
|
|
74
|
+
classId: null,
|
|
75
|
+
expiresAt: { lte: twoHoursFromNow },
|
|
76
|
+
createdAt: { gte: new Date(Date.now() - 60_000) },
|
|
77
|
+
},
|
|
78
|
+
orderBy: { createdAt: 'desc' },
|
|
79
|
+
});
|
|
80
|
+
if (!restoreSession) throw new Error('No reset token found');
|
|
81
|
+
await caller.auth.resetPassword({
|
|
82
|
+
token: restoreSession.id,
|
|
83
|
+
password: 'password_is_1234',
|
|
84
|
+
confirmPassword: 'password_is_1234',
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('registration creates sessions', async () => {
|
|
89
|
+
expect(session1).toBeDefined();
|
|
90
|
+
expect(session1.id).toBeDefined();
|
|
91
|
+
expect(session2).toBeDefined();
|
|
92
|
+
expect(session2.id).toBeDefined();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('email verification works', async () => {
|
|
96
|
+
expect(verification1).toBeDefined();
|
|
97
|
+
expect(verification2).toBeDefined();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('login returns valid tokens', async () => {
|
|
101
|
+
expect(login1).toBeDefined();
|
|
102
|
+
expect(login1.token).toBeDefined();
|
|
103
|
+
expect(login1.user).toBeDefined();
|
|
104
|
+
expect(login2).toBeDefined();
|
|
105
|
+
expect(login2.token).toBeDefined();
|
|
106
|
+
expect(login2.user).toBeDefined();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('login fails with invalid username', async () => {
|
|
110
|
+
await expect(
|
|
111
|
+
caller.auth.login({ username: 'nonexistent', password: 'any' })
|
|
112
|
+
).rejects.toThrow();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('login fails with wrong password', async () => {
|
|
116
|
+
await expect(
|
|
117
|
+
caller.auth.login({ username: 'testuser1', password: 'wrongpassword' })
|
|
118
|
+
).rejects.toThrow();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('register fails when username already exists', async () => {
|
|
122
|
+
await expect(
|
|
123
|
+
caller.auth.register({
|
|
124
|
+
username: 'testuser1',
|
|
125
|
+
email: 'newemail@studious.sh',
|
|
126
|
+
password: 'password_is_1234',
|
|
127
|
+
confirmPassword: 'password_is_1234',
|
|
128
|
+
})
|
|
129
|
+
).rejects.toThrow();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test('register fails when email already exists', async () => {
|
|
133
|
+
await expect(
|
|
134
|
+
caller.auth.register({
|
|
135
|
+
username: 'newuser',
|
|
136
|
+
email: 'test@studious.sh',
|
|
137
|
+
password: 'password_is_1234',
|
|
138
|
+
confirmPassword: 'password_is_1234',
|
|
139
|
+
})
|
|
140
|
+
).rejects.toThrow();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test('verify fails with invalid token', async () => {
|
|
144
|
+
await expect(
|
|
145
|
+
caller.auth.verify({ token: 'invalid-token-12345' })
|
|
146
|
+
).rejects.toThrow();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test('resetPassword fails with invalid token', async () => {
|
|
150
|
+
await expect(
|
|
151
|
+
caller.auth.resetPassword({
|
|
152
|
+
token: 'invalid-token-12345',
|
|
153
|
+
password: 'newpass123',
|
|
154
|
+
confirmPassword: 'newpass123',
|
|
155
|
+
})
|
|
156
|
+
).rejects.toThrow();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('logout invalidates session', async () => {
|
|
160
|
+
const logout = await user3Caller.auth.logout();
|
|
161
|
+
expect(logout).toBeDefined();
|
|
162
|
+
expect(logout.success).toBe(true);
|
|
163
|
+
|
|
164
|
+
const ctx = await createTRPCContext({
|
|
165
|
+
req: { headers: { authorization: `Bearer ${login3.token}`, 'x-user': login3.token } } as any,
|
|
166
|
+
res: {} as any,
|
|
167
|
+
});
|
|
168
|
+
const router = appRouter.createCaller(ctx);
|
|
169
|
+
await expect(router.user.getProfile()).rejects.toThrow('Invalid or expired session');
|
|
170
|
+
});
|
|
171
|
+
});
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import { test, expect, describe, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
1
|
+
import { test, expect, describe, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import { expectForbidden, expectRejects, expectUnauth } from '../helpers';
|
|
3
|
+
import { prisma } from '../../src/lib/prisma';
|
|
4
|
+
import { caller, user1Caller, user2Caller, user3Caller } from '../setup';
|
|
5
|
+
|
|
6
|
+
vi.mock('../../src/lib/googleCloudStorage.js', async (importOriginal) => {
|
|
7
|
+
const actual = await importOriginal<typeof import('../../src/lib/googleCloudStorage.js')>();
|
|
8
|
+
return {
|
|
9
|
+
...actual,
|
|
10
|
+
copyFile: vi.fn().mockResolvedValue(undefined),
|
|
11
|
+
};
|
|
12
|
+
});
|
|
6
13
|
|
|
7
14
|
describe('Class Router', () => {
|
|
8
15
|
let testClass: any;
|
|
@@ -44,28 +51,19 @@ describe('Class Router', () => {
|
|
|
44
51
|
expect(testClass.id).not.toBe(testClass2.id);
|
|
45
52
|
});
|
|
46
53
|
|
|
47
|
-
test('should fail to create class without authentication',
|
|
48
|
-
|
|
49
|
-
req: { headers: {} } as any,
|
|
50
|
-
res: {} as any,
|
|
51
|
-
});
|
|
52
|
-
const router = appRouter.createCaller(invalidCaller);
|
|
53
|
-
|
|
54
|
-
await expect(router.class.create({
|
|
54
|
+
test('should fail to create class without authentication', () =>
|
|
55
|
+
expectUnauth((c) => c.class.create({
|
|
55
56
|
name: 'Test Class',
|
|
56
57
|
subject: 'Mathematics',
|
|
57
58
|
section: '10th Grade',
|
|
58
|
-
}))
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test('should fail to create class with missing required fields', async () => {
|
|
59
|
+
})));
|
|
62
60
|
|
|
61
|
+
test('should fail to create class with missing required fields', () =>
|
|
63
62
|
// @ts-expect-error - test case
|
|
64
|
-
|
|
63
|
+
expectRejects(() => user1Caller.class.create({
|
|
65
64
|
name: '',
|
|
66
65
|
color: '#3B82F6',
|
|
67
|
-
}))
|
|
68
|
-
});
|
|
66
|
+
})));
|
|
69
67
|
});
|
|
70
68
|
|
|
71
69
|
describe('getAll', () => {
|
|
@@ -85,15 +83,8 @@ describe('Class Router', () => {
|
|
|
85
83
|
expect(userClass?.id).toBe(testClass.id);
|
|
86
84
|
});
|
|
87
85
|
|
|
88
|
-
test('should fail to get classes without authentication',
|
|
89
|
-
|
|
90
|
-
req: { headers: {} } as any,
|
|
91
|
-
res: {} as any,
|
|
92
|
-
});
|
|
93
|
-
const router = appRouter.createCaller(invalidCaller);
|
|
94
|
-
|
|
95
|
-
await expect(router.class.getAll()).rejects.toThrow();
|
|
96
|
-
});
|
|
86
|
+
test('should fail to get classes without authentication', () =>
|
|
87
|
+
expectUnauth((c) => c.class.getAll()));
|
|
97
88
|
});
|
|
98
89
|
|
|
99
90
|
describe('get', () => {
|
|
@@ -105,19 +96,11 @@ describe('Class Router', () => {
|
|
|
105
96
|
expect(classData.class.name).toBe('Test Class 1');
|
|
106
97
|
});
|
|
107
98
|
|
|
108
|
-
test('should fail to get non-existent class',
|
|
109
|
-
|
|
110
|
-
});
|
|
99
|
+
test('should fail to get non-existent class', () =>
|
|
100
|
+
expectRejects(() => user1Caller.class.get({ classId: 'non-existent-id' })));
|
|
111
101
|
|
|
112
|
-
test('should fail to get class without authentication',
|
|
113
|
-
|
|
114
|
-
req: { headers: {} } as any,
|
|
115
|
-
res: {} as any,
|
|
116
|
-
});
|
|
117
|
-
const router = appRouter.createCaller(invalidCaller);
|
|
118
|
-
|
|
119
|
-
await expect(router.class.get({ classId: testClass.id })).rejects.toThrow();
|
|
120
|
-
});
|
|
102
|
+
test('should fail to get class without authentication', () =>
|
|
103
|
+
expectUnauth((c) => c.class.get({ classId: testClass.id })));
|
|
121
104
|
});
|
|
122
105
|
|
|
123
106
|
describe('update', () => {
|
|
@@ -136,23 +119,21 @@ describe('Class Router', () => {
|
|
|
136
119
|
expect(updatedClass.updatedClass.section).toBe('12th Grade');
|
|
137
120
|
});
|
|
138
121
|
|
|
139
|
-
test('should fail to update class user is not teacher of',
|
|
140
|
-
|
|
122
|
+
test('should fail to update class user is not teacher of', () =>
|
|
123
|
+
expectForbidden(() => user2Caller.class.update({
|
|
141
124
|
classId: testClass.id,
|
|
142
125
|
name: 'Updated Test Class',
|
|
143
126
|
subject: 'Physics',
|
|
144
127
|
section: '12th Grade',
|
|
145
|
-
}))
|
|
146
|
-
});
|
|
128
|
+
})));
|
|
147
129
|
|
|
148
|
-
test('should fail to update non-existent class',
|
|
149
|
-
|
|
130
|
+
test('should fail to update non-existent class', () =>
|
|
131
|
+
expectRejects(() => user1Caller.class.update({
|
|
150
132
|
classId: 'non-existent-id',
|
|
151
133
|
name: 'Updated Test Class',
|
|
152
134
|
subject: 'Physics',
|
|
153
135
|
section: '12th Grade',
|
|
154
|
-
}))
|
|
155
|
-
});
|
|
136
|
+
})));
|
|
156
137
|
});
|
|
157
138
|
|
|
158
139
|
describe('delete', () => {
|
|
@@ -169,13 +150,11 @@ describe('Class Router', () => {
|
|
|
169
150
|
}
|
|
170
151
|
});
|
|
171
152
|
|
|
172
|
-
test('should fail to delete class user is not teacher of',
|
|
173
|
-
|
|
174
|
-
});
|
|
153
|
+
test('should fail to delete class user is not teacher of', () =>
|
|
154
|
+
expectForbidden(() => user2Caller.class.delete({ classId: testClass.id, id: testClass.id })));
|
|
175
155
|
|
|
176
|
-
test('should fail to delete non-existent class',
|
|
177
|
-
|
|
178
|
-
});
|
|
156
|
+
test('should fail to delete non-existent class', () =>
|
|
157
|
+
expectRejects(() => user1Caller.class.delete({ classId: 'non-existent-id', id: 'non-existent-id' })));
|
|
179
158
|
});
|
|
180
159
|
|
|
181
160
|
describe('addStudent', () => {
|
|
@@ -190,13 +169,14 @@ describe('Class Router', () => {
|
|
|
190
169
|
expect(result.newStudent).toBeDefined();
|
|
191
170
|
});
|
|
192
171
|
|
|
193
|
-
test('should fail to add student if user is not class teacher',
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
172
|
+
test('should fail to add student if user is not class teacher', () =>
|
|
173
|
+
expectForbidden(async () => {
|
|
174
|
+
const user1Profile = await user1Caller.user.getProfile();
|
|
175
|
+
return user2Caller.class.addStudent({
|
|
176
|
+
classId: testClass.id,
|
|
177
|
+
studentId: user1Profile.id,
|
|
178
|
+
});
|
|
179
|
+
}));
|
|
200
180
|
});
|
|
201
181
|
|
|
202
182
|
describe('changeRole', () => {
|
|
@@ -213,14 +193,15 @@ describe('Class Router', () => {
|
|
|
213
193
|
expect(result.user.type).toBe('teacher');
|
|
214
194
|
});
|
|
215
195
|
|
|
216
|
-
test('should fail to change role if user is not class teacher',
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
196
|
+
test('should fail to change role if user is not class teacher', () =>
|
|
197
|
+
expectForbidden(async () => {
|
|
198
|
+
const user1Profile = await user1Caller.user.getProfile();
|
|
199
|
+
return user2Caller.class.changeRole({
|
|
200
|
+
classId: testClass.id,
|
|
201
|
+
userId: user1Profile.id,
|
|
202
|
+
type: 'teacher',
|
|
203
|
+
});
|
|
204
|
+
}));
|
|
224
205
|
});
|
|
225
206
|
|
|
226
207
|
describe('removeMember', () => {
|
|
@@ -244,13 +225,14 @@ describe('Class Router', () => {
|
|
|
244
225
|
expect(result.removedUserId).toBe(user2Profile.id);
|
|
245
226
|
});
|
|
246
227
|
|
|
247
|
-
test('should fail to remove member if user is not class teacher',
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
228
|
+
test('should fail to remove member if user is not class teacher', () =>
|
|
229
|
+
expectForbidden(async () => {
|
|
230
|
+
const user1Profile = await user1Caller.user.getProfile();
|
|
231
|
+
return user2Caller.class.removeMember({
|
|
232
|
+
classId: testClass.id,
|
|
233
|
+
userId: user1Profile.id,
|
|
234
|
+
});
|
|
235
|
+
}));
|
|
254
236
|
});
|
|
255
237
|
|
|
256
238
|
describe('join', () => {
|
|
@@ -273,9 +255,8 @@ describe('Class Router', () => {
|
|
|
273
255
|
});
|
|
274
256
|
});
|
|
275
257
|
|
|
276
|
-
test('should fail to join with invalid class code',
|
|
277
|
-
|
|
278
|
-
});
|
|
258
|
+
test('should fail to join with invalid class code', () =>
|
|
259
|
+
expectRejects(() => user2Caller.class.join({ classCode: 'invalid-code' })));
|
|
279
260
|
});
|
|
280
261
|
|
|
281
262
|
describe('getInviteCode', () => {
|
|
@@ -286,9 +267,8 @@ describe('Class Router', () => {
|
|
|
286
267
|
expect(typeof result.code).toBe('string');
|
|
287
268
|
});
|
|
288
269
|
|
|
289
|
-
test('should fail to get invite code if user is not class teacher',
|
|
290
|
-
|
|
291
|
-
});
|
|
270
|
+
test('should fail to get invite code if user is not class teacher', () =>
|
|
271
|
+
expectForbidden(() => user2Caller.class.getInviteCode({ classId: testClass.id })));
|
|
292
272
|
});
|
|
293
273
|
|
|
294
274
|
describe('createInviteCode', () => {
|
|
@@ -299,8 +279,74 @@ describe('Class Router', () => {
|
|
|
299
279
|
expect(typeof result.code).toBe('string');
|
|
300
280
|
});
|
|
301
281
|
|
|
302
|
-
test('should fail to create invite code if user is not class teacher',
|
|
303
|
-
|
|
282
|
+
test('should fail to create invite code if user is not class teacher', () =>
|
|
283
|
+
expectForbidden(() => user2Caller.class.createInviteCode({ classId: testClass.id })));
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe('exportClass', () => {
|
|
287
|
+
test('teacher can export class', async () => {
|
|
288
|
+
// Ensure class has classFiles (root folder) by fetching it
|
|
289
|
+
await user1Caller.folder.getRootFolder({ classId: testClass.id });
|
|
290
|
+
|
|
291
|
+
const exported = await user1Caller.class.exportClass({ classId: testClass.id });
|
|
292
|
+
|
|
293
|
+
expect(exported).toBeDefined();
|
|
294
|
+
expect(exported.id).toBe(testClass.id);
|
|
295
|
+
expect(exported.name).toBe('Test Class 1');
|
|
296
|
+
expect(exported.subject).toBe('Mathematics');
|
|
297
|
+
expect(exported.assignments).toBeDefined();
|
|
298
|
+
expect(Array.isArray(exported.assignments)).toBe(true);
|
|
299
|
+
expect(exported.classFiles).toBeDefined();
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test('non-teacher cannot export class', () =>
|
|
303
|
+
expectForbidden(() => user2Caller.class.exportClass({ classId: testClass.id })));
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
describe('importClass', () => {
|
|
307
|
+
test('teacher can import class with folder structure', async () => {
|
|
308
|
+
await user1Caller.folder.getRootFolder({ classId: testClass.id });
|
|
309
|
+
await user1Caller.folder.create({
|
|
310
|
+
classId: testClass.id,
|
|
311
|
+
name: 'Source Folder',
|
|
312
|
+
color: '#3B82F6',
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const exported = await user1Caller.class.exportClass({ classId: testClass.id });
|
|
316
|
+
const newClass = await user1Caller.class.create({
|
|
317
|
+
name: 'Imported Class',
|
|
318
|
+
subject: 'Mathematics',
|
|
319
|
+
section: '10th Grade',
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const importedId = await user1Caller.class.importClass({
|
|
323
|
+
classId: newClass.id,
|
|
324
|
+
year: new Date().getFullYear(),
|
|
325
|
+
classData: exported,
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
const importedClass = await user1Caller.class.get({ classId: importedId });
|
|
329
|
+
expect(importedClass.class.name).toBe(exported.name);
|
|
330
|
+
expect(importedClass.class.subject).toBe(exported.subject);
|
|
331
|
+
|
|
332
|
+
const rootFolder = await user1Caller.folder.getRootFolder({ classId: importedId });
|
|
333
|
+
expect(rootFolder).toBeDefined();
|
|
334
|
+
expect(rootFolder.name).toBe('Class Files');
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
test('non-teacher cannot import class', async () => {
|
|
339
|
+
await user1Caller.folder.getRootFolder({ classId: testClass.id });
|
|
340
|
+
const exported = await user1Caller.class.exportClass({ classId: testClass.id });
|
|
341
|
+
const newClassId = `import-forbidden-${Date.now()}`;
|
|
342
|
+
|
|
343
|
+
await expect(
|
|
344
|
+
user3Caller.class.importClass({
|
|
345
|
+
classId: newClassId,
|
|
346
|
+
year: new Date().getFullYear(),
|
|
347
|
+
classData: exported,
|
|
348
|
+
})
|
|
349
|
+
).rejects.toThrow();
|
|
304
350
|
});
|
|
305
351
|
});
|
|
306
352
|
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { test, expect, describe, beforeAll } from 'vitest';
|
|
2
|
+
import { user1Caller, user2Caller } from '../setup';
|
|
3
|
+
|
|
4
|
+
describe('Comment Router', () => {
|
|
5
|
+
let testClass: any;
|
|
6
|
+
let announcement: any;
|
|
7
|
+
let commentId: string;
|
|
8
|
+
let replyId: string;
|
|
9
|
+
|
|
10
|
+
beforeAll(async () => {
|
|
11
|
+
testClass = await user1Caller.class.create({
|
|
12
|
+
name: 'Comment Test Class',
|
|
13
|
+
subject: 'History',
|
|
14
|
+
section: '11th Grade',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const result = await user1Caller.announcement.create({
|
|
18
|
+
classId: testClass.id,
|
|
19
|
+
remarks: 'Test announcement for comments',
|
|
20
|
+
});
|
|
21
|
+
announcement = result.announcement;
|
|
22
|
+
|
|
23
|
+
const comment = await user1Caller.announcement.addComment({
|
|
24
|
+
announcementId: announcement.id,
|
|
25
|
+
classId: testClass.id,
|
|
26
|
+
content: 'Test comment on announcement',
|
|
27
|
+
});
|
|
28
|
+
commentId = comment.comment.id;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('get', () => {
|
|
32
|
+
test('should get a comment by ID', async () => {
|
|
33
|
+
const comment = await user1Caller.comment.get({ id: commentId });
|
|
34
|
+
|
|
35
|
+
expect(comment).toBeDefined();
|
|
36
|
+
expect(comment.id).toBe(commentId);
|
|
37
|
+
expect(comment.content).toBe('Test comment on announcement');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('should fail for non-existent comment', async () => {
|
|
41
|
+
await expect(
|
|
42
|
+
user1Caller.comment.get({ id: 'nonexistent-id' }),
|
|
43
|
+
).rejects.toThrow();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('replyToComment', () => {
|
|
48
|
+
test('should reply to a comment', async () => {
|
|
49
|
+
const reply = await user1Caller.comment.replyToComment({
|
|
50
|
+
parentCommentId: commentId,
|
|
51
|
+
content: 'This is a reply',
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
expect(reply).toBeDefined();
|
|
55
|
+
expect(reply.content).toBe('This is a reply');
|
|
56
|
+
expect(reply.parentCommentId).toBe(commentId);
|
|
57
|
+
replyId = reply.id;
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('getReplies', () => {
|
|
62
|
+
test('should get replies to a comment', async () => {
|
|
63
|
+
const replies = await user1Caller.comment.getReplies({
|
|
64
|
+
commentId,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
expect(Array.isArray(replies)).toBe(true);
|
|
68
|
+
expect(replies.length).toBeGreaterThanOrEqual(1);
|
|
69
|
+
expect(replies.some((r) => r.id === replyId)).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('should return empty array for comment with no replies', async () => {
|
|
73
|
+
const replies = await user1Caller.comment.getReplies({
|
|
74
|
+
commentId: replyId,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
expect(replies).toEqual([]);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('reactions', () => {
|
|
82
|
+
test('should add a reaction to a comment', async () => {
|
|
83
|
+
const result = await user1Caller.comment.addReaction({
|
|
84
|
+
id: commentId,
|
|
85
|
+
type: 'HEART',
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(result.reaction).toBeDefined();
|
|
89
|
+
expect(result.reaction.type).toBe('HEART');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('should get reactions for a comment', async () => {
|
|
93
|
+
const result = await user1Caller.comment.getReactions({
|
|
94
|
+
commentId,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
expect(result).toBeDefined();
|
|
98
|
+
expect(result.counts.HEART).toBe(1);
|
|
99
|
+
expect(result.total).toBe(1);
|
|
100
|
+
expect(result.userReaction).toBe('HEART');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('should update reaction type', async () => {
|
|
104
|
+
const result = await user1Caller.comment.addReaction({
|
|
105
|
+
id: commentId,
|
|
106
|
+
type: 'THUMBSUP',
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
expect(result.reaction.type).toBe('THUMBSUP');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('should remove a reaction', async () => {
|
|
113
|
+
const result = await user1Caller.comment.removeReaction({
|
|
114
|
+
commentId,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
expect(result.success).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('should fail to remove non-existent reaction', async () => {
|
|
121
|
+
await expect(
|
|
122
|
+
user1Caller.comment.removeReaction({ commentId }),
|
|
123
|
+
).rejects.toThrow();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|