From 6d6d6801db5925bfeba08904a99d512a306f2f63 Mon Sep 17 00:00:00 2001 From: Nolan Hellyer Date: Sat, 31 May 2025 11:48:00 -0700 Subject: [PATCH] add istanbul for coverage, unit tests for resposne bodies, add coverage here and there --- .gitignore | 3 +- package-lock.json | 463 ++++++++++++++++++++- package.json | 1 + src/lib/me.ts | 3 +- src/lib/server/test/Game.spec.ts | 16 +- src/lib/server/test/responseBodies.spec.ts | 98 +++++ src/lib/test/GameData.spec.ts | 28 +- src/lib/test/GameEvent.spec.ts | 29 +- src/lib/test/me.spec.ts | 30 ++ vite.config.ts | 1 + 10 files changed, 626 insertions(+), 46 deletions(-) create mode 100644 src/lib/server/test/responseBodies.spec.ts create mode 100644 src/lib/test/me.spec.ts diff --git a/.gitignore b/.gitignore index 4d6ab11..2623d3c 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,5 @@ vite.config.ts.timestamp-* # Development cert -.vscode \ No newline at end of file +.vscode +coverage \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 750ebe3..ffce73d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "@types/bcrypt": "^5.0.2", "@types/jsonwebtoken": "^9.0.7", "@types/node": "^22.10.7", + "@vitest/coverage-istanbul": "^2.1.8", "@vitest/coverage-v8": "^2.1.8", "eslint": "^9.7.0", "eslint-config-prettier": "^9.1.0", @@ -50,10 +51,169 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz", + "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", + "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helpers": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", + "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz", + "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", "engines": { @@ -61,23 +221,47 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/parser": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", - "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", + "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.7" + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", + "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" }, "bin": { "parser": "bin/babel-parser.js" @@ -86,15 +270,59 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/types": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", - "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", + "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", + "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1567,6 +1795,31 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@vitest/coverage-istanbul": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/coverage-istanbul/-/coverage-istanbul-2.1.8.tgz", + "integrity": "sha512-cSaCd8KcWWvgDwEJSXm0NEWZ1YTiJzjicKHy+zOEbUm0gjbbkz+qJf1p8q71uBzSlS7vdnZA8wRLeiwVE3fFTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@istanbuljs/schema": "^0.1.3", + "debug": "^4.3.7", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-instrument": "^6.0.3", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.1.7", + "magicast": "^0.3.5", + "test-exclude": "^7.0.1", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "2.1.8" + } + }, "node_modules/@vitest/coverage-v8": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.8.tgz", @@ -1906,6 +2159,39 @@ "node": ">=8" } }, + "node_modules/browserslist": { + "version": "4.24.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz", + "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001716", + "electron-to-chromium": "^1.5.149", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/bson": { "version": "6.10.1", "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.1.tgz", @@ -1941,6 +2227,27 @@ "node": ">=6" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001718", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz", + "integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, "node_modules/chai": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", @@ -2061,6 +2368,13 @@ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "license": "ISC" }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", @@ -2181,6 +2495,13 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/electron-to-chromium": { + "version": "1.5.152", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.152.tgz", + "integrity": "sha512-xBOfg/EBaIlVsHipHl2VdTPJRSvErNUaqW8ejTq5OlOlIYx1wOllCHsAvAIrr55jD1IYEfdR86miUEt8H5IeJg==", + "dev": true, + "license": "ISC" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -2233,6 +2554,16 @@ "@esbuild/win32-x64": "0.21.5" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2737,6 +3068,16 @@ "node": ">=10" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -2975,6 +3316,23 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/istanbul-lib-report": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", @@ -3051,6 +3409,13 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3064,6 +3429,19 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -3085,6 +3463,19 @@ "dev": true, "license": "MIT" }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -3568,6 +3959,13 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -4689,6 +5087,37 @@ "dev": true, "license": "MIT" }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/package.json b/package.json index 52e9624..c367d77 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@types/bcrypt": "^5.0.2", "@types/jsonwebtoken": "^9.0.7", "@types/node": "^22.10.7", + "@vitest/coverage-istanbul": "^2.1.8", "@vitest/coverage-v8": "^2.1.8", "eslint": "^9.7.0", "eslint-config-prettier": "^9.1.0", diff --git a/src/lib/me.ts b/src/lib/me.ts index 347c831..27b53bc 100644 --- a/src/lib/me.ts +++ b/src/lib/me.ts @@ -1,4 +1,4 @@ -import { hasProperty } from "./validation"; +import { hasProperty, hasOnlyKeys } from "./validation"; export interface Me { id: string; @@ -10,5 +10,6 @@ export function isMe(target: unknown): target is Me { if (!hasProperty(target, "id", "string")) return false; if (!hasProperty(target, "role", "string")) return false; if (!hasProperty(target, "username", "string")) return false; + if (!hasOnlyKeys(target, ["id", "role", "username"])) return false; return true; } diff --git a/src/lib/server/test/Game.spec.ts b/src/lib/server/test/Game.spec.ts index 91fc5a9..77a0420 100644 --- a/src/lib/server/test/Game.spec.ts +++ b/src/lib/server/test/Game.spec.ts @@ -1,19 +1,21 @@ import { describe, it } from "vitest"; import { Game } from "$lib/server/Game"; -import { deepEqual, ok, throws } from "node:assert/strict"; -import { createId, idFromString, stringFromId } from "$lib/Id"; -import { equal } from "node:assert"; +import { ok, throws } from "node:assert/strict"; +import { createId } from "$lib/Id"; +import { equal, deepEqual } from "node:assert"; describe("Game", () => { - const idString = stringFromId(createId()); + const idString = createId(); describe("addPlayer", () => { it("should push a player id into the player array", () => { const game = new Game(); - deepEqual(game.players, []); + const user = "Mr. User"; - game.addPlayer(idFromString(idString)); + equal(game.players.length, 0); + + game.addPlayer(idString, user); equal(game.players.length, 1); - equal(stringFromId(game.players[0]), idString); + deepEqual(game.players[0], { id: idString, username: user }); }); }); diff --git a/src/lib/server/test/responseBodies.spec.ts b/src/lib/server/test/responseBodies.spec.ts new file mode 100644 index 0000000..1101172 --- /dev/null +++ b/src/lib/server/test/responseBodies.spec.ts @@ -0,0 +1,98 @@ +import { describe, it } from "vitest"; +import { + listResponse, + singleResponse, + createdResponse, + tokenResponse, + badRequestResponse, + notFoundResponse, + serverErrorResponse, + unauthorizedResponse, + forbiddenResponse, +} from "../responseBodies"; +import { deepEqual, equal } from "assert"; +import { createId } from "$lib/Id"; + +describe("responseBodies", () => { + describe("the following response body functions return expected response", () => { + it("listResponse", async () => { + const expected = [{ some: "item", another: "thing" }]; + const { body, status } = await unwrapResponse(listResponse, expected); + + deepEqual(body, { items: expected }); + equal(status, 200); + }); + + it("singleResponse", async () => { + const expected = { some: "item" }; + const { body, status } = await unwrapResponse(singleResponse, expected); + + deepEqual(body, { item: expected }); + equal(status, 200); + }); + + it("createdResponse", async () => { + const expectedId = createId(); + const { body, status } = await unwrapResponse(createdResponse, expectedId); + + deepEqual(body, { item: expectedId }); + equal(status, 201); + }); + + it("tokenResponse", async () => { + const token = "some-generated-token"; + const { body, status } = await unwrapResponse(tokenResponse, token); + + deepEqual(body, { access_token: token }); + equal(status, 200); + }); + + it("badRequestResponse", async () => { + const errorString = "something bad happened"; + let { body, status } = await unwrapResponse(badRequestResponse, errorString); + + deepEqual(body, { error: errorString }); + equal(status, 400); + + const res = await unwrapResponse(badRequestResponse); + body = res.body; + status = res.status; + + deepEqual(body, { error: "Bad Request" }); + equal(status, 400); + }); + + it("notFoundResponse", async () => { + const { body, status } = await unwrapResponse(notFoundResponse); + + deepEqual(body, { error: "Not Found" }); + equal(status, 404); + }); + + it("serverErrorResponse", async () => { + const { body, status } = await unwrapResponse(serverErrorResponse); + + deepEqual(body, { error: "Unexpected Server Error" }); + equal(status, 500); + }); + + it("unauthorizedResponse", async () => { + const { body, status } = await unwrapResponse(unauthorizedResponse); + + deepEqual(body, { error: "Unauthorized" }); + equal(status, 401); + }); + + it("forbiddenResponse", async () => { + const { body, status } = await unwrapResponse(forbiddenResponse); + + deepEqual(body, { error: "Forbidden" }); + equal(status, 403); + }); + }); +}); + +async function unwrapResponse(fn: Function, body?: unknown) { + const res = fn(body); + return { status: res.status, body: await res.json() }; +} diff --git a/src/lib/test/GameData.spec.ts b/src/lib/test/GameData.spec.ts index 9ef4ada..a1b9f31 100644 --- a/src/lib/test/GameData.spec.ts +++ b/src/lib/test/GameData.spec.ts @@ -1,34 +1,44 @@ import { describe, it } from "vitest"; -import { type GameData, isGameData } from "$lib/GameData"; +import { isGameData } from "$lib/GameData"; import { equal, ok } from "node:assert/strict"; -import { createId, idFromString, stringFromId } from "$lib/Id"; +import { createId } from "$lib/Id"; describe("GameData", () => { - const idString = stringFromId(createId()); + const idString = createId(); describe("isGameData", () => { it("rejects a malformed object", () => { let data: unknown = { - players: [idFromString(idString), idString], + players: [idString, idString], isStarted: false, state: {}, }; equal(isGameData(data), false); data = { - players: [idFromString(idString)], + players: [idString], isStarted: null, state: {}, }; equal(isGameData(data), false); data = { - players: [idFromString(idString)], + players: [{ username: "Mr. User", id: idString }], isStarted: false, }; equal(isGameData(data), false); }); + it("rejects an object with a malformed players array", () => { + const data: unknown = { + players: [{ id: idString }], + state: {}, + isStarted: false, + }; + + equal(isGameData(data), false); + }); + it("rejects an object without a players property", () => { const data: unknown = { state: {}, @@ -40,7 +50,7 @@ describe("GameData", () => { it("rejects an object with extra properties", () => { const data: unknown = { - players: [idFromString(idString)], + players: [{ username: "Mr. User", id: idString }], isStarted: false, state: {}, extra: true, @@ -50,8 +60,8 @@ describe("GameData", () => { }); it("should accept a proper GameData object", () => { - const data: GameData = { - players: [idFromString(idString)], + const data: unknown = { + players: [{ username: "Mr. User", id: idString }], state: {}, isStarted: false, }; diff --git a/src/lib/test/GameEvent.spec.ts b/src/lib/test/GameEvent.spec.ts index 17c2c2b..d44a0af 100644 --- a/src/lib/test/GameEvent.spec.ts +++ b/src/lib/test/GameEvent.spec.ts @@ -15,7 +15,7 @@ import type { GameData } from "$lib/GameData"; import { describe, it } from "vitest"; import type { State } from "$lib/State"; import { doesNotThrow, deepStrictEqual, equal, ok, throws } from "assert"; -import { createId, idFromString, stringFromId } from "$lib/Id"; +import { createId } from "$lib/Id"; describe("Game Events", () => { describe("isGameEventData", () => { @@ -55,13 +55,20 @@ describe("Game Events", () => { }); describe("getGameEvent", () => { - const idString = stringFromId(createId()); - const anotherIdString = stringFromId(createId()); + const playerOne = { + id: createId(), + username: "Player One", + }; + + const playerTwo = { + id: createId(), + username: "Player Two", + }; it("should throw if the kind is unkown", () => { const data: GameData = { isStarted: false, - players: [idFromString(idString), idFromString(anotherIdString)], + players: [playerOne, playerTwo], state: {}, }; @@ -77,7 +84,7 @@ describe("Game Events", () => { it("should throw when SeatPlayers has the wrong number of players", () => { const data: GameData = { isStarted: true, - players: [idFromString(idString), idFromString(anotherIdString)], + players: [playerOne, playerTwo], state: {}, }; @@ -92,7 +99,7 @@ describe("Game Events", () => { it("should return a SeatPlayers object when the number of players is correct", () => { const data: GameData = { isStarted: true, - players: [idFromString(idString), idFromString(anotherIdString)], + players: [playerOne, playerTwo], state: {}, }; @@ -107,7 +114,7 @@ describe("Game Events", () => { it("should throw an error if the player passes a full roll with Roll", () => { const data: GameData = { isStarted: true, - players: [idFromString(idString), idFromString(anotherIdString)], + players: [playerOne, playerTwo], state: {}, }; @@ -123,7 +130,7 @@ describe("Game Events", () => { it("should return a Roll object with dice values when the player passes a die count as a value", () => { const data: GameData = { isStarted: true, - players: [idFromString(idString), idFromString(anotherIdString)], + players: [playerOne, playerTwo], state: {}, }; @@ -143,7 +150,7 @@ describe("Game Events", () => { it("should return the class that corresponds with a given kind", () => { const data: GameData = { isStarted: true, - players: [idFromString(idString), idFromString(anotherIdString)], + players: [playerOne, playerTwo], state: {}, }; @@ -754,7 +761,7 @@ describe("Game Events", () => { }); }); - it("should allow the player to hold 2 threes of a kind for 1,500 points", () => { + it("should allow the player to hold 2 threes of a kind for 1,600 points", () => { const state: State = { dice: [2, 2, 2, 3, 3, 3], playing: 0, @@ -770,7 +777,7 @@ describe("Game Events", () => { deepStrictEqual(state, { dieCount: 6, - heldScore: 1_500, + heldScore: 1_600, playing: 0, scores: [0, 0, 0], }); diff --git a/src/lib/test/me.spec.ts b/src/lib/test/me.spec.ts new file mode 100644 index 0000000..7aa8d26 --- /dev/null +++ b/src/lib/test/me.spec.ts @@ -0,0 +1,30 @@ +import { createId } from "$lib/Id"; +import { isMe, type Me } from "$lib/me"; +import { equal, ok } from "assert"; +import { describe, it } from "vitest"; + +describe("me", () => { + describe("isMe", () => { + const target: Me = { + id: createId(), + role: "test-role", + username: "Ms. User", + }; + + it("returns true when the target is shaped like a 'Me'", () => { + ok(isMe(target)); + }); + + it("returns false when the target is missing a property", () => { + const { id, role, username } = target; + equal(isMe({ role, username }), false); + equal(isMe({ id, username }), false); + equal(isMe({ id, role }), false); + }); + + it("returns false when the target has an extra property", () => { + const { id, role, username } = target; + equal(isMe({ id, role, username, extra: "something" }), false); + }); + }); +}); diff --git a/vite.config.ts b/vite.config.ts index 02f6dfe..59ddfc7 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -16,6 +16,7 @@ export default defineConfig({ test: { include: ["src/**/*.{test,spec}.{js,ts}"], coverage: { + provider: "istanbul", exclude: [...coverageConfigDefaults.exclude, "svelte.config.js"], }, environment: "jsdom",