diff --git a/Backend/jest.config.js b/Backend/jest.config.js deleted file mode 100644 index 89ba304b..00000000 --- a/Backend/jest.config.js +++ /dev/null @@ -1,22 +0,0 @@ -/** @type {import('jest').Config} */ -const config = { - preset: 'ts-jest', - testEnvironment: 'node', - roots: ['/tests'], - testMatch: ['**/*.test.ts'], - moduleNameMapper: { - '^@/(.*)$': '/src/$1' - }, - clearMocks: true, - collectCoverageFrom: [ - 'src/**/*.ts', - '!src/**/*.d.ts', - '!src/generated/**', - '!src/server.ts' - ], - transform: { - '^.+\\.tsx?$': ['ts-jest', { tsconfig: './tsconfig.test.json' }] - } -}; - -module.exports = config; diff --git a/Backend/package-lock.json b/Backend/package-lock.json index 7891b301..87a6afa3 100644 --- a/Backend/package-lock.json +++ b/Backend/package-lock.json @@ -8,10 +8,10 @@ "name": "e-learning-backend", "version": "1.0.0", "dependencies": { - "@node-rs/bcrypt": "^1.10.7", "@pdf-lib/fontkit": "^1.1.1", "@prisma/client": "^5.22.0", "@tsoa/runtime": "^6.4.0", + "bcrypt": "^5.1.1", "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.21.2", @@ -33,7 +33,6 @@ "@types/bcrypt": "^5.0.2", "@types/cors": "^2.8.17", "@types/express": "^5.0.0", - "@types/jest": "^30.0.0", "@types/jsonwebtoken": "^9.0.7", "@types/multer": "^1.4.12", "@types/node": "^22.10.5", @@ -1365,37 +1364,6 @@ "kuler": "^2.0.0" } }, - "node_modules/@emnapi/core": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", - "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", - "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", @@ -2382,16 +2350,6 @@ } } }, - "node_modules/@jest/diff-sequences": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", - "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/@jest/environment": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", @@ -2453,16 +2411,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/get-type": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", - "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/@jest/globals": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", @@ -2479,30 +2427,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/pattern": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", - "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-regex-util": "30.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/pattern/node_modules/jest-regex-util": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", - "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/@jest/reporters": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", @@ -2702,16 +2626,24 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "license": "MIT", - "optional": true, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "license": "BSD-3-Clause", "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" } }, "node_modules/@noble/hashes": { @@ -2727,259 +2659,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@node-rs/bcrypt": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.10.7.tgz", - "integrity": "sha512-1wk0gHsUQC/ap0j6SJa2K34qNhomxXRcEe3T8cI5s+g6fgHBgLTN7U9LzWTG/HE6G4+2tWWLeCabk1wiYGEQSA==", - "license": "MIT", - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "optionalDependencies": { - "@node-rs/bcrypt-android-arm-eabi": "1.10.7", - "@node-rs/bcrypt-android-arm64": "1.10.7", - "@node-rs/bcrypt-darwin-arm64": "1.10.7", - "@node-rs/bcrypt-darwin-x64": "1.10.7", - "@node-rs/bcrypt-freebsd-x64": "1.10.7", - "@node-rs/bcrypt-linux-arm-gnueabihf": "1.10.7", - "@node-rs/bcrypt-linux-arm64-gnu": "1.10.7", - "@node-rs/bcrypt-linux-arm64-musl": "1.10.7", - "@node-rs/bcrypt-linux-x64-gnu": "1.10.7", - "@node-rs/bcrypt-linux-x64-musl": "1.10.7", - "@node-rs/bcrypt-wasm32-wasi": "1.10.7", - "@node-rs/bcrypt-win32-arm64-msvc": "1.10.7", - "@node-rs/bcrypt-win32-ia32-msvc": "1.10.7", - "@node-rs/bcrypt-win32-x64-msvc": "1.10.7" - } - }, - "node_modules/@node-rs/bcrypt-android-arm-eabi": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.10.7.tgz", - "integrity": "sha512-8dO6/PcbeMZXS3VXGEtct9pDYdShp2WBOWlDvSbcRwVqyB580aCBh0BEFmKYtXLzLvUK8Wf+CG3U6sCdILW1lA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-android-arm64": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.10.7.tgz", - "integrity": "sha512-UASFBS/CucEMHiCtL/2YYsAY01ZqVR1N7vSb94EOvG5iwW7BQO06kXXCTgj+Xbek9azxixrCUmo3WJnkJZ0hTQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-darwin-arm64": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.10.7.tgz", - "integrity": "sha512-DgzFdAt455KTuiJ/zYIyJcKFobjNDR/hnf9OS7pK5NRS13Nq4gLcSIIyzsgHwZHxsJWbLpHmFc1H23Y7IQoQBw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-darwin-x64": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.10.7.tgz", - "integrity": "sha512-SPWVfQ6sxSokoUWAKWD0EJauvPHqOGQTd7CxmYatcsUgJ/bruvEHxZ4bIwX1iDceC3FkOtmeHO0cPwR480n/xA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-freebsd-x64": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.10.7.tgz", - "integrity": "sha512-gpa+Ixs6GwEx6U6ehBpsQetzUpuAGuAFbOiuLB2oo4N58yU4AZz1VIcWyWAHrSWRs92O0SHtmo2YPrMrwfBbSw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-linux-arm-gnueabihf": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.10.7.tgz", - "integrity": "sha512-kYgJnTnpxrzl9sxYqzflobvMp90qoAlaX1oDL7nhNTj8OYJVDIk0jQgblj0bIkjmoPbBed53OJY/iu4uTS+wig==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-linux-arm64-gnu": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.10.7.tgz", - "integrity": "sha512-7cEkK2RA+gBCj2tCVEI1rDSJV40oLbSq7bQ+PNMHNI6jCoXGmj9Uzo7mg7ZRbNZ7piIyNH5zlJqutjo8hh/tmA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-linux-arm64-musl": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.10.7.tgz", - "integrity": "sha512-X7DRVjshhwxUqzdUKDlF55cwzh+wqWJ2E/tILvZPboO3xaNO07Um568Vf+8cmKcz+tiZCGP7CBmKbBqjvKN/Pw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-linux-x64-gnu": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.10.7.tgz", - "integrity": "sha512-LXRZsvG65NggPD12hn6YxVgH0W3VR5fsE/o1/o2D5X0nxKcNQGeLWnRzs5cP8KpoFOuk1ilctXQJn8/wq+Gn/Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-linux-x64-musl": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.10.7.tgz", - "integrity": "sha512-tCjHmct79OfcO3g5q21ME7CNzLzpw1MAsUXCLHLGWH+V6pp/xTvMbIcLwzkDj6TI3mxK6kehTn40SEjBkZ3Rog==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-wasm32-wasi": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.10.7.tgz", - "integrity": "sha512-4qXSihIKeVXYglfXZEq/QPtYtBUvR8d3S85k15Lilv3z5B6NSGQ9mYiNleZ7QHVLN2gEc5gmi7jM353DMH9GkA==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.5" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@node-rs/bcrypt-win32-arm64-msvc": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.10.7.tgz", - "integrity": "sha512-FdfUQrqmDfvC5jFhntMBkk8EI+fCJTx/I1v7Rj+Ezlr9rez1j1FmuUnywbBj2Cg15/0BDhwYdbyZ5GCMFli2aQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-win32-ia32-msvc": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.10.7.tgz", - "integrity": "sha512-lZLf4Cx+bShIhU071p5BZft4OvP4PGhyp542EEsb3zk34U5GLsGIyCjOafcF/2DGewZL6u8/aqoxbSuROkgFXg==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-win32-x64-msvc": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.10.7.tgz", - "integrity": "sha512-hdw7tGmN1DxVAMTzICLdaHpXjy+4rxaxnBMgI8seG1JL5e3VcRGsd1/1vVDogVp2cbsmgq+6d6yAY+D9lW/DCg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@paralleldrive/cuid2": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", @@ -3920,16 +3599,6 @@ "yarn": ">=1.9.4" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.7.tgz", @@ -4120,230 +3789,6 @@ "@types/istanbul-lib-report": "*" } }, - "node_modules/@types/jest": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", - "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^30.0.0", - "pretty-format": "^30.0.0" - } - }, - "node_modules/@types/jest/node_modules/@jest/expect-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", - "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/@sinclair/typebox": { - "version": "0.34.48", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", - "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/jest/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@types/jest/node_modules/ci-info": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", - "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@types/jest/node_modules/expect": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "30.2.0", - "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/jest-diff": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", - "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/jest-matcher-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", - "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.2.0", - "pretty-format": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/jest-mock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", - "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@types/jest/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -4741,6 +4186,12 @@ "license": "(Unlicense OR Apache-2.0)", "optional": true }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "license": "ISC" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -4777,6 +4228,18 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -4854,6 +4317,26 @@ "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", "license": "MIT" }, + "node_modules/aproba": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", + "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", + "license": "ISC" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -5044,6 +4527,20 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -5391,6 +4888,15 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -5507,6 +5013,15 @@ "node": ">=12.20" } }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/color/node_modules/color-convert": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", @@ -5555,7 +5070,6 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, "license": "MIT" }, "node_modules/concat-stream": { @@ -5603,6 +5117,12 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC" + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -5712,7 +5232,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -5794,6 +5313,12 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT" + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -5813,6 +5338,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -6669,11 +6203,40 @@ "node": ">=14.14" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, "license": "ISC" }, "node_modules/fsevents": { @@ -6700,6 +6263,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/generator-function": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", @@ -6802,7 +6386,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -6836,7 +6419,6 @@ "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -6847,7 +6429,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -6957,6 +6538,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC" + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -7005,6 +6592,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -7096,7 +6696,6 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -8311,6 +7910,30 @@ "yallist": "^3.0.2" } }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -8501,6 +8124,37 @@ "node": ">=8" } }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -8560,6 +8214,32 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "license": "MIT" }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -8659,6 +8339,21 @@ "node": ">=4" } }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "license": "ISC", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -8682,6 +8377,19 @@ "node": ">=8" } }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -8719,7 +8427,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -8877,7 +8584,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -9390,6 +9096,22 @@ "node": ">=10" } }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -9517,6 +9239,12 @@ "node": ">= 0.8.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -9637,7 +9365,6 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, "license": "ISC" }, "node_modules/simple-update-notifier": { @@ -10010,6 +9737,41 @@ "express": ">=4.0.0 || >=5.0.0-beta" } }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -10151,6 +9913,12 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, "node_modules/triple-beam": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", @@ -10277,7 +10045,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "devOptional": true, + "dev": true, "license": "0BSD" }, "node_modules/tsoa": { @@ -10533,6 +10301,22 @@ "@zxing/text-encoding": "0.9.0" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -10569,6 +10353,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/winston": { "version": "3.19.0", "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", @@ -10660,7 +10453,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, "license": "ISC" }, "node_modules/write-file-atomic": { diff --git a/Backend/package.json b/Backend/package.json index 8f71c83c..392d6b82 100644 --- a/Backend/package.json +++ b/Backend/package.json @@ -44,7 +44,6 @@ "@types/bcrypt": "^5.0.2", "@types/cors": "^2.8.17", "@types/express": "^5.0.0", - "@types/jest": "^30.0.0", "@types/jsonwebtoken": "^9.0.7", "@types/multer": "^1.4.12", "@types/node": "^22.10.5", diff --git a/Backend/pnpm-lock.yaml b/Backend/pnpm-lock.yaml index a30e07ad..f7f8d01b 100644 --- a/Backend/pnpm-lock.yaml +++ b/Backend/pnpm-lock.yaml @@ -78,9 +78,6 @@ importers: '@types/express': specifier: ^5.0.0 version: 5.0.6 - '@types/jest': - specifier: ^30.0.0 - version: 30.0.0 '@types/jsonwebtoken': specifier: ^9.0.7 version: 9.0.10 @@ -122,7 +119,7 @@ importers: version: 7.2.2 ts-jest: specifier: ^29.2.5 - version: 29.4.6(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.5))(typescript@5.9.3) + version: 29.4.6(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.5))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.5))(typescript@5.9.3) tsconfig-paths: specifier: ^4.2.0 version: 4.2.0 @@ -618,10 +615,6 @@ packages: node-notifier: optional: true - '@jest/diff-sequences@30.0.1': - resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/environment@29.7.0': resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -630,10 +623,6 @@ packages: resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/expect-utils@30.2.0': - resolution: {integrity: sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/expect@29.7.0': resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -642,18 +631,10 @@ packages: resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/get-type@30.1.0': - resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/globals@29.7.0': resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/pattern@30.0.1': - resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/reporters@29.7.0': resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -667,10 +648,6 @@ packages: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/schemas@30.0.5': - resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/source-map@29.6.3': resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -691,10 +668,6 @@ packages: resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/types@30.2.0': - resolution: {integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -889,9 +862,6 @@ packages: '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - '@sinclair/typebox@0.34.48': - resolution: {integrity: sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==} - '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} @@ -1145,9 +1115,6 @@ packages: '@types/istanbul-reports@3.0.4': resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - '@types/jest@30.0.0': - resolution: {integrity: sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==} - '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -1466,10 +1433,6 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} - ci-info@4.4.0: - resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} - engines: {node: '>=8'} - cjs-module-lexer@1.4.3: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} @@ -1766,10 +1729,6 @@ packages: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - expect@30.2.0: - resolution: {integrity: sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - express-rate-limit@7.5.1: resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} engines: {node: '>= 16'} @@ -2159,10 +2118,6 @@ packages: resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-diff@30.2.0: - resolution: {integrity: sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-docblock@29.7.0: resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2191,26 +2146,14 @@ packages: resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-matcher-utils@30.2.0: - resolution: {integrity: sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-message-util@29.7.0: resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-message-util@30.2.0: - resolution: {integrity: sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-mock@29.7.0: resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-mock@30.2.0: - resolution: {integrity: sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-pnp-resolver@1.2.3: resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} engines: {node: '>=6'} @@ -2224,10 +2167,6 @@ packages: resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-regex-util@30.0.1: - resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-resolve-dependencies@29.7.0: resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2252,10 +2191,6 @@ packages: resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-util@30.2.0: - resolution: {integrity: sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-validate@29.7.0: resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2649,10 +2584,6 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - pretty-format@30.2.0: - resolution: {integrity: sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - prisma@5.22.0: resolution: {integrity: sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==} engines: {node: '>=16.13'} @@ -4096,8 +4027,6 @@ snapshots: - supports-color - ts-node - '@jest/diff-sequences@30.0.1': {} - '@jest/environment@29.7.0': dependencies: '@jest/fake-timers': 29.7.0 @@ -4109,10 +4038,6 @@ snapshots: dependencies: jest-get-type: 29.6.3 - '@jest/expect-utils@30.2.0': - dependencies: - '@jest/get-type': 30.1.0 - '@jest/expect@29.7.0': dependencies: expect: 29.7.0 @@ -4129,8 +4054,6 @@ snapshots: jest-mock: 29.7.0 jest-util: 29.7.0 - '@jest/get-type@30.1.0': {} - '@jest/globals@29.7.0': dependencies: '@jest/environment': 29.7.0 @@ -4140,11 +4063,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@jest/pattern@30.0.1': - dependencies: - '@types/node': 22.19.5 - jest-regex-util: 30.0.1 - '@jest/reporters@29.7.0': dependencies: '@bcoe/v8-coverage': 0.2.3 @@ -4178,10 +4096,6 @@ snapshots: dependencies: '@sinclair/typebox': 0.27.8 - '@jest/schemas@30.0.5': - dependencies: - '@sinclair/typebox': 0.34.48 - '@jest/source-map@29.6.3': dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -4231,16 +4145,6 @@ snapshots: '@types/yargs': 17.0.35 chalk: 4.1.2 - '@jest/types@30.2.0': - dependencies: - '@jest/pattern': 30.0.1 - '@jest/schemas': 30.0.5 - '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports': 3.0.4 - '@types/node': 22.19.5 - '@types/yargs': 17.0.35 - chalk: 4.1.2 - '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -4412,8 +4316,6 @@ snapshots: '@sinclair/typebox@0.27.8': {} - '@sinclair/typebox@0.34.48': {} - '@sinonjs/commons@3.0.1': dependencies: type-detect: 4.0.8 @@ -4819,11 +4721,6 @@ snapshots: dependencies: '@types/istanbul-lib-report': 3.0.3 - '@types/jest@30.0.0': - dependencies: - expect: 30.2.0 - pretty-format: 30.2.0 - '@types/json-schema@7.0.15': {} '@types/jsonwebtoken@9.0.10': @@ -5219,8 +5116,6 @@ snapshots: ci-info@3.9.0: {} - ci-info@4.4.0: {} - cjs-module-lexer@1.4.3: {} cliui@8.0.1: @@ -5503,15 +5398,6 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 - expect@30.2.0: - dependencies: - '@jest/expect-utils': 30.2.0 - '@jest/get-type': 30.1.0 - jest-matcher-utils: 30.2.0 - jest-message-util: 30.2.0 - jest-mock: 30.2.0 - jest-util: 30.2.0 - express-rate-limit@7.5.1(express@4.22.1): dependencies: express: 4.22.1 @@ -5986,13 +5872,6 @@ snapshots: jest-get-type: 29.6.3 pretty-format: 29.7.0 - jest-diff@30.2.0: - dependencies: - '@jest/diff-sequences': 30.0.1 - '@jest/get-type': 30.1.0 - chalk: 4.1.2 - pretty-format: 30.2.0 - jest-docblock@29.7.0: dependencies: detect-newline: 3.1.0 @@ -6044,13 +5923,6 @@ snapshots: jest-get-type: 29.6.3 pretty-format: 29.7.0 - jest-matcher-utils@30.2.0: - dependencies: - '@jest/get-type': 30.1.0 - chalk: 4.1.2 - jest-diff: 30.2.0 - pretty-format: 30.2.0 - jest-message-util@29.7.0: dependencies: '@babel/code-frame': 7.27.1 @@ -6063,38 +5935,18 @@ snapshots: slash: 3.0.0 stack-utils: 2.0.6 - jest-message-util@30.2.0: - dependencies: - '@babel/code-frame': 7.27.1 - '@jest/types': 30.2.0 - '@types/stack-utils': 2.0.3 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.8 - pretty-format: 30.2.0 - slash: 3.0.0 - stack-utils: 2.0.6 - jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 '@types/node': 22.19.5 jest-util: 29.7.0 - jest-mock@30.2.0: - dependencies: - '@jest/types': 30.2.0 - '@types/node': 22.19.5 - jest-util: 30.2.0 - jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): optionalDependencies: jest-resolve: 29.7.0 jest-regex-util@29.6.3: {} - jest-regex-util@30.0.1: {} - jest-resolve-dependencies@29.7.0: dependencies: jest-regex-util: 29.6.3 @@ -6201,15 +6053,6 @@ snapshots: graceful-fs: 4.2.11 picomatch: 2.3.1 - jest-util@30.2.0: - dependencies: - '@jest/types': 30.2.0 - '@types/node': 22.19.5 - chalk: 4.1.2 - ci-info: 4.4.0 - graceful-fs: 4.2.11 - picomatch: 4.0.3 - jest-validate@29.7.0: dependencies: '@jest/types': 29.6.3 @@ -6601,12 +6444,6 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 - pretty-format@30.2.0: - dependencies: - '@jest/schemas': 30.0.5 - ansi-styles: 5.2.0 - react-is: 18.3.1 - prisma@5.22.0: dependencies: '@prisma/engines': 5.22.0 @@ -6960,7 +6797,7 @@ snapshots: ts-deepmerge@7.0.3: {} - ts-jest@29.4.6(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.5))(typescript@5.9.3): + ts-jest@29.4.6(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.5))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.5))(typescript@5.9.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 @@ -6976,9 +6813,9 @@ snapshots: optionalDependencies: '@babel/core': 7.28.5 '@jest/transform': 29.7.0 - '@jest/types': 30.2.0 + '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.28.5) - jest-util: 30.2.0 + jest-util: 29.7.0 tsconfig-paths@4.2.0: dependencies: diff --git a/Backend/src/controllers/AuditController.ts b/Backend/src/controllers/AuditController.ts index a78c8d5a..5de912fc 100644 --- a/Backend/src/controllers/AuditController.ts +++ b/Backend/src/controllers/AuditController.ts @@ -169,8 +169,8 @@ export class AuditController { throw new ValidationError('No token provided'); } - if (days < 6) { - throw new ValidationError('Cannot delete logs newer than 6 days'); + if (days < 30) { + throw new ValidationError('Cannot delete logs newer than 30 days'); } const deleted = await auditService.deleteOldLogs(days); diff --git a/Backend/src/services/CoursesStudent.service.ts b/Backend/src/services/CoursesStudent.service.ts index 986695b1..0e9e5b86 100644 --- a/Backend/src/services/CoursesStudent.service.ts +++ b/Backend/src/services/CoursesStudent.service.ts @@ -340,19 +340,6 @@ export class CoursesStudentService { throw new ForbiddenError('You are not enrolled in this course'); } - // Update last_accessed_at (fire-and-forget — ไม่ block response) - if (enrollment.status === 'ENROLLED') { - prisma.enrollment.update({ - where: { - unique_enrollment: { - user_id: decoded.id, - course_id, - }, - }, - data: { last_accessed_at: new Date() }, - }).catch(err => logger.warn(`Failed to update last_accessed_at: ${err}`)); - } - // Get all lesson progress for this user and course const lessonIds = course.chapters.flatMap(ch => ch.lessons.map(l => l.id)); const lessonProgress = await prisma.lessonProgress.findMany({ @@ -1262,17 +1249,17 @@ export class CoursesStudentService { } catch (error) { logger.error(`Error completing lesson: ${error}`); const decoded = jwt.decode(input.token) as { id: number } | null; - await auditService.logSync({ - userId: decoded?.id || 0, - action: AuditAction.ERROR, - entityType: 'LessonProgress', - entityId: input.lesson_id, - metadata: { - operation: 'complete_lesson', - lesson_id: input.lesson_id, - error: error instanceof Error ? error.message : String(error) - } - }); + await auditService.logSync({ + userId: decoded?.id || 0, + action: AuditAction.ERROR, + entityType: 'LessonProgress', + entityId: input.lesson_id, + metadata: { + operation: 'complete_lesson', + lesson_id: input.lesson_id, + error: error instanceof Error ? error.message : String(error) + } + }); throw error; } } diff --git a/Backend/src/validators/ChaptersLesson.validator.ts b/Backend/src/validators/ChaptersLesson.validator.ts index 45d7687a..933a3e1a 100644 --- a/Backend/src/validators/ChaptersLesson.validator.ts +++ b/Backend/src/validators/ChaptersLesson.validator.ts @@ -79,7 +79,7 @@ export const UpdateLessonValidator = Joi.object({ 'number.min': 'Duration must be at least 0' }), sort_order: Joi.number().integer().min(0).optional(), - prerequisite_lesson_ids: Joi.array().items(Joi.number().integer().positive()).allow(null).optional(), + prerequisite_lesson_ids: Joi.array().items(Joi.number().integer().positive()).optional(), is_published: Joi.boolean().optional() }); diff --git a/Backend/tests/k6/login-load-test.js b/Backend/tests/k6/login-load-test.js index aee4cb4a..2a0c375d 100644 --- a/Backend/tests/k6/login-load-test.js +++ b/Backend/tests/k6/login-load-test.js @@ -31,7 +31,7 @@ export const options = { thresholds: { http_req_duration: ['p(95)<2000'], // 95% of requests < 2s errors: ['rate<0.1'], // Error rate < 10% - login_duration: ['p(95)<2000'], // 95% pof logins < 2s + login_duration: ['p(95)<2000'], // 95% of logins < 2s }, }; diff --git a/Backend/tests/tsconfig.json b/Backend/tests/tsconfig.json deleted file mode 100644 index 330cd8fd..00000000 --- a/Backend/tests/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "../tsconfig.test.json", - "compilerOptions": { - "rootDir": "..", - "types": [ - "node", - "jest" - ] - }, - "include": [ - "../src/**/*", - "./**/*" - ] -} \ No newline at end of file diff --git a/Backend/tests/unit/validators/AdminCourseApproval.validator.test.ts b/Backend/tests/unit/validators/AdminCourseApproval.validator.test.ts deleted file mode 100644 index 9068ad48..00000000 --- a/Backend/tests/unit/validators/AdminCourseApproval.validator.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { - ApproveCourseValidator, - RejectCourseValidator, -} from '@/validators/AdminCourseApproval.validator'; - -describe('ApproveCourseValidator', () => { - it('should pass with no body (comment optional)', () => { - const { error } = ApproveCourseValidator.validate({}); - expect(error).toBeUndefined(); - }); - - it('should pass with optional comment', () => { - const { error } = ApproveCourseValidator.validate({ - comment: 'Looks great!', - }); - expect(error).toBeUndefined(); - }); - - it('should fail when comment exceeds 1000 characters', () => { - const { error } = ApproveCourseValidator.validate({ - comment: 'a'.repeat(1001), - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/must not exceed 1000/i); - }); - - it('should pass with comment exactly 1000 characters', () => { - const { error } = ApproveCourseValidator.validate({ - comment: 'a'.repeat(1000), - }); - expect(error).toBeUndefined(); - }); -}); - -describe('RejectCourseValidator', () => { - it('should pass with valid rejection comment', () => { - const { error } = RejectCourseValidator.validate({ - comment: 'The content is incomplete and needs more details.', - }); - expect(error).toBeUndefined(); - }); - - it('should fail without comment', () => { - const { error } = RejectCourseValidator.validate({}); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Comment is required when rejecting/i); - }); - - it('should fail when comment is too short (< 10 chars)', () => { - const { error } = RejectCourseValidator.validate({ comment: 'Too short' }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/at least 10 characters/i); - }); - - it('should pass with exactly 10 characters', () => { - const { error } = RejectCourseValidator.validate({ comment: '1234567890' }); - expect(error).toBeUndefined(); - }); - - it('should fail when comment exceeds 1000 characters', () => { - const { error } = RejectCourseValidator.validate({ - comment: 'a'.repeat(1001), - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/must not exceed 1000/i); - }); -}); diff --git a/Backend/tests/unit/validators/ChaptersLesson.validator.test.ts b/Backend/tests/unit/validators/ChaptersLesson.validator.test.ts deleted file mode 100644 index 7e80bc68..00000000 --- a/Backend/tests/unit/validators/ChaptersLesson.validator.test.ts +++ /dev/null @@ -1,263 +0,0 @@ -import { - CreateChapterValidator, - UpdateChapterValidator, - ReorderChapterValidator, - CreateLessonValidator, - UpdateLessonValidator, - ReorderLessonsValidator, - AddQuestionValidator, - UpdateQuizValidator, -} from '@/validators/ChaptersLesson.validator'; - - -// ============================================================ -// Chapter Validators -// ============================================================ - -describe('CreateChapterValidator', () => { - it('should pass with valid data', () => { - const { error } = CreateChapterValidator.validate({ - title: { th: 'บทที่ 1', en: 'Chapter 1' }, - }); - expect(error).toBeUndefined(); - }); - - it('should pass with optional fields', () => { - const { error } = CreateChapterValidator.validate({ - title: { th: 'บทที่ 1', en: 'Chapter 1' }, - description: { th: 'คำอธิบาย', en: 'Description' }, - sort_order: 0, - }); - expect(error).toBeUndefined(); - }); - - it('should fail if title is missing', () => { - const { error } = CreateChapterValidator.validate({}); - expect(error).toBeDefined(); - }); - - it('should fail if title.th is missing', () => { - const { error } = CreateChapterValidator.validate({ - title: { en: 'Chapter 1' }, - }); - expect(error).toBeDefined(); - }); - - it('should fail if title.en is missing', () => { - const { error } = CreateChapterValidator.validate({ - title: { th: 'บทที่ 1' }, - }); - expect(error).toBeDefined(); - }); -}); - -describe('UpdateChapterValidator', () => { - it('should pass with empty object (all fields optional)', () => { - const { error } = UpdateChapterValidator.validate({}); - expect(error).toBeUndefined(); - }); - - it('should pass with partial fields', () => { - const { error } = UpdateChapterValidator.validate({ - is_published: true, - }); - expect(error).toBeUndefined(); - }); -}); - -describe('ReorderChapterValidator', () => { - it('should pass with valid sort_order', () => { - const { error } = ReorderChapterValidator.validate({ sort_order: 0 }); - expect(error).toBeUndefined(); - }); - - it('should fail without sort_order', () => { - const { error } = ReorderChapterValidator.validate({}); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Sort order is required/i); - }); - - it('should fail with negative sort_order', () => { - const { error } = ReorderChapterValidator.validate({ sort_order: -1 }); - expect(error).toBeDefined(); - }); -}); - -// ============================================================ -// Lesson Validators -// ============================================================ - -describe('CreateLessonValidator', () => { - it('should pass with VIDEO type', () => { - const { error } = CreateLessonValidator.validate({ - title: { th: 'บทเรียน 1', en: 'Lesson 1' }, - type: 'VIDEO', - }); - expect(error).toBeUndefined(); - }); - - it('should pass with QUIZ type', () => { - const { error } = CreateLessonValidator.validate({ - title: { th: 'แบบทดสอบ', en: 'Quiz' }, - type: 'QUIZ', - }); - expect(error).toBeUndefined(); - }); - - it('should fail with invalid type', () => { - const { error } = CreateLessonValidator.validate({ - title: { th: 'บทเรียน', en: 'Lesson' }, - type: 'DOCUMENT', - }); - expect(error).toBeDefined(); - }); - - it('should fail without title', () => { - const { error } = CreateLessonValidator.validate({ type: 'VIDEO' }); - expect(error).toBeDefined(); - }); - - it('should fail without type', () => { - const { error } = CreateLessonValidator.validate({ - title: { th: 'บทเรียน', en: 'Lesson' }, - }); - expect(error).toBeDefined(); - }); -}); - -describe('UpdateLessonValidator — prerequisite_lesson_ids', () => { - it('should pass with valid array of ids', () => { - const { error } = UpdateLessonValidator.validate({ - prerequisite_lesson_ids: [1, 2, 3], - }); - expect(error).toBeUndefined(); - }); - - it('should pass with null (clear prerequisites)', () => { - const { error } = UpdateLessonValidator.validate({ - prerequisite_lesson_ids: null, - }); - expect(error).toBeUndefined(); - }); - - it('should pass with empty array (clear prerequisites)', () => { - const { error } = UpdateLessonValidator.validate({ - prerequisite_lesson_ids: [], - }); - expect(error).toBeUndefined(); - }); - - it('should pass without the field (no change)', () => { - const { error } = UpdateLessonValidator.validate({}); - expect(error).toBeUndefined(); - }); - - it('should fail with non-integer ids', () => { - const { error } = UpdateLessonValidator.validate({ - prerequisite_lesson_ids: [1.5], - }); - expect(error).toBeDefined(); - }); - - it('should fail with negative ids', () => { - const { error } = UpdateLessonValidator.validate({ - prerequisite_lesson_ids: [-1], - }); - expect(error).toBeDefined(); - }); - - it('should fail with string ids', () => { - const { error } = UpdateLessonValidator.validate({ - prerequisite_lesson_ids: ['abc'], - }); - expect(error).toBeDefined(); - }); -}); - -describe('ReorderLessonsValidator', () => { - it('should pass with valid data', () => { - const { error } = ReorderLessonsValidator.validate({ - lesson_id: 1, - sort_order: 0, - }); - expect(error).toBeUndefined(); - }); - - it('should fail without lesson_id', () => { - const { error } = ReorderLessonsValidator.validate({ sort_order: 0 }); - expect(error).toBeDefined(); - }); - - it('should fail without sort_order', () => { - const { error } = ReorderLessonsValidator.validate({ lesson_id: 1 }); - expect(error).toBeDefined(); - }); -}); - -// ============================================================ -// Quiz Validators -// ============================================================ - -describe('AddQuestionValidator', () => { - it('should pass with MULTIPLE_CHOICE type + choices', () => { - const { error } = AddQuestionValidator.validate({ - question: { th: 'ข้อที่ 1 คืออะไร?', en: 'What is question 1?' }, - question_type: 'MULTIPLE_CHOICE', - choices: [ - { text: { th: 'ก', en: 'A' }, is_correct: true }, - { text: { th: 'ข', en: 'B' }, is_correct: false }, - ], - }); - expect(error).toBeUndefined(); - }); - - it('should pass with TRUE_FALSE type without choices', () => { - const { error } = AddQuestionValidator.validate({ - question: { th: 'ถูกหรือผิด?', en: 'True or False?' }, - question_type: 'TRUE_FALSE', - }); - expect(error).toBeUndefined(); - }); - - it('should fail with invalid question_type', () => { - const { error } = AddQuestionValidator.validate({ - question: { th: 'คำถาม', en: 'Question' }, - question_type: 'ESSAY', - }); - expect(error).toBeDefined(); - }); - - it('should fail without question', () => { - const { error } = AddQuestionValidator.validate({ - question_type: 'TRUE_FALSE', - }); - expect(error).toBeDefined(); - }); -}); - -describe('UpdateQuizValidator', () => { - it('should pass with empty object (all optional)', () => { - const { error } = UpdateQuizValidator.validate({}); - expect(error).toBeUndefined(); - }); - - it('should pass with valid passing_score', () => { - const { error } = UpdateQuizValidator.validate({ passing_score: 70 }); - expect(error).toBeUndefined(); - }); - - it('should fail with passing_score > 100', () => { - const { error } = UpdateQuizValidator.validate({ passing_score: 101 }); - expect(error).toBeDefined(); - }); - - it('should fail with passing_score < 0', () => { - const { error } = UpdateQuizValidator.validate({ passing_score: -1 }); - expect(error).toBeDefined(); - }); - - it('should pass with time_limit 0 (no limit)', () => { - const { error } = UpdateQuizValidator.validate({ time_limit: 0 }); - expect(error).toBeUndefined(); - }); -}); diff --git a/Backend/tests/unit/validators/CoursesInstructor.validator.test.ts b/Backend/tests/unit/validators/CoursesInstructor.validator.test.ts deleted file mode 100644 index 95c13c07..00000000 --- a/Backend/tests/unit/validators/CoursesInstructor.validator.test.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { - CreateCourseValidator, - UpdateCourseValidator, - CloneCourseValidator, - addInstructorCourseValidator, -} from '@/validators/CoursesInstructor.validator'; - -// ============================================================ -// addInstructorCourseValidator -// ============================================================ - -describe('addInstructorCourseValidator', () => { - it('should pass with valid user_id and course_id', () => { - const { error } = addInstructorCourseValidator.validate({ - user_id: 1, - course_id: 2, - }); - expect(error).toBeUndefined(); - }); - - it('should fail without user_id', () => { - const { error } = addInstructorCourseValidator.validate({ course_id: 2 }); - expect(error).toBeDefined(); - }); - - it('should fail without course_id', () => { - const { error } = addInstructorCourseValidator.validate({ user_id: 1 }); - expect(error).toBeDefined(); - }); -}); - -// ============================================================ -// CreateCourseValidator -// ============================================================ - -describe('CreateCourseValidator', () => { - const validPayload = { - category_id: 1, - title: { th: 'คอร์สทดสอบ', en: 'Test Course' }, - slug: 'test-course', - description: { th: 'คำอธิบาย', en: 'Description' }, - price: 500, - is_free: false, - have_certificate: true, - }; - - it('should pass with all required fields', () => { - const { error } = CreateCourseValidator.validate(validPayload); - expect(error).toBeUndefined(); - }); - - it('should fail if title.th is missing', () => { - const { error } = CreateCourseValidator.validate({ - ...validPayload, - title: { en: 'Test Course' }, - }); - expect(error).toBeDefined(); - }); - - it('should fail if slug is missing', () => { - const { error } = CreateCourseValidator.validate({ - ...validPayload, - slug: undefined, - }); - expect(error).toBeDefined(); - }); - - it('should fail if price is missing', () => { - const { error } = CreateCourseValidator.validate({ - ...validPayload, - price: undefined, - }); - expect(error).toBeDefined(); - }); - - it('should fail if is_free is missing', () => { - const { error } = CreateCourseValidator.validate({ - ...validPayload, - is_free: undefined, - }); - expect(error).toBeDefined(); - }); - - it('should allow price = 0 (free course)', () => { - const { error } = CreateCourseValidator.validate({ - ...validPayload, - price: 0, - is_free: true, - }); - expect(error).toBeUndefined(); - }); -}); - -// ============================================================ -// UpdateCourseValidator -// ============================================================ - -describe('UpdateCourseValidator', () => { - it('should pass with empty object (all optional)', () => { - const { error } = UpdateCourseValidator.validate({}); - expect(error).toBeUndefined(); - }); - - it('should pass with partial update', () => { - const { error } = UpdateCourseValidator.validate({ price: 999 }); - expect(error).toBeUndefined(); - }); - - it('should pass with title partial update (th only)', () => { - const { error } = UpdateCourseValidator.validate({ - title: { th: 'ชื่อใหม่' }, - }); - expect(error).toBeUndefined(); - }); -}); - -// ============================================================ -// CloneCourseValidator -// ============================================================ - -describe('CloneCourseValidator', () => { - it('should pass with valid title', () => { - const { error } = CloneCourseValidator.validate({ - title: { th: 'คอร์ส Copy', en: 'Course Copy' }, - }); - expect(error).toBeUndefined(); - }); - - it('should fail without title.th', () => { - const { error } = CloneCourseValidator.validate({ - title: { en: 'Course Copy' }, - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Thai title is required/i); - }); - - it('should fail without title.en', () => { - const { error } = CloneCourseValidator.validate({ - title: { th: 'คอร์ส Copy' }, - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/English title is required/i); - }); - - it('should fail without title entirely', () => { - const { error } = CloneCourseValidator.validate({}); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Title is required/i); - }); -}); diff --git a/Backend/tests/unit/validators/CoursesStudent.validator.test.ts b/Backend/tests/unit/validators/CoursesStudent.validator.test.ts deleted file mode 100644 index ae9569c4..00000000 --- a/Backend/tests/unit/validators/CoursesStudent.validator.test.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { - SaveVideoProgressValidator, - SubmitQuizValidator, -} from '@/validators/CoursesStudent.validator'; - -describe('SaveVideoProgressValidator', () => { - it('should pass with required field only', () => { - const { error } = SaveVideoProgressValidator.validate({ - video_progress_seconds: 60, - }); - expect(error).toBeUndefined(); - }); - - it('should pass with all fields', () => { - const { error } = SaveVideoProgressValidator.validate({ - video_progress_seconds: 120, - video_duration_seconds: 600, - }); - expect(error).toBeUndefined(); - }); - - it('should pass with progress = 0 (start of video)', () => { - const { error } = SaveVideoProgressValidator.validate({ - video_progress_seconds: 0, - }); - expect(error).toBeUndefined(); - }); - - it('should fail without video_progress_seconds', () => { - const { error } = SaveVideoProgressValidator.validate({}); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Video progress seconds is required/i); - }); - - it('should fail with negative progress', () => { - const { error } = SaveVideoProgressValidator.validate({ - video_progress_seconds: -1, - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/at least 0/i); - }); - - it('should fail with negative video duration', () => { - const { error } = SaveVideoProgressValidator.validate({ - video_progress_seconds: 10, - video_duration_seconds: -1, - }); - expect(error).toBeDefined(); - }); -}); - -describe('SubmitQuizValidator', () => { - const validAnswer = { question_id: 1, choice_id: 2 }; - - it('should pass with valid answers', () => { - const { error } = SubmitQuizValidator.validate({ - answers: [validAnswer, { question_id: 2, choice_id: 5 }], - }); - expect(error).toBeUndefined(); - }); - - it('should fail without answers', () => { - const { error } = SubmitQuizValidator.validate({}); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Answers are required/i); - }); - - it('should fail with empty answers array', () => { - const { error } = SubmitQuizValidator.validate({ answers: [] }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/At least one answer/i); - }); - - it('should fail if question_id is missing in an answer', () => { - const { error } = SubmitQuizValidator.validate({ - answers: [{ choice_id: 2 }], - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Question ID is required/i); - }); - - it('should fail if choice_id is missing in an answer', () => { - const { error } = SubmitQuizValidator.validate({ - answers: [{ question_id: 1 }], - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Choice ID is required/i); - }); - - it('should fail if question_id is not a positive integer', () => { - const { error } = SubmitQuizValidator.validate({ - answers: [{ question_id: -1, choice_id: 1 }], - }); - expect(error).toBeDefined(); - }); -}); diff --git a/Backend/tests/unit/validators/Lessons.validator.test.ts b/Backend/tests/unit/validators/Lessons.validator.test.ts deleted file mode 100644 index 81ef5471..00000000 --- a/Backend/tests/unit/validators/Lessons.validator.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { SetYouTubeVideoValidator } from '@/validators/Lessons.validator'; - -describe('SetYouTubeVideoValidator', () => { - it('should pass with valid youtube_video_id and video_title', () => { - const { error } = SetYouTubeVideoValidator.validate({ - youtube_video_id: 'dQw4w9WgXcQ', - video_title: 'Introduction to TypeScript', - }); - expect(error).toBeUndefined(); - }); - - it('should fail without youtube_video_id', () => { - const { error } = SetYouTubeVideoValidator.validate({ - video_title: 'Intro to TS', - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/YouTube video ID is required/i); - }); - - it('should fail with empty youtube_video_id string', () => { - const { error } = SetYouTubeVideoValidator.validate({ - youtube_video_id: '', - video_title: 'Intro to TS', - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/cannot be empty/i); - }); - - it('should fail without video_title', () => { - const { error } = SetYouTubeVideoValidator.validate({ - youtube_video_id: 'dQw4w9WgXcQ', - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Video title is required/i); - }); - - it('should fail with empty video_title string', () => { - const { error } = SetYouTubeVideoValidator.validate({ - youtube_video_id: 'dQw4w9WgXcQ', - video_title: '', - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/cannot be empty/i); - }); -}); diff --git a/Backend/tests/unit/validators/announcements.validator.test.ts b/Backend/tests/unit/validators/announcements.validator.test.ts deleted file mode 100644 index 9e186ca2..00000000 --- a/Backend/tests/unit/validators/announcements.validator.test.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { - CreateAnnouncementValidator, - UpdateAnnouncementValidator, -} from '@/validators/announcements.validator'; - -describe('CreateAnnouncementValidator', () => { - const validPayload = { - title: { th: 'ประกาศใหม่', en: 'New Announcement' }, - content: { th: 'เนื้อหา', en: 'Content' }, - status: 'DRAFT', - is_pinned: false, - }; - - it('should pass with all required fields', () => { - const { error } = CreateAnnouncementValidator.validate(validPayload); - expect(error).toBeUndefined(); - }); - - it('should pass with optional published_at as ISO date', () => { - const { error } = CreateAnnouncementValidator.validate({ - ...validPayload, - published_at: '2026-03-01T00:00:00.000Z', - }); - expect(error).toBeUndefined(); - }); - - it('should fail with invalid published_at format', () => { - const { error } = CreateAnnouncementValidator.validate({ - ...validPayload, - published_at: '01-03-2026', - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/ISO date/i); - }); - - it('should fail with invalid status', () => { - const { error } = CreateAnnouncementValidator.validate({ - ...validPayload, - status: 'HIDDEN', - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/DRAFT, PUBLISHED, ARCHIVED/i); - }); - - it('should pass with PUBLISHED status', () => { - const { error } = CreateAnnouncementValidator.validate({ - ...validPayload, - status: 'PUBLISHED', - }); - expect(error).toBeUndefined(); - }); - - it('should pass with ARCHIVED status', () => { - const { error } = CreateAnnouncementValidator.validate({ - ...validPayload, - status: 'ARCHIVED', - }); - expect(error).toBeUndefined(); - }); - - it('should fail without title', () => { - const { error } = CreateAnnouncementValidator.validate({ - ...validPayload, - title: undefined, - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Title is required/i); - }); - - it('should fail without content', () => { - const { error } = CreateAnnouncementValidator.validate({ - ...validPayload, - content: undefined, - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Content is required/i); - }); - - it('should fail without is_pinned', () => { - const { error } = CreateAnnouncementValidator.validate({ - ...validPayload, - is_pinned: undefined, - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/is_pinned is required/i); - }); -}); - -describe('UpdateAnnouncementValidator', () => { - it('should pass with empty object (all optional)', () => { - const { error } = UpdateAnnouncementValidator.validate({}); - expect(error).toBeUndefined(); - }); - - it('should pass with partial update', () => { - const { error } = UpdateAnnouncementValidator.validate({ - status: 'PUBLISHED', - is_pinned: true, - }); - expect(error).toBeUndefined(); - }); - - it('should fail with invalid status', () => { - const { error } = UpdateAnnouncementValidator.validate({ status: 'DELETED' }); - expect(error).toBeDefined(); - }); - - it('should fail with invalid published_at format', () => { - const { error } = UpdateAnnouncementValidator.validate({ - published_at: 'not-a-date', - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/ISO date/i); - }); -}); diff --git a/Backend/tests/unit/validators/auth.validator.test.ts b/Backend/tests/unit/validators/auth.validator.test.ts deleted file mode 100644 index 2ef38652..00000000 --- a/Backend/tests/unit/validators/auth.validator.test.ts +++ /dev/null @@ -1,246 +0,0 @@ -import { - loginSchema, - registerSchema, - refreshTokenSchema, - resetPasswordSchema, - changePasswordSchema, - resetRequestSchema, -} from '@/validators/auth.validator'; - -// ============================================================ -// loginSchema -// ============================================================ - -describe('loginSchema', () => { - it('should pass with valid email and password', () => { - const { error } = loginSchema.validate({ - email: 'user@example.com', - password: 'password123', - }); - expect(error).toBeUndefined(); - }); - - it('should fail with invalid email format', () => { - const { error } = loginSchema.validate({ - email: 'not-an-email', - password: 'password123', - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/valid email/i); - }); - - it('should fail without email', () => { - const { error } = loginSchema.validate({ password: 'password123' }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Email is required/i); - }); - - it('should fail without password', () => { - const { error } = loginSchema.validate({ email: 'user@example.com' }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Password is required/i); - }); - - it('should fail with password shorter than 6 characters', () => { - const { error } = loginSchema.validate({ - email: 'user@example.com', - password: '12345', - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/at least 6 characters/i); - }); -}); - -// ============================================================ -// registerSchema -// ============================================================ - -describe('registerSchema', () => { - const validPayload = { - username: 'john_doe', - email: 'john@example.com', - password: 'securepass', - first_name: 'John', - last_name: 'Doe', - phone: '0812345678', - }; - - it('should pass with all required fields', () => { - const { error } = registerSchema.validate(validPayload); - expect(error).toBeUndefined(); - }); - - it('should pass with optional prefix', () => { - const { error } = registerSchema.validate({ - ...validPayload, - prefix: { th: 'นาย', en: 'Mr.' }, - }); - expect(error).toBeUndefined(); - }); - - it('should fail if username has invalid characters', () => { - const { error } = registerSchema.validate({ - ...validPayload, - username: 'john doe!', - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/letters, numbers, and underscores/i); - }); - - it('should fail if username is too short (< 3 chars)', () => { - const { error } = registerSchema.validate({ - ...validPayload, - username: 'ab', - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/at least 3 characters/i); - }); - - it('should fail if username is too long (> 50 chars)', () => { - const { error } = registerSchema.validate({ - ...validPayload, - username: 'a'.repeat(51), - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/not exceed 50 characters/i); - }); - - it('should fail with invalid email', () => { - const { error } = registerSchema.validate({ - ...validPayload, - email: 'bad-email', - }); - expect(error).toBeDefined(); - }); - - it('should fail if phone is too short (< 10 chars)', () => { - const { error } = registerSchema.validate({ - ...validPayload, - phone: '081234', - }); - expect(error).toBeDefined(); - }); - - it('should fail without first_name', () => { - const { error } = registerSchema.validate({ - ...validPayload, - first_name: undefined, - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/First name is required/i); - }); - - it('should fail without last_name', () => { - const { error } = registerSchema.validate({ - ...validPayload, - last_name: undefined, - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Last name is required/i); - }); -}); - -// ============================================================ -// refreshTokenSchema -// ============================================================ - -describe('refreshTokenSchema', () => { - it('should pass with a valid refreshToken', () => { - const { error } = refreshTokenSchema.validate({ - refreshToken: 'some-refresh-token-string', - }); - expect(error).toBeUndefined(); - }); - - it('should fail without refreshToken', () => { - const { error } = refreshTokenSchema.validate({}); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Refresh token is required/i); - }); -}); - -// ============================================================ -// resetPasswordSchema -// ============================================================ - -describe('resetPasswordSchema', () => { - it('should pass with valid token and password', () => { - const { error } = resetPasswordSchema.validate({ - token: 'reset-token-abc', - password: 'newpassword', - }); - expect(error).toBeUndefined(); - }); - - it('should fail without token', () => { - const { error } = resetPasswordSchema.validate({ password: 'newpassword' }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Reset token is required/i); - }); - - it('should fail with password too short', () => { - const { error } = resetPasswordSchema.validate({ - token: 'abc', - password: '12345', - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/at least 6 characters/i); - }); - - it('should fail with password too long (> 100 chars)', () => { - const { error } = resetPasswordSchema.validate({ - token: 'abc', - password: 'a'.repeat(101), - }); - expect(error).toBeDefined(); - }); -}); - -// ============================================================ -// changePasswordSchema (auth) -// ============================================================ - -describe('changePasswordSchema (auth.validator)', () => { - it('should pass with valid old and new passwords', () => { - const { error } = changePasswordSchema.validate({ - oldPassword: 'oldpass123', - newPassword: 'newpass456', - }); - expect(error).toBeUndefined(); - }); - - it('should fail without oldPassword', () => { - const { error } = changePasswordSchema.validate({ newPassword: 'newpass456' }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Password is required/i); - }); - - it('should fail without newPassword', () => { - const { error } = changePasswordSchema.validate({ oldPassword: 'oldpass123' }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Password is required/i); - }); -}); - -// ============================================================ -// resetRequestSchema -// ============================================================ - -describe('resetRequestSchema', () => { - it('should pass with valid email', () => { - const { error } = resetRequestSchema.validate({ email: 'user@example.com' }); - expect(error).toBeUndefined(); - }); - - it('should fail without email', () => { - const { error } = resetRequestSchema.validate({}); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Email is required/i); - }); - - it('should fail with invalid email', () => { - const { error } = resetRequestSchema.validate({ email: 'not-email' }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/valid email/i); - }); -}); diff --git a/Backend/tests/unit/validators/categories.validator.test.ts b/Backend/tests/unit/validators/categories.validator.test.ts deleted file mode 100644 index 64249c2a..00000000 --- a/Backend/tests/unit/validators/categories.validator.test.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { - CreateCategoryValidator, - UpdateCategoryValidator, -} from '@/validators/categories.validator'; - -describe('CreateCategoryValidator', () => { - const validPayload = { - name: { th: 'การพัฒนาเว็บ', en: 'Web Development' }, - slug: 'web-development', - description: { th: 'หมวดหมู่การพัฒนาเว็บ', en: 'Web development category' }, - }; - - it('should pass with all required fields', () => { - const { error } = CreateCategoryValidator.validate(validPayload); - expect(error).toBeUndefined(); - }); - - it('should pass with optional created_by', () => { - const { error } = CreateCategoryValidator.validate({ - ...validPayload, - created_by: 1, - }); - expect(error).toBeUndefined(); - }); - - it('should fail without name', () => { - const { error } = CreateCategoryValidator.validate({ - ...validPayload, - name: undefined, - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Name is required/i); - }); - - it('should fail without name.th', () => { - const { error } = CreateCategoryValidator.validate({ - ...validPayload, - name: { en: 'Web Development' }, - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Thai name is required/i); - }); - - it('should fail without slug', () => { - const { error } = CreateCategoryValidator.validate({ - ...validPayload, - slug: undefined, - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Slug is required/i); - }); - - it('should fail with invalid slug format (uppercase)', () => { - const { error } = CreateCategoryValidator.validate({ - ...validPayload, - slug: 'Web-Development', - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/lowercase with hyphens/i); - }); - - it('should fail with invalid slug format (spaces)', () => { - const { error } = CreateCategoryValidator.validate({ - ...validPayload, - slug: 'web development', - }); - expect(error).toBeDefined(); - }); - - it('should pass with valid slug formats', () => { - const slugs = ['web', 'web-dev', 'web-development-101']; - for (const slug of slugs) { - const { error } = CreateCategoryValidator.validate({ - ...validPayload, - slug, - }); - expect(error).toBeUndefined(); - } - }); - - it('should fail without description', () => { - const { error } = CreateCategoryValidator.validate({ - ...validPayload, - description: undefined, - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Description is required/i); - }); -}); - -describe('UpdateCategoryValidator', () => { - it('should pass with only required id', () => { - const { error } = UpdateCategoryValidator.validate({ id: 1 }); - expect(error).toBeUndefined(); - }); - - it('should pass with all optional fields', () => { - const { error } = UpdateCategoryValidator.validate({ - id: 1, - name: { th: 'ใหม่', en: 'New' }, - slug: 'new-category', - description: { th: 'คำอธิบาย', en: 'Description' }, - }); - expect(error).toBeUndefined(); - }); - - it('should fail without id', () => { - const { error } = UpdateCategoryValidator.validate({ name: { th: 'ใหม่', en: 'New' } }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Category ID is required/i); - }); - - it('should fail with invalid slug format', () => { - const { error } = UpdateCategoryValidator.validate({ - id: 1, - slug: 'Invalid Slug!', - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/lowercase with hyphens/i); - }); -}); diff --git a/Backend/tests/unit/validators/user.validator.test.ts b/Backend/tests/unit/validators/user.validator.test.ts deleted file mode 100644 index 349ceaa4..00000000 --- a/Backend/tests/unit/validators/user.validator.test.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { - profileUpdateSchema, - changePasswordSchema, -} from '@/validators/user.validator'; - -describe('profileUpdateSchema', () => { - it('should pass with empty object (all optional)', () => { - const { error } = profileUpdateSchema.validate({}); - expect(error).toBeUndefined(); - }); - - it('should pass with all fields', () => { - const { error } = profileUpdateSchema.validate({ - prefix: { th: 'นาย', en: 'Mr.' }, - first_name: 'John', - last_name: 'Doe', - phone: '0812345678', - avatar_url: 'https://example.com/avatar.jpg', - birth_date: new Date('1990-01-01'), - }); - expect(error).toBeUndefined(); - }); - - it('should pass with partial update (first_name only)', () => { - const { error } = profileUpdateSchema.validate({ first_name: 'Jane' }); - expect(error).toBeUndefined(); - }); - - it('should fail if first_name is empty string', () => { - const { error } = profileUpdateSchema.validate({ first_name: '' }); - expect(error).toBeDefined(); - }); - - it('should fail if first_name exceeds 100 characters', () => { - const { error } = profileUpdateSchema.validate({ first_name: 'a'.repeat(101) }); - expect(error).toBeDefined(); - }); - - it('should fail if phone is too short (< 10 chars)', () => { - const { error } = profileUpdateSchema.validate({ phone: '081234' }); - expect(error).toBeDefined(); - }); - - it('should fail if phone exceeds 15 characters', () => { - const { error } = profileUpdateSchema.validate({ phone: '1'.repeat(16) }); - expect(error).toBeDefined(); - }); - - it('should pass with valid birth_date', () => { - const { error } = profileUpdateSchema.validate({ birth_date: new Date('2000-06-15') }); - expect(error).toBeUndefined(); - }); -}); - -describe('changePasswordSchema (user.validator)', () => { - it('should pass with valid old and new passwords', () => { - const { error } = changePasswordSchema.validate({ - oldPassword: 'oldpass123', - newPassword: 'newpass456', - }); - expect(error).toBeUndefined(); - }); - - it('should fail without oldPassword', () => { - const { error } = changePasswordSchema.validate({ newPassword: 'newpass456' }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Old password is required/i); - }); - - it('should fail without newPassword', () => { - const { error } = changePasswordSchema.validate({ oldPassword: 'oldpass123' }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/New password is required/i); - }); - - it('should fail if oldPassword is shorter than 6 chars', () => { - const { error } = changePasswordSchema.validate({ - oldPassword: '12345', - newPassword: 'newpass456', - }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/at least 6 characters/i); - }); - - it('should fail if newPassword is shorter than 6 chars', () => { - const { error } = changePasswordSchema.validate({ - oldPassword: 'oldpass123', - newPassword: '123', - }); - expect(error).toBeDefined(); - }); - - it('should fail if oldPassword exceeds 100 characters', () => { - const { error } = changePasswordSchema.validate({ - oldPassword: 'a'.repeat(101), - newPassword: 'newpass456', - }); - expect(error).toBeDefined(); - }); -}); diff --git a/Backend/tests/unit/validators/usermanagement.validator.test.ts b/Backend/tests/unit/validators/usermanagement.validator.test.ts deleted file mode 100644 index 6d57de81..00000000 --- a/Backend/tests/unit/validators/usermanagement.validator.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { - getUserByIdValidator, - updateUserRoleValidator, -} from '@/validators/usermanagement.validator'; - -describe('getUserByIdValidator', () => { - it('should pass with valid id', () => { - const { error } = getUserByIdValidator.validate({ id: 1 }); - expect(error).toBeUndefined(); - }); - - it('should fail without id', () => { - const { error } = getUserByIdValidator.validate({}); - expect(error).toBeDefined(); - }); - - it('should fail with non-numeric id', () => { - const { error } = getUserByIdValidator.validate({ id: 'abc' }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/ID must be a number/i); - }); - - it('should pass with id = 0', () => { - // Joi number() allows 0 by default unless positive() is specified - const { error } = getUserByIdValidator.validate({ id: 0 }); - expect(error).toBeUndefined(); - }); -}); - -describe('updateUserRoleValidator', () => { - it('should pass with valid id and role_id', () => { - const { error } = updateUserRoleValidator.validate({ id: 1, role_id: 2 }); - expect(error).toBeUndefined(); - }); - - it('should fail without id', () => { - const { error } = updateUserRoleValidator.validate({ role_id: 2 }); - expect(error).toBeDefined(); - }); - - it('should fail without role_id', () => { - const { error } = updateUserRoleValidator.validate({ id: 1 }); - expect(error).toBeDefined(); - // Joi uses field name in message when custom messages don't match - expect(error?.details[0].message).toContain('role_id'); - }); - - it('should fail with non-numeric role_id', () => { - const { error } = updateUserRoleValidator.validate({ id: 1, role_id: 'admin' }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/Role ID must be a number/i); - }); - - it('should fail with non-numeric id', () => { - const { error } = updateUserRoleValidator.validate({ id: 'abc', role_id: 1 }); - expect(error).toBeDefined(); - expect(error?.details[0].message).toMatch(/ID must be a number/i); - }); -}); diff --git a/Backend/tsconfig.test.json b/Backend/tsconfig.test.json deleted file mode 100644 index 5eb9ef95..00000000 --- a/Backend/tsconfig.test.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "rootDir": ".", - "baseUrl": ".", - "paths": { - "@/*": [ - "src/*" - ] - }, - "types": [ - "node", - "jest" - ] - }, - "include": [ - "src/**/*", - "tests/**/*" - ] -} \ No newline at end of file diff --git a/Frontend-Learner/.nuxtrc b/Frontend-Learner/.nuxtrc deleted file mode 100644 index 1e1fe833..00000000 --- a/Frontend-Learner/.nuxtrc +++ /dev/null @@ -1 +0,0 @@ -setups.@nuxt/test-utils="4.0.0" \ No newline at end of file diff --git a/Frontend-Learner/app.vue b/Frontend-Learner/app.vue index 1dfac678..a1ac35c7 100644 --- a/Frontend-Learner/app.vue +++ b/Frontend-Learner/app.vue @@ -1,34 +1,27 @@ -