From 3fae9c27c118b70c5b6146ec8c910b9a58737d16 Mon Sep 17 00:00:00 2001 From: Dasha Date: Sun, 8 Mar 2026 18:11:16 +0100 Subject: [PATCH 1/6] fix: heroku deployment --- package.json | 2 +- .../__tests__/student/student.service.test.ts | 301 ++++++++++++++++++ 2 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 server/__tests__/student/student.service.test.ts diff --git a/package.json b/package.json index dbd8bcbd..a8c8509d 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "setup:client": "cd client && npm run setup && cd ..", "setup:server": "cd server && npm run setup && cd ..", "build:server": "cd server && npm run build && cd ..", - "heroku-postbuild": "npm run setup && npm run build:client && npm run build:server", + "heroku-postbuild": "npm run setup:server && npm run build:client && npm run build:server", "test": "npm run test:client && npm run test:server", "test:client": "cd client && npm run test && cd ..", "test:server": "cd server && npm run test && cd ..", diff --git a/server/__tests__/student/student.service.test.ts b/server/__tests__/student/student.service.test.ts new file mode 100644 index 00000000..ba094351 --- /dev/null +++ b/server/__tests__/student/student.service.test.ts @@ -0,0 +1,301 @@ +import { + createTestStudent, + getFutureDate, +} from "../helpers/test.helpers.js"; +import { StudentTypeDB } from "../../src/db/schemes/types/student.types.js"; +import { + setupTestDatabase, + teardownTestDatabase, + clearTestDatabase, +} from "../setup/database.setup.js"; +import { StudentModel } from "../../src/db/schemes/studentSchema.js"; +import { StudentQuery } from "../../src/repositories/queryRepositories/student.query.js"; +import { StudentCommand } from "../../src/repositories/commandRepositories/student.command.js"; +import { randomUUID } from "node:crypto"; + +describe("Student Service", () => { + let studentQuery: StudentQuery; + let studentCommand: StudentCommand; + let testStudent: StudentTypeDB; + + beforeAll(async () => { + await setupTestDatabase(); + studentQuery = new StudentQuery(); + studentCommand = new StudentCommand(); + }); + + afterAll(async () => { + await teardownTestDatabase(); + }); + + beforeEach(async () => { + await clearTestDatabase(); + testStudent = await createTestStudent(); + }); + + describe("StudentQuery", () => { + it("should find student by id", async () => { + const result = await studentQuery.getStudentById(testStudent.id); + + expect(result).toBeTruthy(); + expect(result?.id).toBe(testStudent.id); + expect(result?.firstName).toBe(testStudent.firstName); + expect(result?.lastName).toBe(testStudent.lastName); + expect(result?.email).toBe(testStudent.email); + }); + + it("should return null for nonexistent student", async () => { + const result = await studentQuery.getStudentById("nonexistent-student"); + expect(result).toBeNull(); + }); + + it("should find student by email", async () => { + const result = await studentQuery.getStudentByEmail(testStudent.email); + + expect(result).toBeTruthy(); + expect(result?.id).toBe(testStudent.id); + expect(result?.email).toBe(testStudent.email); + }); + + it("should return null for nonexistent email", async () => { + const result = await studentQuery.getStudentByEmail("nonexistent@test.com"); + expect(result).toBeNull(); + }); + + it("should be case sensitive for email search", async () => { + const upperCaseEmail = testStudent.email.toUpperCase(); + const result = await studentQuery.getStudentByEmail(upperCaseEmail); + expect(result).toBeNull(); + }); + }); + + describe("StudentCommand", () => { + it("should create new student", async () => { + const newStudentData = { + id: randomUUID(), + firstName: "NewStudent", + lastName: "Test", + email: `newstudent.${Date.now()}@test.com`, + passwordHash: "hashedpassword123", + passwordSalt: "salt123", + profileImageUrl: null, + phoneNumber: null, + address: null, + mainLanguage: null, + role: "student" as const, + authProvider: "local" as const, + googleSub: null, + createdAt: new Date(), + passwordReset: { + tokenHash: null, + expiresAt: null, + }, + }; + + const result = await studentCommand.createStudent(newStudentData); + + expect(result).toBeTruthy(); + expect(result.firstName).toBe("NewStudent"); + expect(result.lastName).toBe("Test"); + expect(result.email).toBe(newStudentData.email); + expect(result.role).toBe("student"); + }); + + it("should update student profile", async () => { + const updates = { + firstName: "UpdatedName", + lastName: "UpdatedLastName", + phoneNumber: "+1234567890", + address: "123 Test Street", + }; + + const result = await studentCommand.updateStudent(testStudent.id, updates); + + expect(result).toBeTruthy(); + expect(result?.firstName).toBe("UpdatedName"); + expect(result?.lastName).toBe("UpdatedLastName"); + expect(result?.phoneNumber).toBe("+1234567890"); + expect(result?.address).toBe("123 Test Street"); + }); + + it("should update profile image", async () => { + const profileImageUrl = "https://example.com/new-profile.jpg"; + + const result = await studentCommand.updateStudent(testStudent.id, { + profileImageUrl, + }); + + expect(result).toBeTruthy(); + expect(result?.profileImageUrl).toBe(profileImageUrl); + }); + + it("should handle partial updates", async () => { + const originalStudent = await studentQuery.getStudentById(testStudent.id); + + const result = await studentCommand.updateStudent(testStudent.id, { + firstName: "OnlyFirstName", + }); + + expect(result).toBeTruthy(); + expect(result?.firstName).toBe("OnlyFirstName"); + expect(result?.lastName).toBe(originalStudent?.lastName); + expect(result?.email).toBe(originalStudent?.email); + }); + + it("should return null when updating nonexistent student", async () => { + const result = await studentCommand.updateStudent("nonexistent-student", { + firstName: "NonExistent", + }); + + expect(result).toBeNull(); + }); + + it("should update password reset token", async () => { + const tokenHash = "reset-token-hash"; + const expiresAt = new Date(Date.now() + 3600000); // 1 hour from now + + const result = await studentCommand.updateStudent(testStudent.id, { + passwordReset: { + tokenHash, + expiresAt, + }, + }); + + expect(result).toBeTruthy(); + expect(result?.passwordReset.tokenHash).toBe(tokenHash); + expect(result?.passwordReset.expiresAt).toEqual(expiresAt); + }); + + it("should clear password reset token", async () => { + // First set a token + await studentCommand.updateStudent(testStudent.id, { + passwordReset: { + tokenHash: "temp-token", + expiresAt: new Date(), + }, + }); + + // Then clear it + const result = await studentCommand.updateStudent(testStudent.id, { + passwordReset: { + tokenHash: null, + expiresAt: null, + }, + }); + + expect(result).toBeTruthy(); + expect(result?.passwordReset.tokenHash).toBeNull(); + expect(result?.passwordReset.expiresAt).toBeNull(); + }); + }); + + describe("StudentModel CRUD Operations", () => { + it("should create student in database", async () => { + const studentData = { + id: randomUUID(), + firstName: "Database", + lastName: "Student", + email: `database.student.${Date.now()}@test.com`, + passwordHash: "hashedpassword123", + passwordSalt: "salt123", + profileImageUrl: null, + phoneNumber: null, + address: null, + mainLanguage: null, + role: "student" as const, + authProvider: "local" as const, + googleSub: null, + createdAt: new Date(), + passwordReset: { + tokenHash: null, + expiresAt: null, + }, + }; + + const created = await StudentModel.create(studentData); + + expect(created.firstName).toBe("Database"); + expect(created.lastName).toBe("Student"); + expect(created.email).toBe(studentData.email); + expect(created.role).toBe("student"); + }); + + it("should find student by email in database", async () => { + const found = await StudentModel.findOne({ email: testStudent.email }).exec(); + + expect(found).toBeTruthy(); + expect(found?.id).toBe(testStudent.id); + expect(found?.email).toBe(testStudent.email); + }); + + it("should update student in database", async () => { + const updated = await StudentModel.findOneAndUpdate( + { id: testStudent.id }, + { firstName: "DatabaseUpdated", phoneNumber: "+9876543210" }, + { new: true } + ).exec(); + + expect(updated?.firstName).toBe("DatabaseUpdated"); + expect(updated?.phoneNumber).toBe("+9876543210"); + }); + + it("should delete student from database", async () => { + await StudentModel.findOneAndDelete({ id: testStudent.id }).exec(); + + const deleted = await StudentModel.findOne({ id: testStudent.id }).exec(); + expect(deleted).toBeNull(); + }); + + it("should handle unique email constraint", async () => { + const duplicateStudentData = { + id: randomUUID(), + firstName: "Duplicate", + lastName: "Student", + email: testStudent.email, // Same email as existing student + passwordHash: "hashedpassword123", + passwordSalt: "salt123", + profileImageUrl: null, + phoneNumber: null, + address: null, + mainLanguage: null, + role: "student" as const, + authProvider: "local" as const, + googleSub: null, + createdAt: new Date(), + passwordReset: { + tokenHash: null, + expiresAt: null, + }, + }; + + await expect(StudentModel.create(duplicateStudentData)).rejects.toThrow(); + }); + }); + + describe("Student Profile Validation", () => { + it("should preserve existing data when partially updating", async () => { + const original = await studentQuery.getStudentById(testStudent.id); + + const result = await studentCommand.updateStudent(testStudent.id, { + firstName: "PartialUpdate", + }); + + expect(result?.firstName).toBe("PartialUpdate"); + expect(result?.lastName).toBe(original?.lastName); + expect(result?.email).toBe(original?.email); + expect(result?.phoneNumber).toBe(original?.phoneNumber); + }); + + it("should handle null values correctly", async () => { + const result = await studentCommand.updateStudent(testStudent.id, { + phoneNumber: null, + address: null, + profileImageUrl: null, + }); + + expect(result?.phoneNumber).toBeNull(); + expect(result?.address).toBeNull(); + expect(result?.profileImageUrl).toBeNull(); + }); + }); +}); \ No newline at end of file From 9d1895cf9d345e549e343d8c62e3078e7a3a5003 Mon Sep 17 00:00:00 2001 From: Dasha Date: Sun, 8 Mar 2026 18:17:15 +0100 Subject: [PATCH 2/6] fix: heroku deployment path --- server/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/package.json b/server/package.json index 5c0872e5..f2af6962 100644 --- a/server/package.json +++ b/server/package.json @@ -2,7 +2,7 @@ "name": "project-server", "version": "1.0.0", "description": "", - "main": "dist/index.js", + "main": "dist/src/index.js", "type": "module", "engines": { "node": ">=24.0.0" @@ -10,7 +10,7 @@ "scripts": { "setup": "npm ci --include=dev", "build": "tsc -p tsconfig.json", - "start": "node ./dist/index.js", + "start": "node ./dist/src/index.js", "dev": "tsx watch ./src/index.ts", "test": "jest", "test:watch": "jest --watch", From 80eb91692861a16c86c13130c896e622e70d28fe Mon Sep 17 00:00:00 2001 From: Dasha Date: Sun, 8 Mar 2026 18:22:23 +0100 Subject: [PATCH 3/6] fix: move vite dependencies --- client/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/package.json b/client/package.json index 7e33937b..6cc50bf8 100644 --- a/client/package.json +++ b/client/package.json @@ -36,6 +36,7 @@ "@stream-io/video-react-sdk": "^1.32.4", "@tailwindcss/vite": "4.1.18", "@tanstack/react-query": "^5.90.20", + "@vitejs/plugin-react": "^5.1.0", "axios": "^1.13.4", "class-variance-authority": "0.7.1", "framer-motion": "^11.18.2", @@ -46,6 +47,8 @@ "socket.io-client": "^4.8.3", "tailwind-merge": "3.4.0", "tailwindcss": "4.1.18", + "typescript": "5.9.3", + "vite": "^7.3", "zod": "4.3.6", "zustand": "^5.0.11" }, @@ -63,7 +66,6 @@ "@types/react-dom": "^19.0.4", "@typescript-eslint/eslint-plugin": "8.54.0", "@typescript-eslint/parser": "8.54.0", - "@vitejs/plugin-react": "^5.1.0", "babel-jest": "30.2.0", "dotenv": "^17.2.3", "eslint": "^9.21.0", @@ -79,8 +81,6 @@ "jest-environment-jsdom": "^30.2.0", "jest-fetch-mock": "^3.0.3", "prettier": "^3.2.5", - "prettier-eslint": "^16.3.0", - "typescript": "5.9.3", - "vite": "^7.3" + "prettier-eslint": "^16.3.0" } } From 9d8dd6e911ae7aa7cf1a815cbc38b594addf5fc5 Mon Sep 17 00:00:00 2001 From: Dasha Date: Sun, 8 Mar 2026 18:30:15 +0100 Subject: [PATCH 4/6] fix: production build configuration --- server/package.json | 4 ++-- server/tsconfig.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/package.json b/server/package.json index f2af6962..5c0872e5 100644 --- a/server/package.json +++ b/server/package.json @@ -2,7 +2,7 @@ "name": "project-server", "version": "1.0.0", "description": "", - "main": "dist/src/index.js", + "main": "dist/index.js", "type": "module", "engines": { "node": ">=24.0.0" @@ -10,7 +10,7 @@ "scripts": { "setup": "npm ci --include=dev", "build": "tsc -p tsconfig.json", - "start": "node ./dist/src/index.js", + "start": "node ./dist/index.js", "dev": "tsx watch ./src/index.ts", "test": "jest", "test:watch": "jest --watch", diff --git a/server/tsconfig.json b/server/tsconfig.json index 9642a364..394fe5bc 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -15,6 +15,6 @@ "types": ["jest", "node"] }, "files": ["index.d.ts"], - "include": ["src/**/*", "__tests__/**/*"], - "exclude": ["node_modules"] + "include": ["src/**/*"], + "exclude": ["node_modules", "__tests__"] } From 8fc05ce49438df01691f967e356874d8ed4b6127 Mon Sep 17 00:00:00 2001 From: Dasha Date: Sun, 8 Mar 2026 18:36:30 +0100 Subject: [PATCH 5/6] fix: move mongodb-memory-server devDependencies --- server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/package.json b/server/package.json index 5c0872e5..da38bdac 100644 --- a/server/package.json +++ b/server/package.json @@ -46,6 +46,7 @@ "eslint-plugin-import": "2.32.0", "eslint-plugin-prettier": "^5.1.3", "jest": "^30.2.0", + "mongodb-memory-server": "^11.0.1", "nodemon": "^3.1.0", "prettier": "^3.2.5", "prettier-eslint": "^16.3.0", @@ -66,7 +67,6 @@ "google-auth-library": "^10.6.1", "inversify": "^7.11.0", "jsonwebtoken": "^9.0.3", - "mongodb-memory-server": "^11.0.1", "mongoose": "^9.1.5", "multer": "^2.0.2", "nodemailer": "^8.0.1", From 05850f4a5ae3560808c4b63f2a3c09198d8df9b8 Mon Sep 17 00:00:00 2001 From: Dasha Date: Sun, 8 Mar 2026 18:39:58 +0100 Subject: [PATCH 6/6] fix: copilot suggestions improvements --- client/package-lock.json | 50 ++----------------- package.json | 2 +- .../__tests__/student/student.service.test.ts | 5 +- 3 files changed, 7 insertions(+), 50 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index fcf79b1c..41455dd8 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -20,6 +20,7 @@ "@stream-io/video-react-sdk": "^1.32.4", "@tailwindcss/vite": "4.1.18", "@tanstack/react-query": "^5.90.20", + "@vitejs/plugin-react": "^5.1.0", "axios": "^1.13.4", "class-variance-authority": "0.7.1", "framer-motion": "^11.18.2", @@ -30,6 +31,8 @@ "socket.io-client": "^4.8.3", "tailwind-merge": "3.4.0", "tailwindcss": "4.1.18", + "typescript": "5.9.3", + "vite": "^7.3", "zod": "4.3.6", "zustand": "^5.0.11" }, @@ -47,7 +50,6 @@ "@types/react-dom": "^19.0.4", "@typescript-eslint/eslint-plugin": "8.54.0", "@typescript-eslint/parser": "8.54.0", - "@vitejs/plugin-react": "^5.1.0", "babel-jest": "30.2.0", "dotenv": "^17.2.3", "eslint": "^9.21.0", @@ -63,9 +65,7 @@ "jest-environment-jsdom": "^30.2.0", "jest-fetch-mock": "^3.0.3", "prettier": "^3.2.5", - "prettier-eslint": "^16.3.0", - "typescript": "5.9.3", - "vite": "^7.3" + "prettier-eslint": "^16.3.0" }, "engines": { "node": ">=24.0.0" @@ -103,7 +103,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", @@ -118,7 +117,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -128,7 +126,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.28.6", @@ -159,7 +156,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.28.6", @@ -189,7 +185,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.28.6", @@ -263,7 +258,6 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -287,7 +281,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.28.6", @@ -301,7 +294,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.28.6", @@ -332,7 +324,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -392,7 +383,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -402,7 +392,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -412,7 +401,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -437,7 +425,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.28.6", @@ -451,7 +438,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.28.6" @@ -1547,7 +1533,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1563,7 +1548,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1984,7 +1968,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.28.6", @@ -1999,7 +1982,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.28.6", @@ -2018,7 +2000,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -4516,7 +4497,6 @@ "version": "1.0.0-beta.53", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==", - "dev": true, "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { @@ -5345,7 +5325,6 @@ "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.20.7", @@ -5359,7 +5338,6 @@ "version": "7.27.0", "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.0.0" @@ -5369,7 +5347,6 @@ "version": "7.4.4", "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.1.0", @@ -5380,7 +5357,6 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.28.2" @@ -6518,7 +6494,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz", "integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.28.5", @@ -7062,7 +7037,6 @@ "version": "2.9.18", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.18.tgz", "integrity": "sha512-e23vBV1ZLfjb9apvfPk4rHVu2ry6RIr2Wfs+O324okSidrX7pTAnEJPCh/O5BtRlr7QtZI7ktOP3vsqr7Z5XoA==", - "dev": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.js" @@ -7096,7 +7070,6 @@ "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "dev": true, "funding": [ { "type": "opencollective", @@ -7216,7 +7189,6 @@ "version": "1.0.30001766", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz", "integrity": "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==", - "dev": true, "funding": [ { "type": "opencollective", @@ -7402,7 +7374,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, "license": "MIT" }, "node_modules/cookie": { @@ -7775,7 +7746,6 @@ "version": "1.5.279", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.279.tgz", "integrity": "sha512-0bblUU5UNdOt5G7XqGiJtpZMONma6WAfq9vsFmtn9x1+joAObr6x1chfqyxFSDCAFwFhCQDrqeAr6MYdpwJ9Hg==", - "dev": true, "license": "ISC" }, "node_modules/emittery": { @@ -8095,7 +8065,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -8922,7 +8891,6 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -11295,7 +11263,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -11355,7 +11322,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -11396,7 +11362,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -11880,7 +11845,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^3.0.2" @@ -12178,7 +12142,6 @@ "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true, "license": "MIT" }, "node_modules/normalize-path": { @@ -13217,7 +13180,6 @@ "version": "0.18.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -13729,7 +13691,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -14643,7 +14604,6 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -14788,7 +14748,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, "funding": [ { "type": "opencollective", @@ -15409,7 +15368,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, "license": "ISC" }, "node_modules/yargs": { diff --git a/package.json b/package.json index a8c8509d..39f214e8 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "setup:client": "cd client && npm run setup && cd ..", "setup:server": "cd server && npm run setup && cd ..", "build:server": "cd server && npm run build && cd ..", - "heroku-postbuild": "npm run setup:server && npm run build:client && npm run build:server", + "heroku-postbuild": "npm run setup:client && npm run setup:server && npm run build:client && npm run build:server", "test": "npm run test:client && npm run test:server", "test:client": "cd client && npm run test && cd ..", "test:server": "cd server && npm run test && cd ..", diff --git a/server/__tests__/student/student.service.test.ts b/server/__tests__/student/student.service.test.ts index ba094351..693e6f21 100644 --- a/server/__tests__/student/student.service.test.ts +++ b/server/__tests__/student/student.service.test.ts @@ -1,6 +1,5 @@ import { createTestStudent, - getFutureDate, } from "../helpers/test.helpers.js"; import { StudentTypeDB } from "../../src/db/schemes/types/student.types.js"; import { @@ -13,7 +12,7 @@ import { StudentQuery } from "../../src/repositories/queryRepositories/student.q import { StudentCommand } from "../../src/repositories/commandRepositories/student.command.js"; import { randomUUID } from "node:crypto"; -describe("Student Service", () => { +describe("StudentQuery and StudentCommand", () => { let studentQuery: StudentQuery; let studentCommand: StudentCommand; let testStudent: StudentTypeDB; @@ -268,7 +267,7 @@ describe("Student Service", () => { }, }; - await expect(StudentModel.create(duplicateStudentData)).rejects.toThrow(); + await expect(StudentModel.create(duplicateStudentData)).rejects.toThrow(/duplicate key/i); }); });