From cface54888fd7c7ed9cd8944eb9bd0f0231b7d8a Mon Sep 17 00:00:00 2001 From: bewest <2492599-bewest@users.noreply.replit.com> Date: Mon, 19 Jan 2026 22:47:02 +0000 Subject: [PATCH] Optimize test suite by converting beforeEach to before and adding parallel testing scripts Updated test execution scripts in package.json to include parallel and fast test options. Converted several `beforeEach` hooks to `before` in test files for more efficient application setup. Added documentation for test optimizations. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 271f3cac-bab1-4e81-9982-f31b3b030aab Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: ac6eb857-be5a-454b-96cf-b9c4c57f058b Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7ea4278b-5c6c-4065-9cb8-f1013771318d/271f3cac-bab1-4e81-9982-f31b3b030aab/69H2O1r Replit-Helium-Checkpoint-Created: true --- docs/TEST-OPTIMIZATION-GUIDE.md | 186 ++++++++++++++++++++++ package-lock.json | 210 ++++++++++++++++--------- package.json | 3 + tests/XX_clean.test.js | 4 +- tests/api.aaps-client.test.js | 3 +- tests/api.activity.js | 4 +- tests/api.deduplication.test.js | 3 +- tests/api.devicestatus.test.js | 4 +- tests/api.food.js | 4 +- tests/api.partial-failures.test.js | 3 +- tests/api.profiles.test.js | 4 +- tests/api.treatments.test.js | 4 +- tests/api.v1-batch-operations.test.js | 3 +- tests/hooks.js | 8 +- tests/storage.shape-handling.test.js | 3 +- tests/websocket.shape-handling.test.js | 16 +- 16 files changed, 372 insertions(+), 90 deletions(-) create mode 100644 docs/TEST-OPTIMIZATION-GUIDE.md diff --git a/docs/TEST-OPTIMIZATION-GUIDE.md b/docs/TEST-OPTIMIZATION-GUIDE.md new file mode 100644 index 00000000..1ff013b4 --- /dev/null +++ b/docs/TEST-OPTIMIZATION-GUIDE.md @@ -0,0 +1,186 @@ +# Test Suite Optimization Guide + +This document describes optimizations made to the Nightscout test suite and recommendations for further improvements, especially in GitHub Actions CI/CD pipelines. + +## Optimizations Implemented + +### 1. Converted beforeEach to before for App Initialization + +**Files Modified:** +- `tests/api.partial-failures.test.js` +- `tests/api.deduplication.test.js` +- `tests/api.aaps-client.test.js` +- `tests/api.v1-batch-operations.test.js` +- `tests/websocket.shape-handling.test.js` +- `tests/storage.shape-handling.test.js` +- `tests/api.treatments.test.js` +- `tests/api.profiles.test.js` +- `tests/api.devicestatus.test.js` +- `tests/api.food.js` +- `tests/api.activity.js` +- `tests/XX_clean.test.js` + +**Impact:** Each file now boots the app once per test file instead of once per test. For files with 10+ tests, this saves significant time. + +### 2. Made clearRequireCache Optional + +**File Modified:** `tests/hooks.js` + +**Change:** The `clearRequireCache()` function now only runs when `CLEAR_REQUIRE_CACHE=true` is set. By default, the require cache is preserved between tests. + +**Rationale:** Clearing the require cache after every test forces complete re-initialization of all modules, which is expensive. Most tests don't need full isolation. + +**Usage:** +```bash +# Default (faster) - cache is preserved +npm test + +# Full isolation mode (slower but more isolated) +CLEAR_REQUIRE_CACHE=true npm test +``` + +### 3. Added Parallel Test Scripts + +**File Modified:** `package.json` + +**New Scripts:** +```json +{ + "test:fast": "env-cmd -f ./my.test.env mocha --timeout 5000 --require ./tests/hooks.js --exit --reporter min ./tests/*.test.js", + "test:parallel": "env-cmd -f ./my.test.env mocha --timeout 10000 --require ./tests/hooks.js --exit --parallel --jobs 4 ./tests/*.test.js", + "test:parallel:ci": "env-cmd -f ./tests/ci.test.env nyc --reporter=lcov --reporter=text-summary mocha --timeout 10000 --require ./tests/hooks.js --exit --parallel --jobs 4 ./tests/*.test.js" +} +``` + +## GitHub Actions Recommendations + +### Option 1: Sequential Testing with Optimizations (Recommended for CI Stability) + +The safest option for CI is to continue running tests sequentially but benefit from the `beforeEach→before` optimizations: + +```yaml +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '16' + cache: 'npm' + - name: Start MongoDB + uses: supercharge/mongodb-github-action@1.10.0 + - run: npm ci + - run: npm run test-ci +``` + +### Option 2: Parallel Testing (Experimental) + +**Warning:** Parallel testing shares the same MongoDB instance across workers. This can cause flaky tests if tests modify global state or use the same document IDs. The `test:parallel:ci` script enables `CLEAR_REQUIRE_CACHE=true` for isolation and limits to 2 jobs to reduce contention. + +```yaml +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '16' + cache: 'npm' + - name: Start MongoDB + uses: supercharge/mongodb-github-action@1.10.0 + - run: npm ci + - run: npm run test:parallel:ci +``` + +For true parallel isolation, use the matrix sharding approach below which runs each shard in a separate job with its own MongoDB instance. + +### Option 3: Test Sharding with Matrix Strategy + +Split tests across multiple runners for maximum parallelization: + +```yaml +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [1, 2, 3, 4] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '16' + cache: 'npm' + - name: Start MongoDB + uses: supercharge/mongodb-github-action@1.10.0 + - run: npm ci + - name: Run Tests (Shard ${{ matrix.shard }}) + run: | + # Get list of test files and run only this shard's portion + files=(tests/*.test.js) + total=${#files[@]} + per_shard=$(( (total + 3) / 4 )) + start=$(( (matrix.shard - 1) * per_shard )) + shard_files="${files[@]:$start:$per_shard}" + env-cmd -f ./tests/ci.test.env mocha --timeout 10000 --require ./tests/hooks.js --exit $shard_files +``` + +### Option 3: Dependency Caching + +Ensure npm dependencies are cached: + +```yaml +- uses: actions/setup-node@v4 + with: + node-version: '16' + cache: 'npm' +``` + +### Option 4: Conditional Test Execution + +Only run tests affected by changes: + +```yaml +- name: Get changed files + id: changed + uses: tj-actions/changed-files@v40 + with: + files: | + lib/** + tests/** + +- name: Run tests + if: steps.changed.outputs.any_changed == 'true' + run: npm run test:parallel:ci +``` + +## Performance Comparison + +| Mode | Estimated Time | Use Case | +|------|---------------|----------| +| `npm test` | Baseline | Local development | +| `npm run test:fast` | ~20% faster | Quick feedback, minimal output | +| `npm run test:parallel` | ~50-70% faster | Local with multiple cores | +| Matrix sharding (4 runners) | ~75% faster | CI/CD pipelines | + +## Monitoring Test Performance + +Use the built-in timing instrumentation: + +```bash +# Show slow test warnings +npm run test:timing + +# Lower threshold for more aggressive detection +SLOW_TEST_THRESHOLD=500 npm run test:timing +``` + +## Best Practices for New Tests + +1. **Use `before()` for app setup** - Not `beforeEach()` unless you specifically need fresh state +2. **Only clean data in `beforeEach()`** - Database cleanup should happen before tests, not app initialization +3. **Avoid `setTimeout` in tests** - Use polling patterns with `waitForConditionWithWarning()` from `tests/lib/test-helpers.js` +4. **Keep tests independent** - Each test should clean its own data before running diff --git a/package-lock.json b/package-lock.json index 14b1cc6a..e3e9231d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,7 +55,7 @@ "moment-timezone": "^0.5.31", "moment-timezone-data-webpack-plugin": "^1.5.0", "mongo-url-parser": "^1.0.2", - "mongodb": "^3.6.0", + "mongodb-legacy": "^5.0.0", "mongomock": "^0.1.2", "nightscout-connect": "^0.0.12", "node-cache": "^4.2.1", @@ -1732,6 +1732,16 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.5.tgz", + "integrity": "sha512-k64Lbyb7ycCSXHSLzxVdb2xsKGPMvYZfCICXvDsI8Z65CeWQzTEKS4YmGbnqw+U9RBvLPTsB6UCmwkgsDTGWIw==", + "license": "MIT", + "optional": true, + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@parse/node-apn": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@parse/node-apn/-/node-apn-5.2.3.tgz", @@ -1895,6 +1905,22 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", @@ -2705,16 +2731,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", - "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", - "license": "MIT", - "dependencies": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" - } - }, "node_modules/bn.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", @@ -2916,12 +2932,12 @@ } }, "node_modules/bson": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", - "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz", + "integrity": "sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g==", "license": "Apache-2.0", "engines": { - "node": ">=0.6.19" + "node": ">=14.20.1" } }, "node_modules/buffer": { @@ -4166,15 +4182,6 @@ "node": ">=0.4.0" } }, - "node_modules/denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.10" - } - }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -6509,6 +6516,15 @@ "node": ">= 0.10" } }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -9193,28 +9209,33 @@ "license": "Apache 2.0" }, "node_modules/mongodb": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.4.tgz", - "integrity": "sha512-K5q8aBqEXMwWdVNh94UQTwZ6BejVbFhh1uB6c5FKtPE9eUMZPUO3sRZdgIEcHSrAWmxzpG/FeODDKL388sqRmw==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.2.tgz", + "integrity": "sha512-H60HecKO4Bc+7dhOv4sJlgvenK4fQNqqUIlXxZYQNbfEWSALGAwGoyJd/0Qwk4TttFXUOHJ2ZJQe/52ScaUwtQ==", "license": "Apache-2.0", "dependencies": { - "bl": "^2.2.1", - "bson": "^1.1.4", - "denque": "^1.4.1", - "optional-require": "^1.1.8", - "safe-buffer": "^5.1.2" + "bson": "^5.5.0", + "mongodb-connection-string-url": "^2.6.0", + "socks": "^2.7.1" }, "engines": { - "node": ">=4" + "node": ">=14.20.1" }, "optionalDependencies": { - "saslprep": "^1.0.0" + "@mongodb-js/saslprep": "^1.1.0" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.0.0", + "kerberos": "^1.0.0 || ^2.0.0", + "mongodb-client-encryption": ">=2.3.0 <3", + "snappy": "^7.2.2" }, "peerDependenciesMeta": { - "aws4": { + "@aws-sdk/credential-providers": { "optional": true }, - "bson-ext": { + "@mongodb-js/zstd": { "optional": true }, "kerberos": { @@ -9223,14 +9244,67 @@ "mongodb-client-encryption": { "optional": true }, - "mongodb-extjson": { - "optional": true - }, "snappy": { "optional": true } } }, + "node_modules/mongodb-connection-string-url": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "license": "MIT", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "license": "MIT", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-legacy": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mongodb-legacy/-/mongodb-legacy-5.0.0.tgz", + "integrity": "sha512-q2G+MRwde6114bCAF/EZLmMXSsebIKMHmzsfOJq6M/Tj4gr3wLT50+rJsJNkiR0e0kjFx3dllWjqwRR1n11Zsw==", + "license": "Apache-2.0", + "dependencies": { + "mongodb": "^5.0.0" + }, + "engines": { + "node": ">=14.20.1" + } + }, "node_modules/mongomock": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/mongomock/-/mongomock-0.1.2.tgz", @@ -10083,18 +10157,6 @@ "opener": "bin/opener-bin.js" } }, - "node_modules/optional-require": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.10.tgz", - "integrity": "sha512-0r3OB9EIQsP+a5HVATHq2ExIy2q/Vaffoo4IAikW1spCYswhLxqWQS0i3GwS3AdY/OIP4SWZHLGz8CMU558PGw==", - "license": "Apache-2.0", - "dependencies": { - "require-at": "^1.0.6" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -11144,15 +11206,6 @@ "uuid": "bin/uuid" } }, - "node_modules/require-at": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", - "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==", - "license": "Apache-2.0", - "engines": { - "node": ">=4" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -11393,19 +11446,6 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, - "node_modules/saslprep": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", - "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", - "license": "MIT", - "optional": true, - "dependencies": { - "sparse-bitfield": "^3.0.3" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/sax": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.4.tgz", @@ -13276,6 +13316,16 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, "node_modules/socket.io": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.4.tgz", @@ -13344,6 +13394,20 @@ } } }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", diff --git a/package.json b/package.json index 1594df66..61fef550 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,9 @@ "test": "env-cmd -f ./my.test.env mocha --timeout 5000 --require ./tests/hooks.js --exit ./tests/*.test.js", "test-single": "env-cmd -f ./my.test.env mocha --timeout 5000 --require ./tests/hooks.js --exit ./tests/$TEST.test.js", "test-ci": "env-cmd -f ./tests/ci.test.env nyc --reporter=lcov --reporter=text-summary mocha --timeout 5000 --require ./tests/hooks.js --exit ./tests/*.test.js", + "test:fast": "env-cmd -f ./my.test.env mocha --timeout 5000 --require ./tests/hooks.js --exit --reporter min ./tests/*.test.js", + "test:parallel": "env-cmd -f ./my.test.env mocha --timeout 10000 --require ./tests/hooks.js --exit --parallel --jobs 4 ./tests/*.test.js", + "test:parallel:ci": "CLEAR_REQUIRE_CACHE=true env-cmd -f ./tests/ci.test.env nyc --reporter=lcov --reporter=text-summary mocha --timeout 10000 --require ./tests/hooks.js --exit --parallel --jobs 2 ./tests/*.test.js", "env": "env", "postinstall": "webpack --mode production --config webpack/webpack.config.js && npm run-script post-generate-keys", "bundle": "webpack --mode production --config webpack/webpack.config.js && npm run-script post-generate-keys", diff --git a/tests/XX_clean.test.js b/tests/XX_clean.test.js index 3549baa4..1dad2881 100644 --- a/tests/XX_clean.test.js +++ b/tests/XX_clean.test.js @@ -8,7 +8,9 @@ describe('Clean MONGO after tests', function ( ) { var self = this; var api = require('../lib/api/'); - beforeEach(function (done) { + + // Use before() instead of beforeEach() for app setup - boots once for all tests + before(function (done) { process.env.API_SECRET = 'this is my long pass phrase'; self.env = require('../lib/server/env')(); self.env.settings.authDefaultRoles = 'readable'; diff --git a/tests/api.aaps-client.test.js b/tests/api.aaps-client.test.js index d5c8c73f..b5b31337 100644 --- a/tests/api.aaps-client.test.js +++ b/tests/api.aaps-client.test.js @@ -33,7 +33,8 @@ describe('AAPS Client Document Handling', function() { const api_secret_hash = 'b723e97aa97846eb92d5264f084b2823f57c4aa1'; - beforeEach(function(done) { + // Use before() instead of beforeEach() for app setup - boots once for all tests + before(function(done) { process.env.API_SECRET = 'this is my long pass phrase'; self.env = require('../lib/server/env')(); self.env.settings.authDefaultRoles = 'readable'; diff --git a/tests/api.activity.js b/tests/api.activity.js index 2192ac26..cac2952d 100644 --- a/tests/api.activity.js +++ b/tests/api.activity.js @@ -11,7 +11,9 @@ describe('Activity API', function ( ) { var known = 'b723e97aa97846eb92d5264f084b2823f57c4aa1'; var api = require('../lib/api/'); - beforeEach(function (done) { + + // Use before() instead of beforeEach() for app setup - boots once for all tests + before(function (done) { process.env.API_SECRET = 'this is my long pass phrase'; self.env = require('../lib/server/env')(); self.env.settings.authDefaultRoles = 'readable'; diff --git a/tests/api.deduplication.test.js b/tests/api.deduplication.test.js index 586184df..b7b0de1b 100644 --- a/tests/api.deduplication.test.js +++ b/tests/api.deduplication.test.js @@ -31,7 +31,8 @@ describe('v1 API Deduplication Behavior', function() { const api_secret_hash = 'b723e97aa97846eb92d5264f084b2823f57c4aa1'; - beforeEach(function(done) { + // Use before() instead of beforeEach() for app setup - boots once for all tests + before(function(done) { process.env.API_SECRET = 'this is my long pass phrase'; self.env = require('../lib/server/env')(); self.env.settings.authDefaultRoles = 'readable'; diff --git a/tests/api.devicestatus.test.js b/tests/api.devicestatus.test.js index 50b6c202..1cbe7ee0 100644 --- a/tests/api.devicestatus.test.js +++ b/tests/api.devicestatus.test.js @@ -11,7 +11,9 @@ describe('Devicestatus API', function ( ) { var known = 'b723e97aa97846eb92d5264f084b2823f57c4aa1'; var api = require('../lib/api/'); - beforeEach(function (done) { + + // Use before() instead of beforeEach() for app setup - boots once for all tests + before(function (done) { process.env.API_SECRET = 'this is my long pass phrase'; self.env = require('../lib/server/env')(); self.env.settings.authDefaultRoles = 'readable'; diff --git a/tests/api.food.js b/tests/api.food.js index 4bfca8b0..e22ce240 100644 --- a/tests/api.food.js +++ b/tests/api.food.js @@ -11,7 +11,9 @@ describe('Food API', function ( ) { var known = 'b723e97aa97846eb92d5264f084b2823f57c4aa1'; var api = require('../lib/api/'); - beforeEach(function (done) { + + // Use before() instead of beforeEach() for app setup - boots once for all tests + before(function (done) { process.env.API_SECRET = 'this is my long pass phrase'; self.env = require('../lib/server/env')(); self.env.settings.authDefaultRoles = 'readable'; diff --git a/tests/api.partial-failures.test.js b/tests/api.partial-failures.test.js index 05a0b40c..b3d2b784 100644 --- a/tests/api.partial-failures.test.js +++ b/tests/api.partial-failures.test.js @@ -28,7 +28,8 @@ describe('v1 API Partial Failures and Edge Cases', function() { const api_secret_hash = 'b723e97aa97846eb92d5264f084b2823f57c4aa1'; - beforeEach(function(done) { + // Use before() instead of beforeEach() for app setup - boots once for all tests + before(function(done) { process.env.API_SECRET = 'this is my long pass phrase'; self.env = require('../lib/server/env')(); self.env.settings.authDefaultRoles = 'readable'; diff --git a/tests/api.profiles.test.js b/tests/api.profiles.test.js index 0a19d34b..7b455cce 100644 --- a/tests/api.profiles.test.js +++ b/tests/api.profiles.test.js @@ -11,7 +11,9 @@ describe('Profiles API', function ( ) { var known = 'b723e97aa97846eb92d5264f084b2823f57c4aa1'; var api = require('../lib/api/'); - beforeEach(function (done) { + + // Use before() instead of beforeEach() for app setup - boots once for all tests + before(function (done) { process.env.API_SECRET = 'this is my long pass phrase'; self.env = require('../lib/server/env')(); self.env.settings.authDefaultRoles = 'readable'; diff --git a/tests/api.treatments.test.js b/tests/api.treatments.test.js index 17241643..b4afcd57 100644 --- a/tests/api.treatments.test.js +++ b/tests/api.treatments.test.js @@ -13,7 +13,9 @@ describe('Treatment API', function ( ) { var api_secret_hash = 'b723e97aa97846eb92d5264f084b2823f57c4aa1'; var api = require('../lib/api/'); - beforeEach(function (done) { + + // Use before() instead of beforeEach() for app setup - boots once for all tests + before(function (done) { process.env.API_SECRET = 'this is my long pass phrase'; self.env = require('../lib/server/env')(); self.env.settings.authDefaultRoles = 'readable'; diff --git a/tests/api.v1-batch-operations.test.js b/tests/api.v1-batch-operations.test.js index a0b978b1..924c0d9e 100644 --- a/tests/api.v1-batch-operations.test.js +++ b/tests/api.v1-batch-operations.test.js @@ -32,7 +32,8 @@ describe('v1 API Batch Operations - MongoDB Modernization', function() { const api_secret_hash = 'b723e97aa97846eb92d5264f084b2823f57c4aa1'; - beforeEach(function(done) { + // Use before() instead of beforeEach() for app setup - boots once for all tests + before(function(done) { process.env.API_SECRET = 'this is my long pass phrase'; self.env = require('../lib/server/env')(); self.env.settings.authDefaultRoles = 'readable'; diff --git a/tests/hooks.js b/tests/hooks.js index baf6c0bf..3d30ba1b 100644 --- a/tests/hooks.js +++ b/tests/hooks.js @@ -4,6 +4,7 @@ var testHelpers = require('./lib/test-helpers'); var slowTestThreshold = parseInt(process.env.SLOW_TEST_THRESHOLD, 10) || 2000; var enableTimingWarnings = process.env.ENABLE_TIMING_WARNINGS === 'true'; +var enableRequireCacheClear = process.env.CLEAR_REQUIRE_CACHE === 'true'; var restoreSetTimeout = null; var testTimings = []; @@ -22,6 +23,9 @@ exports.mochaHooks = { longDelayThreshold: 100 }); } + if (enableRequireCacheClear) { + console.log('[CACHE CLEAR] Enabled - will clear require cache after each test (slower but more isolated)'); + } done(); }, @@ -46,7 +50,9 @@ exports.mochaHooks = { } } - clearRequireCache(); + if (enableRequireCacheClear) { + clearRequireCache(); + } done(); }, diff --git a/tests/storage.shape-handling.test.js b/tests/storage.shape-handling.test.js index e2e31253..ffca75e9 100644 --- a/tests/storage.shape-handling.test.js +++ b/tests/storage.shape-handling.test.js @@ -7,7 +7,8 @@ describe('Storage Layer Shape Handling - Direct Storage Tests', function () { this.timeout(15000); var self = this; - beforeEach(function (done) { + // Use before() instead of beforeEach() for app setup - boots once for all tests + before(function (done) { process.env.API_SECRET = 'this is my long pass phrase'; self.env = require('../lib/server/env')(); self.env.settings.authDefaultRoles = 'readable'; diff --git a/tests/websocket.shape-handling.test.js b/tests/websocket.shape-handling.test.js index 700b9e4f..ffb64826 100644 --- a/tests/websocket.shape-handling.test.js +++ b/tests/websocket.shape-handling.test.js @@ -12,7 +12,8 @@ describe('WebSocket Shape Handling - dbAdd Single vs Array Input', function () { var http = require('http'); var io = require('socket.io-client'); - beforeEach(function (done) { + // Use before() instead of beforeEach() for app setup - boots once for all tests + before(function (done) { process.env.API_SECRET = 'this is my long pass phrase'; self.env = require('../lib/server/env')(); self.env.settings.authDefaultRoles = 'readable'; @@ -37,10 +38,7 @@ describe('WebSocket Shape Handling - dbAdd Single vs Array Input', function () { }); }); - afterEach(function (done) { - if (self.socket) { - self.socket.disconnect(); - } + after(function (done) { if (self.server) { self.server.close(done); } else { @@ -48,6 +46,14 @@ describe('WebSocket Shape Handling - dbAdd Single vs Array Input', function () { } }); + afterEach(function (done) { + if (self.socket) { + self.socket.disconnect(); + self.socket = null; + } + done(); + }); + function connectAndAuthorize(callback) { var socket = io('http://localhost:' + self.port, { transports: ['websocket'],