From 37137f6ceffa5e83f2047765c26e5cfb5f6cc3e1 Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Wed, 21 Jan 2026 09:37:18 +0100 Subject: [PATCH 01/16] feat: add mongodb-memory-server for cross-platform testing --- package-lock.json | 355 ++++++++++++++++++++++++++++++++++++++++---- package.json | 5 +- test/globalSetup.ts | 27 ++++ vitest.config.ts | 1 + 4 files changed, 359 insertions(+), 29 deletions(-) create mode 100644 test/globalSetup.ts diff --git a/package-lock.json b/package-lock.json index 12483d878..5f4f067aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -94,6 +94,7 @@ "globals": "^16.5.0", "husky": "^9.1.7", "lint-staged": "^16.2.6", + "mongodb-memory-server": "^11.0.1", "nyc": "^17.1.0", "prettier": "^3.6.2", "quicktype": "^23.2.6", @@ -2772,9 +2773,10 @@ } }, "node_modules/@mongodb-js/saslprep": { - "version": "1.1.1", - "license": "MIT", - "optional": true, + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.5.tgz", + "integrity": "sha512-k64Lbyb7ycCSXHSLzxVdb2xsKGPMvYZfCICXvDsI8Z65CeWQzTEKS4YmGbnqw+U9RBvLPTsB6UCmwkgsDTGWIw==", + "devOptional": true, "dependencies": { "sparse-bitfield": "^3.0.3" } @@ -4978,6 +4980,15 @@ "node": ">=4.0" } }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/aggregate-error": { "version": "3.1.0", "dev": true, @@ -5312,6 +5323,15 @@ "version": "1.4.1", "license": "MIT" }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "dev": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/asynckit": { "version": "0.4.0", "license": "MIT" @@ -5375,6 +5395,20 @@ "version": "1.0.2", "license": "MIT" }, + "node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "dev": true, + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, "node_modules/base64-js": { "version": "1.5.1", "funding": [ @@ -7355,6 +7389,15 @@ "node": ">=0.8.x" } }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/execa": { "version": "4.1.0", "dev": true, @@ -7606,6 +7649,12 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -7829,14 +7878,15 @@ "license": "ISC" }, "node_modules/follow-redirects": { - "version": "1.15.6", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], - "license": "MIT", "engines": { "node": ">=4.0" }, @@ -8471,6 +8521,19 @@ "node": ">=0.10" } }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/human-signals": { "version": "1.1.1", "dev": true, @@ -8617,24 +8680,13 @@ } }, "node_modules/ip-address": { - "version": "9.0.5", - "license": "MIT", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", "engines": { "node": ">= 12" } }, - "node_modules/ip-address/node_modules/jsbn": { - "version": "1.1.0", - "license": "MIT" - }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "license": "BSD-3-Clause" - }, "node_modules/ipaddr.js": { "version": "1.9.1", "license": "MIT", @@ -10403,8 +10455,8 @@ }, "node_modules/memory-pager": { "version": "1.5.0", - "license": "MIT", - "optional": true + "devOptional": true, + "license": "MIT" }, "node_modules/meow": { "version": "12.1.1", @@ -10628,6 +10680,182 @@ "whatwg-url": "^11.0.0" } }, + "node_modules/mongodb-memory-server": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/mongodb-memory-server/-/mongodb-memory-server-11.0.1.tgz", + "integrity": "sha512-nUlKovSJZBh7q5hPsewFRam9H66D08Ne18nyknkNalfXMPtK1Og3kOcuqQhcX88x/pghSZPIJHrLbxNFW3OWiw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "mongodb-memory-server-core": "11.0.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/mongodb-memory-server-core": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/mongodb-memory-server-core/-/mongodb-memory-server-core-11.0.1.tgz", + "integrity": "sha512-IcIb2S9Xf7Lmz43Z1ZujMqNg7PU5Q7yn+4wOnu7l6pfeGPkEmlqzV1hIbroVx8s4vXhPB1oMGC1u8clW7aj3Xw==", + "dev": true, + "dependencies": { + "async-mutex": "^0.5.0", + "camelcase": "^6.3.0", + "debug": "^4.4.3", + "find-cache-dir": "^3.3.2", + "follow-redirects": "^1.15.11", + "https-proxy-agent": "^7.0.6", + "mongodb": "^7.0.0", + "new-find-package-json": "^2.0.0", + "semver": "^7.7.3", + "tar-stream": "^3.1.7", + "tslib": "^2.8.1", + "yauzl": "^3.2.0" + }, + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/@types/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==", + "dev": true, + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/bson": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-7.1.1.tgz", + "integrity": "sha512-TtJgBB+QyOlWjrbM+8bRgH84VM/xrDjyBFgSgGrfZF4xvt6gbEDtcswm27Tn9F9TWsjQybxT8b8VpCP/oJK4Dw==", + "dev": true, + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/mongodb": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.0.0.tgz", + "integrity": "sha512-vG/A5cQrvGGvZm2mTnCSz1LUcbOPl83hfB6bxULKQ8oFZauyox/2xbZOoGNl+64m8VBrETkdGCDBdOsCr3F3jg==", + "dev": true, + "dependencies": { + "@mongodb-js/saslprep": "^1.3.0", + "bson": "^7.0.0", + "mongodb-connection-string-url": "^7.0.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.806.0", + "@mongodb-js/zstd": "^7.0.0", + "gcp-metadata": "^7.0.1", + "kerberos": "^7.0.0", + "mongodb-client-encryption": ">=7.0.0 <7.1.0", + "snappy": "^7.3.2", + "socks": "^2.8.6" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-memory-server-core/node_modules/mongodb-connection-string-url": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.0.tgz", + "integrity": "sha512-irhhjRVLE20hbkRl4zpAYLnDMM+zIZnp0IDB9akAFFUZp/3XdOfwwddc7y6cNvF2WCEtfTYRwYbIfYa2kVY0og==", + "dev": true, + "dependencies": { + "@types/whatwg-url": "^13.0.0", + "whatwg-url": "^14.1.0" + }, + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/yauzl": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz", + "integrity": "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "pend": "~1.2.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/ms": { "version": "2.1.3", "license": "MIT" @@ -10676,6 +10904,18 @@ "node": ">= 0.6" } }, + "node_modules/new-find-package-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/new-find-package-json/-/new-find-package-json-2.0.0.tgz", + "integrity": "sha512-lDcBsjBSMlj3LXH2v/FW3txlh2pYTjmbOXPYJD93HI5EwuLzI11tdHSIpUMmfq/IOsldj4Ps8M8flhm+pCK4Ew==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">=12.22.0" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "dev": true, @@ -12694,10 +12934,11 @@ } }, "node_modules/socks": { - "version": "2.8.3", - "license": "MIT", + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", "dependencies": { - "ip-address": "^9.0.5", + "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" }, "engines": { @@ -12725,8 +12966,8 @@ }, "node_modules/sparse-bitfield": { "version": "3.0.3", + "devOptional": true, "license": "MIT", - "optional": true, "dependencies": { "memory-pager": "^1.0.2" } @@ -12842,6 +13083,17 @@ "stream-chain": "^2.2.5" } }, + "node_modules/streamx": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "dev": true, + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "license": "MIT", @@ -13227,6 +13479,31 @@ "node": ">=12.17" } }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/tar-stream/node_modules/b4a": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", + "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", + "dev": true, + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, "node_modules/test-exclude": { "version": "6.0.0", "dev": true, @@ -13259,6 +13536,29 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/text-decoder/node_modules/b4a": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", + "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", + "dev": true, + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, "node_modules/text-extensions": { "version": "2.4.0", "dev": true, @@ -13547,8 +13847,9 @@ } }, "node_modules/tslib": { - "version": "2.6.2", - "license": "0BSD" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/tsscmp": { "version": "1.0.6", diff --git a/package.json b/package.json index 84966d499..4b096c703 100644 --- a/package.json +++ b/package.json @@ -161,6 +161,7 @@ "globals": "^16.5.0", "husky": "^9.1.7", "lint-staged": "^16.2.6", + "mongodb-memory-server": "^11.0.1", "nyc": "^17.1.0", "prettier": "^3.6.2", "quicktype": "^23.2.6", @@ -170,8 +171,8 @@ "typescript": "^5.9.3", "typescript-eslint": "^8.46.4", "vite": "^7.1.9", - "vitest": "^3.2.4", - "vite-tsconfig-paths": "^5.1.4" + "vite-tsconfig-paths": "^5.1.4", + "vitest": "^3.2.4" }, "optionalDependencies": { "@esbuild/darwin-arm64": "^0.27.0", diff --git a/test/globalSetup.ts b/test/globalSetup.ts new file mode 100644 index 000000000..526edb411 --- /dev/null +++ b/test/globalSetup.ts @@ -0,0 +1,27 @@ +import { MongoMemoryServer } from 'mongodb-memory-server'; + +let mongoInstance: MongoMemoryServer | undefined; + +export async function setup() { + const mongoVersion = process.env.MONGODB_VERSION || '8.0.4'; + + mongoInstance = await MongoMemoryServer.create({ + binary: { + version: mongoVersion, + }, + }); + const uri = mongoInstance.getUri(); + + // Set the connection string for tests to use + process.env.MONGO_URI = uri.slice(0, uri.lastIndexOf('/')); + process.env.GIT_PROXY_MONGO_CONNECTION_STRING = `${process.env.MONGO_URI}/gitproxy`; + + console.log(`MongoDB Memory Server (v${mongoVersion}) started at ${process.env.MONGO_URI}`); +} + +export async function teardown() { + if (mongoInstance) { + await mongoInstance.stop(); + console.log('MongoDB Memory Server stopped'); + } +} diff --git a/vitest.config.ts b/vitest.config.ts index 3e8b1ac1c..c1e310c71 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -2,6 +2,7 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { + globalSetup: './test/globalSetup.ts', pool: 'forks', poolOptions: { forks: { From b362e46c9d4a89825ab68064da39aa972e57e27e Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Wed, 21 Jan 2026 09:37:27 +0100 Subject: [PATCH 02/16] ci: add Windows to test matrix and use mongodb-memory-server --- .github/workflows/ci.yml | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 703e32da7..e74a92f78 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ name: CI on: push: - branches: [main] + branches: [main, windows-failing-tests] pull_request: branches: [main] @@ -14,13 +14,17 @@ permissions: jobs: build: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: + os: [ubuntu-latest, windows-latest] node-version: [20.x, 22.x, 24.x] - mongodb-version: ['6.0', '7.0', '8.0'] + mongodb-version: ['6.0.18', '7.0.16', '8.0.4'] + + env: + MONGODB_VERSION: ${{ matrix.mongodb-version }} steps: - name: Harden Runner @@ -37,11 +41,6 @@ jobs: with: node-version: ${{ matrix.node-version }} - - name: Start MongoDB - uses: supercharge/mongodb-github-action@315db7fe45ac2880b7758f1933e6e5d59afd5e94 # ratchet:supercharge/mongodb-github-action@1.12.1 - with: - mongodb-version: ${{ matrix.mongodb-version }} - - name: Install dependencies run: npm ci @@ -56,11 +55,13 @@ jobs: - name: Test id: test + shell: bash run: | npm run test-coverage-ci npm run test-coverage-ci --workspaces --if-present - name: Upload test coverage report + if: runner.os == 'Linux' uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # ratchet:codecov/codecov-action@v5.5.1 with: files: ./coverage/lcov.info @@ -75,14 +76,14 @@ jobs: - name: Save build folder uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # ratchet:actions/upload-artifact@v4 with: - name: build-${{ matrix.node-version }}-mongo-${{ matrix.mongodb-version }} + name: build-${{ matrix.os }}-node-${{ matrix.node-version }}-mongo-${{ matrix.mongodb-version }} if-no-files-found: error path: build - name: Download the build folders uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # ratchet:actions/download-artifact@v5 with: - name: build-${{ matrix.node-version }}-mongo-${{ matrix.mongodb-version }} + name: build-${{ matrix.os }}-node-${{ matrix.node-version }}-mongo-${{ matrix.mongodb-version }} path: build - name: Run cypress test @@ -116,14 +117,14 @@ jobs: run: | echo "## Failed Matrix Combinations" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - echo "| Node Version | MongoDB Version | Status |" >> $GITHUB_STEP_SUMMARY - echo "|--------------|-----------------|--------|" >> $GITHUB_STEP_SUMMARY + echo "| OS | Node Version | MongoDB Version | Status |" >> $GITHUB_STEP_SUMMARY + echo "|----|--------------|-----------------|--------|" >> $GITHUB_STEP_SUMMARY # Parse the matrix results from the build job results='${{ toJSON(needs.build.outputs) }}' # Since we can't directly get individual matrix job statuses, # we'll note that the build job failed - echo "| Multiple | Multiple | ❌ Failed |" >> $GITHUB_STEP_SUMMARY + echo "| Multiple | Multiple | Multiple | ❌ Failed |" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "⚠️ Check the [build job logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details on which specific matrix combinations failed." >> $GITHUB_STEP_SUMMARY From d784b2bd6bf60ab02e0f4326778b5c0a7f916537 Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Wed, 21 Jan 2026 09:45:23 +0100 Subject: [PATCH 03/16] fix: replace bash script with Node.js for cross-platform build --- package.json | 2 +- scripts/fix-shebang.js | 16 ++++++++++++++++ scripts/fix-shebang.sh | 8 -------- 3 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 scripts/fix-shebang.js delete mode 100755 scripts/fix-shebang.sh diff --git a/package.json b/package.json index 4b096c703..5ea52e706 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "server": "ALLOWED_ORIGINS=* tsx index.ts", "start": "concurrently \"npm run server\" \"npm run client\"", "build": "npm run generate-config-types && npm run build-ui && npm run build-ts", - "build-ts": "tsc --project tsconfig.publish.json && ./scripts/fix-shebang.sh", + "build-ts": "tsc --project tsconfig.publish.json && node scripts/fix-shebang.js", "build-ui": "vite build", "check-types": "tsc", "check-types:server": "tsc --project tsconfig.publish.json --noEmit", diff --git a/scripts/fix-shebang.js b/scripts/fix-shebang.js new file mode 100644 index 000000000..06cd5b8e6 --- /dev/null +++ b/scripts/fix-shebang.js @@ -0,0 +1,16 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); + +const distIndexPath = path.join(__dirname, '..', 'dist', 'index.js'); + +if (fs.existsSync(distIndexPath)) { + let content = fs.readFileSync(distIndexPath, 'utf-8'); + // Replace tsx with node in the shebang (first line) + content = content.replace(/^#!.*tsx/, '#!/usr/bin/env node'); + fs.writeFileSync(distIndexPath, content); + console.log('Fixed shebang in dist/index.js'); +} else { + console.log('dist/index.js not found, skipping shebang fix'); +} diff --git a/scripts/fix-shebang.sh b/scripts/fix-shebang.sh deleted file mode 100755 index dd899a2cb..000000000 --- a/scripts/fix-shebang.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -REPO_ROOT="$(git rev-parse --show-toplevel)" -cd "$REPO_ROOT" - -# Replace tsx with node in the shebang -sed -ie '1s/tsx/node/' dist/index.js From 0c602decb598eca9734e7f198b2c89e22261a3c2 Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Wed, 21 Jan 2026 09:52:21 +0100 Subject: [PATCH 04/16] fix: add cross-env for Windows-compatible test scripts --- package-lock.json | 26 ++++++++++++++++++++++++++ package.json | 9 +++++---- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5f4f067aa..0e6f4086a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -85,6 +85,7 @@ "@types/yargs": "^17.0.35", "@vitejs/plugin-react": "^5.1.1", "@vitest/coverage-v8": "^3.2.4", + "cross-env": "^10.1.0", "cypress": "^15.6.0", "eslint": "^9.39.1", "eslint-config-prettier": "^10.1.8", @@ -1642,6 +1643,13 @@ "version": "0.8.0", "license": "MIT" }, + "node_modules/@epic-web/invariant": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", + "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==", + "dev": true, + "license": "MIT" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.11", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", @@ -6286,6 +6294,24 @@ "dev": true, "license": "MIT" }, + "node_modules/cross-env": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", + "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@epic-web/invariant": "^1.0.0", + "cross-spawn": "^7.0.6" + }, + "bin": { + "cross-env": "dist/bin/cross-env.js", + "cross-env-shell": "dist/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/cross-fetch": { "version": "4.1.0", "dev": true, diff --git a/package.json b/package.json index 5ea52e706..7cce070d1 100644 --- a/package.json +++ b/package.json @@ -53,12 +53,12 @@ "build-ui": "vite build", "check-types": "tsc", "check-types:server": "tsc --project tsconfig.publish.json --noEmit", - "test": "NODE_ENV=test vitest --run --dir ./test", + "test": "cross-env NODE_ENV=test vitest --run --dir ./test", "test:e2e": "vitest run --config vitest.config.e2e.ts", "test:e2e:watch": "vitest --config vitest.config.e2e.ts", - "test-coverage": "NODE_ENV=test vitest --run --dir ./test --coverage", - "test-coverage-ci": "NODE_ENV=test vitest --run --dir ./test --coverage.enabled=true --coverage.reporter=lcovonly --coverage.reporter=text", - "test-watch": "NODE_ENV=test vitest --dir ./test --watch", + "test-coverage": "cross-env NODE_ENV=test vitest --run --dir ./test --coverage", + "test-coverage-ci": "cross-env NODE_ENV=test vitest --run --dir ./test --coverage.enabled=true --coverage.reporter=lcovonly --coverage.reporter=text", + "test-watch": "cross-env NODE_ENV=test vitest --dir ./test --watch", "prepare": "node ./scripts/prepare.js", "lint": "eslint", "lint:fix": "eslint --fix", @@ -152,6 +152,7 @@ "@types/yargs": "^17.0.35", "@vitejs/plugin-react": "^5.1.1", "@vitest/coverage-v8": "^3.2.4", + "cross-env": "^10.1.0", "cypress": "^15.6.0", "eslint": "^9.39.1", "eslint-config-prettier": "^10.1.8", From edc03b536f45ebe6015cad6a80d13723d95d649a Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Wed, 21 Jan 2026 10:00:14 +0100 Subject: [PATCH 05/16] fix: make tests cross-platform compatible for Windows --- test/ConfigLoader.test.ts | 4 +++- test/plugin/plugin.test.ts | 6 ++++-- test/processors/writePack.test.ts | 8 +++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/test/ConfigLoader.test.ts b/test/ConfigLoader.test.ts index 0121b775f..b92b2d166 100644 --- a/test/ConfigLoader.test.ts +++ b/test/ConfigLoader.test.ts @@ -213,7 +213,9 @@ describe('ConfigLoader', () => { } else if (process.platform === 'linux') { expect(configLoader.cacheDirPath).toContain('.cache'); } else if (process.platform === 'win32') { - expect(configLoader.cacheDirPath).toContain('AppData/Local'); + // Windows uses backslash in paths, so check for path components separately + expect(configLoader.cacheDirPath).toContain('AppData'); + expect(configLoader.cacheDirPath).toContain('Local'); } }); diff --git a/test/plugin/plugin.test.ts b/test/plugin/plugin.test.ts index 0d0afe56f..1b8b220f4 100644 --- a/test/plugin/plugin.test.ts +++ b/test/plugin/plugin.test.ts @@ -8,7 +8,8 @@ const testPackagePath = join(__dirname, '../fixtures', 'test-package'); describe('loading plugins from packages', () => { beforeAll(() => { - spawnSync('npm', ['install'], { cwd: testPackagePath, timeout: 5000 }); + // Use shell: true for cross-platform compatibility (npm.cmd on Windows) + spawnSync('npm', ['install'], { cwd: testPackagePath, timeout: 30000, shell: true }); }); describe('CommonJS syntax', () => { @@ -120,7 +121,8 @@ describe('loading plugins from packages', () => { ); afterAll(() => { - rmSync(join(testPackagePath, 'node_modules'), { recursive: true }); + // Use force: true to avoid error if node_modules doesn't exist + rmSync(join(testPackagePath, 'node_modules'), { recursive: true, force: true }); }); }); diff --git a/test/processors/writePack.test.ts b/test/processors/writePack.test.ts index 85d948243..68ede8dae 100644 --- a/test/processors/writePack.test.ts +++ b/test/processors/writePack.test.ts @@ -1,4 +1,5 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import path from 'path'; import { Action, Step } from '../../src/proxy/actions'; import * as childProcess from 'child_process'; import * as fs from 'fs'; @@ -51,7 +52,8 @@ describe('writePack', () => { 1234567890, 'https://github.com/finos/git-proxy.git', ); - action.proxyGitPath = '/path/to'; + // Use path.join for cross-platform compatibility + action.proxyGitPath = path.join('path', 'to'); action.repoName = 'repo'; }); @@ -66,14 +68,14 @@ describe('writePack', () => { 1, 'git', ['config', 'receive.unpackLimit', '0'], - expect.objectContaining({ cwd: '/path/to/repo' }), + expect.objectContaining({ cwd: path.join('path', 'to', 'repo') }), ); expect(spawnSyncMock).toHaveBeenNthCalledWith( 2, 'git', ['receive-pack', 'repo'], expect.objectContaining({ - cwd: '/path/to', + cwd: path.join('path', 'to'), input: 'pack data', }), ); From d57d31d478546556ab241c229a03c7530c6de6a9 Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Wed, 21 Jan 2026 10:12:15 +0100 Subject: [PATCH 06/16] fix: improve Windows compatibility for tests --- test/plugin/plugin.test.ts | 12 +++++++++++- test/preReceive/preReceive.test.ts | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/test/plugin/plugin.test.ts b/test/plugin/plugin.test.ts index 1b8b220f4..208742318 100644 --- a/test/plugin/plugin.test.ts +++ b/test/plugin/plugin.test.ts @@ -9,7 +9,17 @@ const testPackagePath = join(__dirname, '../fixtures', 'test-package'); describe('loading plugins from packages', () => { beforeAll(() => { // Use shell: true for cross-platform compatibility (npm.cmd on Windows) - spawnSync('npm', ['install'], { cwd: testPackagePath, timeout: 30000, shell: true }); + const result = spawnSync('npm', ['install', '--install-links'], { + cwd: testPackagePath, + timeout: 60000, + shell: true, + }); + if (result.error) { + console.error('npm install failed:', result.error); + } + if (result.stderr) { + console.error('npm install stderr:', result.stderr.toString()); + } }); describe('CommonJS syntax', () => { diff --git a/test/preReceive/preReceive.test.ts b/test/preReceive/preReceive.test.ts index bc8f3a416..8c45c576e 100644 --- a/test/preReceive/preReceive.test.ts +++ b/test/preReceive/preReceive.test.ts @@ -6,7 +6,7 @@ import { exec } from '../../src/proxy/processors/push-action/preReceive'; // TODO: Replace with memfs to prevent test pollution issues vi.mock('fs', { spy: true }); -describe('Pre-Receive Hook Execution', () => { +describe.skipIf(process.platform === 'win32')('Pre-Receive Hook Execution', () => { let action: any; let req: any; From c4aa15e5178bd2dc47c6f3f08171f41c4cda89f0 Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Wed, 21 Jan 2026 10:22:17 +0100 Subject: [PATCH 07/16] fix: enable Windows Developer Mode for npm symlinks in CI --- .github/workflows/ci.yml | 6 ++++++ test/plugin/plugin.test.ts | 12 +----------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e74a92f78..44b59ee9c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,6 +41,12 @@ jobs: with: node-version: ${{ matrix.node-version }} + - name: Enable Windows Developer Mode + if: runner.os == 'Windows' + shell: powershell + run: | + reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1" + - name: Install dependencies run: npm ci diff --git a/test/plugin/plugin.test.ts b/test/plugin/plugin.test.ts index 208742318..1b8b220f4 100644 --- a/test/plugin/plugin.test.ts +++ b/test/plugin/plugin.test.ts @@ -9,17 +9,7 @@ const testPackagePath = join(__dirname, '../fixtures', 'test-package'); describe('loading plugins from packages', () => { beforeAll(() => { // Use shell: true for cross-platform compatibility (npm.cmd on Windows) - const result = spawnSync('npm', ['install', '--install-links'], { - cwd: testPackagePath, - timeout: 60000, - shell: true, - }); - if (result.error) { - console.error('npm install failed:', result.error); - } - if (result.stderr) { - console.error('npm install stderr:', result.stderr.toString()); - } + spawnSync('npm', ['install'], { cwd: testPackagePath, timeout: 30000, shell: true }); }); describe('CommonJS syntax', () => { From d770edd69c12044f256e653f2efb86ac8239164a Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Wed, 21 Jan 2026 10:29:23 +0100 Subject: [PATCH 08/16] test: debug errors --- test/plugin/plugin.test.ts | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/test/plugin/plugin.test.ts b/test/plugin/plugin.test.ts index 1b8b220f4..697ab85bc 100644 --- a/test/plugin/plugin.test.ts +++ b/test/plugin/plugin.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { spawnSync } from 'child_process'; -import { rmSync } from 'fs'; +import { rmSync, existsSync, readdirSync } from 'fs'; import { join } from 'path'; import { isCompatiblePlugin, PushActionPlugin, PluginLoader } from '../../src/plugin'; @@ -9,7 +9,33 @@ const testPackagePath = join(__dirname, '../fixtures', 'test-package'); describe('loading plugins from packages', () => { beforeAll(() => { // Use shell: true for cross-platform compatibility (npm.cmd on Windows) - spawnSync('npm', ['install'], { cwd: testPackagePath, timeout: 30000, shell: true }); + console.log('=== Plugin test debug info ==='); + console.log('Test package path:', testPackagePath); + console.log('Platform:', process.platform); + + const result = spawnSync('npm', ['install'], { + cwd: testPackagePath, + timeout: 30000, + shell: true, + encoding: 'utf-8', + }); + + console.log('npm install exit code:', result.status); + if (result.stdout) console.log('npm install stdout:', result.stdout); + if (result.stderr) console.log('npm install stderr:', result.stderr); + if (result.error) console.log('npm install error:', result.error); + + const nodeModulesPath = join(testPackagePath, 'node_modules'); + console.log('node_modules exists:', existsSync(nodeModulesPath)); + + if (existsSync(nodeModulesPath)) { + console.log('node_modules contents:', readdirSync(nodeModulesPath)); + const finosPath = join(nodeModulesPath, '@finos'); + if (existsSync(finosPath)) { + console.log('@finos contents:', readdirSync(finosPath)); + } + } + console.log('=== End debug info ==='); }); describe('CommonJS syntax', () => { From 7eed120f73969d50b57ade55d62171022ed495c8 Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Wed, 21 Jan 2026 14:04:19 +0100 Subject: [PATCH 09/16] test: debug errors --- test/plugin/plugin.test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/plugin/plugin.test.ts b/test/plugin/plugin.test.ts index 697ab85bc..d17ffb003 100644 --- a/test/plugin/plugin.test.ts +++ b/test/plugin/plugin.test.ts @@ -42,8 +42,13 @@ describe('loading plugins from packages', () => { it( 'should load plugins that are the default export (module.exports = pluginObj)', async () => { - const loader = new PluginLoader([join(testPackagePath, 'default-export.js')]); + const pluginPath = join(testPackagePath, 'default-export.js'); + console.log('Loading plugin from:', pluginPath); + console.log('Plugin file exists:', existsSync(pluginPath)); + const loader = new PluginLoader([pluginPath]); await loader.load(); + console.log('Push plugins loaded:', loader.pushPlugins.length); + console.log('Pull plugins loaded:', loader.pullPlugins.length); expect(loader.pushPlugins.length).toBe(1); expect(loader.pushPlugins.every((p) => isCompatiblePlugin(p))).toBe(true); expect( From e746e755cc0f38c490b5d532a7c0ef89747f5d4c Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Wed, 21 Jan 2026 14:14:04 +0100 Subject: [PATCH 10/16] fix: use file:// URLs for Windows ESM loader compatibility in plugin tests --- test/plugin/plugin.test.ts | 61 +++++++++++++------------------------- 1 file changed, 20 insertions(+), 41 deletions(-) diff --git a/test/plugin/plugin.test.ts b/test/plugin/plugin.test.ts index d17ffb003..f8e60d4a6 100644 --- a/test/plugin/plugin.test.ts +++ b/test/plugin/plugin.test.ts @@ -1,54 +1,29 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { spawnSync } from 'child_process'; -import { rmSync, existsSync, readdirSync } from 'fs'; +import { rmSync } from 'fs'; import { join } from 'path'; +import { pathToFileURL } from 'url'; import { isCompatiblePlugin, PushActionPlugin, PluginLoader } from '../../src/plugin'; +// On Windows, ESM requires file:// URLs instead of absolute paths +const toPluginPath = (filePath: string): string => { + return process.platform === 'win32' ? pathToFileURL(filePath).href : filePath; +}; + const testPackagePath = join(__dirname, '../fixtures', 'test-package'); describe('loading plugins from packages', () => { beforeAll(() => { // Use shell: true for cross-platform compatibility (npm.cmd on Windows) - console.log('=== Plugin test debug info ==='); - console.log('Test package path:', testPackagePath); - console.log('Platform:', process.platform); - - const result = spawnSync('npm', ['install'], { - cwd: testPackagePath, - timeout: 30000, - shell: true, - encoding: 'utf-8', - }); - - console.log('npm install exit code:', result.status); - if (result.stdout) console.log('npm install stdout:', result.stdout); - if (result.stderr) console.log('npm install stderr:', result.stderr); - if (result.error) console.log('npm install error:', result.error); - - const nodeModulesPath = join(testPackagePath, 'node_modules'); - console.log('node_modules exists:', existsSync(nodeModulesPath)); - - if (existsSync(nodeModulesPath)) { - console.log('node_modules contents:', readdirSync(nodeModulesPath)); - const finosPath = join(nodeModulesPath, '@finos'); - if (existsSync(finosPath)) { - console.log('@finos contents:', readdirSync(finosPath)); - } - } - console.log('=== End debug info ==='); + spawnSync('npm', ['install'], { cwd: testPackagePath, timeout: 30000, shell: true }); }); describe('CommonJS syntax', () => { it( 'should load plugins that are the default export (module.exports = pluginObj)', async () => { - const pluginPath = join(testPackagePath, 'default-export.js'); - console.log('Loading plugin from:', pluginPath); - console.log('Plugin file exists:', existsSync(pluginPath)); - const loader = new PluginLoader([pluginPath]); + const loader = new PluginLoader([toPluginPath(join(testPackagePath, 'default-export.js'))]); await loader.load(); - console.log('Push plugins loaded:', loader.pushPlugins.length); - console.log('Pull plugins loaded:', loader.pullPlugins.length); expect(loader.pushPlugins.length).toBe(1); expect(loader.pushPlugins.every((p) => isCompatiblePlugin(p))).toBe(true); expect( @@ -61,7 +36,9 @@ describe('loading plugins from packages', () => { it( 'should load multiple plugins from a module that match the plugin class (module.exports = { pluginFoo, pluginBar })', async () => { - const loader = new PluginLoader([join(testPackagePath, 'multiple-export.js')]); + const loader = new PluginLoader([ + toPluginPath(join(testPackagePath, 'multiple-export.js')), + ]); await loader.load(); expect(loader.pushPlugins.length).toBe(1); expect(loader.pullPlugins.length).toBe(1); @@ -79,7 +56,7 @@ describe('loading plugins from packages', () => { it( 'should load plugins that are subclassed from plugin classes', async () => { - const loader = new PluginLoader([join(testPackagePath, 'subclass.js')]); + const loader = new PluginLoader([toPluginPath(join(testPackagePath, 'subclass.js'))]); await loader.load(); expect(loader.pushPlugins.length).toBe(1); expect(loader.pushPlugins.every((p) => isCompatiblePlugin(p))).toBe(true); @@ -95,7 +72,7 @@ describe('loading plugins from packages', () => { it( 'should load plugins that are the default export (exports default pluginObj)', async () => { - const loader = new PluginLoader([join(testPackagePath, 'esm-export.js')]); + const loader = new PluginLoader([toPluginPath(join(testPackagePath, 'esm-export.js'))]); await loader.load(); expect(loader.pushPlugins.length).toBe(1); expect(loader.pushPlugins.every((p) => isCompatiblePlugin(p))).toBe(true); @@ -106,7 +83,9 @@ describe('loading plugins from packages', () => { { timeout: 10000 }, ); it('should load multiple plugins from a module that match the plugin class (exports default { pluginFoo, pluginBar })', async () => { - const loader = new PluginLoader([join(testPackagePath, 'esm-multiple-export.js')]); + const loader = new PluginLoader([ + toPluginPath(join(testPackagePath, 'esm-multiple-export.js')), + ]); await loader.load(); expect(loader.pushPlugins.length).toBe(1); expect(loader.pullPlugins.length).toBe(1); @@ -119,7 +98,7 @@ describe('loading plugins from packages', () => { ).toBe(true); }); it('should load plugins that are subclassed from plugin classes (exports default class DummyPlugin extends PushActionPlugin {})', async () => { - const loader = new PluginLoader([join(testPackagePath, 'esm-subclass.js')]); + const loader = new PluginLoader([toPluginPath(join(testPackagePath, 'esm-subclass.js'))]); await loader.load(); expect(loader.pushPlugins.length).toBe(1); expect(loader.pushPlugins.every((p) => isCompatiblePlugin(p))).toBe(true); @@ -132,7 +111,7 @@ describe('loading plugins from packages', () => { it( 'should not load plugins that are not valid modules', async () => { - const loader = new PluginLoader([join(__dirname, './dummy.js')]); + const loader = new PluginLoader([toPluginPath(join(__dirname, './dummy.js'))]); await loader.load(); expect(loader.pushPlugins.length).toBe(0); expect(loader.pullPlugins.length).toBe(0); @@ -143,7 +122,7 @@ describe('loading plugins from packages', () => { it( 'should not load plugins that are not extended from plugin objects', async () => { - const loader = new PluginLoader([join(__dirname, './fixtures/baz.js')]); + const loader = new PluginLoader([toPluginPath(join(__dirname, './fixtures/baz.js'))]); await loader.load(); expect(loader.pushPlugins.length).toBe(0); expect(loader.pullPlugins.length).toBe(0); From 05bd573fa4f765f897864bf8ac4cbd60a03ed07c Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Wed, 21 Jan 2026 14:29:43 +0100 Subject: [PATCH 11/16] test: skip cypress tests for windows --- .github/workflows/ci.yml | 2 + test/ConfigLoader.test.ts | 126 +++++++++++++++++++++----------------- 2 files changed, 73 insertions(+), 55 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44b59ee9c..f797f9685 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,7 +92,9 @@ jobs: name: build-${{ matrix.os }}-node-${{ matrix.node-version }}-mongo-${{ matrix.mongodb-version }} path: build + # Skip Cypress on Windows - e2e tests are properly handled by e2e.yml with Docker - name: Run cypress test + if: runner.os == 'Linux' uses: cypress-io/github-action@7ef72e250a9e564efb4ed4c2433971ada4cc38b4 # ratchet:cypress-io/github-action@v6.10.4 with: start: npm start & diff --git a/test/ConfigLoader.test.ts b/test/ConfigLoader.test.ts index b92b2d166..5e0d5cdd7 100644 --- a/test/ConfigLoader.test.ts +++ b/test/ConfigLoader.test.ts @@ -432,61 +432,77 @@ describe('ConfigLoader', () => { ); }); - it('should throw error if repository is a valid URL but not a git repository', async () => { - const source: ConfigurationSource = { - type: 'git', - repository: 'https://github.com/finos/made-up-test-repo.git', - path: 'proxy.config.json', - branch: 'main', - enabled: true, - }; - - await expect(configLoader.loadFromSource(source)).rejects.toThrow( - /Failed to clone repository/, - ); - }); - - it('should throw error if repository is a valid git repo but the branch does not exist', async () => { - const source: ConfigurationSource = { - type: 'git', - repository: 'https://github.com/finos/git-proxy.git', - path: 'proxy.config.json', - branch: 'branch-does-not-exist', - enabled: true, - }; - - await expect(configLoader.loadFromSource(source)).rejects.toThrow( - /Failed to checkout branch/, - ); - }); - - it('should throw error if config path was not found', async () => { - const source: ConfigurationSource = { - type: 'git', - repository: 'https://github.com/finos/git-proxy.git', - path: 'path-not-found.json', - branch: 'main', - enabled: true, - }; - - await expect(configLoader.loadFromSource(source)).rejects.toThrow( - /Configuration file not found at/, - ); - }); - - it('should throw error if config file is not valid JSON', async () => { - const source: ConfigurationSource = { - type: 'git', - repository: 'https://github.com/finos/git-proxy.git', - path: 'test/fixtures/baz.js', - branch: 'main', - enabled: true, - }; - - await expect(configLoader.loadFromSource(source)).rejects.toThrow( - /Failed to read or parse configuration file/, - ); - }); + it( + 'should throw error if repository is a valid URL but not a git repository', + async () => { + const source: ConfigurationSource = { + type: 'git', + repository: 'https://github.com/finos/made-up-test-repo.git', + path: 'proxy.config.json', + branch: 'main', + enabled: true, + }; + + await expect(configLoader.loadFromSource(source)).rejects.toThrow( + /Failed to clone repository/, + ); + }, + { timeout: 15000 }, + ); + + it( + 'should throw error if repository is a valid git repo but the branch does not exist', + async () => { + const source: ConfigurationSource = { + type: 'git', + repository: 'https://github.com/finos/git-proxy.git', + path: 'proxy.config.json', + branch: 'branch-does-not-exist', + enabled: true, + }; + + await expect(configLoader.loadFromSource(source)).rejects.toThrow( + /Failed to checkout branch/, + ); + }, + { timeout: 30000 }, + ); + + it( + 'should throw error if config path was not found', + async () => { + const source: ConfigurationSource = { + type: 'git', + repository: 'https://github.com/finos/git-proxy.git', + path: 'path-not-found.json', + branch: 'main', + enabled: true, + }; + + await expect(configLoader.loadFromSource(source)).rejects.toThrow( + /Configuration file not found at/, + ); + }, + { timeout: 30000 }, + ); + + it( + 'should throw error if config file is not valid JSON', + async () => { + const source: ConfigurationSource = { + type: 'git', + repository: 'https://github.com/finos/git-proxy.git', + path: 'test/fixtures/baz.js', + branch: 'main', + enabled: true, + }; + + await expect(configLoader.loadFromSource(source)).rejects.toThrow( + /Failed to read or parse configuration file/, + ); + }, + { timeout: 30000 }, + ); }); describe('deepMerge', () => { From 30122d419f8b23bb1c846c673accef298c123e1f Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Wed, 21 Jan 2026 14:36:29 +0100 Subject: [PATCH 12/16] fix: increase test timeout for windows --- test/ConfigLoader.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ConfigLoader.test.ts b/test/ConfigLoader.test.ts index 5e0d5cdd7..e02affc37 100644 --- a/test/ConfigLoader.test.ts +++ b/test/ConfigLoader.test.ts @@ -447,7 +447,7 @@ describe('ConfigLoader', () => { /Failed to clone repository/, ); }, - { timeout: 15000 }, + { timeout: 30000 }, ); it( From d20413b23a947b51b8b327e308f900d3615ae6c3 Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Fri, 23 Jan 2026 14:44:07 +0100 Subject: [PATCH 13/16] fix: remove unnecessary MongoDB dependency from tests --- .github/workflows/ci.yml | 8 +- package-lock.json | 316 +-------------------------------------- package.json | 1 - test/globalSetup.ts | 27 ---- vitest.config.ts | 1 - 5 files changed, 6 insertions(+), 347 deletions(-) delete mode 100644 test/globalSetup.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f797f9685..28a4f3028 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,10 +21,6 @@ jobs: matrix: os: [ubuntu-latest, windows-latest] node-version: [20.x, 22.x, 24.x] - mongodb-version: ['6.0.18', '7.0.16', '8.0.4'] - - env: - MONGODB_VERSION: ${{ matrix.mongodb-version }} steps: - name: Harden Runner @@ -82,14 +78,14 @@ jobs: - name: Save build folder uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # ratchet:actions/upload-artifact@v4 with: - name: build-${{ matrix.os }}-node-${{ matrix.node-version }}-mongo-${{ matrix.mongodb-version }} + name: build-${{ matrix.os }}-node-${{ matrix.node-version }} if-no-files-found: error path: build - name: Download the build folders uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # ratchet:actions/download-artifact@v5 with: - name: build-${{ matrix.os }}-node-${{ matrix.node-version }}-mongo-${{ matrix.mongodb-version }} + name: build-${{ matrix.os }}-node-${{ matrix.node-version }} path: build # Skip Cypress on Windows - e2e tests are properly handled by e2e.yml with Docker diff --git a/package-lock.json b/package-lock.json index 0e6f4086a..7035c93cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -95,7 +95,6 @@ "globals": "^16.5.0", "husky": "^9.1.7", "lint-staged": "^16.2.6", - "mongodb-memory-server": "^11.0.1", "nyc": "^17.1.0", "prettier": "^3.6.2", "quicktype": "^23.2.6", @@ -2784,7 +2783,7 @@ "version": "1.4.5", "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.5.tgz", "integrity": "sha512-k64Lbyb7ycCSXHSLzxVdb2xsKGPMvYZfCICXvDsI8Z65CeWQzTEKS4YmGbnqw+U9RBvLPTsB6UCmwkgsDTGWIw==", - "devOptional": true, + "optional": true, "dependencies": { "sparse-bitfield": "^3.0.3" } @@ -4988,15 +4987,6 @@ "node": ">=4.0" } }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "engines": { - "node": ">= 14" - } - }, "node_modules/aggregate-error": { "version": "3.1.0", "dev": true, @@ -5331,15 +5321,6 @@ "version": "1.4.1", "license": "MIT" }, - "node_modules/async-mutex": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", - "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", - "dev": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/asynckit": { "version": "0.4.0", "license": "MIT" @@ -5403,20 +5384,6 @@ "version": "1.0.2", "license": "MIT" }, - "node_modules/bare-events": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", - "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", - "dev": true, - "peerDependencies": { - "bare-abort-controller": "*" - }, - "peerDependenciesMeta": { - "bare-abort-controller": { - "optional": true - } - } - }, "node_modules/base64-js": { "version": "1.5.1", "funding": [ @@ -7415,15 +7382,6 @@ "node": ">=0.8.x" } }, - "node_modules/events-universal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", - "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", - "dev": true, - "dependencies": { - "bare-events": "^2.7.0" - } - }, "node_modules/execa": { "version": "4.1.0", "dev": true, @@ -7675,12 +7633,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "dev": true - }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -8547,19 +8499,6 @@ "node": ">=0.10" } }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/human-signals": { "version": "1.1.1", "dev": true, @@ -10481,8 +10420,8 @@ }, "node_modules/memory-pager": { "version": "1.5.0", - "devOptional": true, - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/meow": { "version": "12.1.1", @@ -10706,182 +10645,6 @@ "whatwg-url": "^11.0.0" } }, - "node_modules/mongodb-memory-server": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/mongodb-memory-server/-/mongodb-memory-server-11.0.1.tgz", - "integrity": "sha512-nUlKovSJZBh7q5hPsewFRam9H66D08Ne18nyknkNalfXMPtK1Og3kOcuqQhcX88x/pghSZPIJHrLbxNFW3OWiw==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "mongodb-memory-server-core": "11.0.1", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=20.19.0" - } - }, - "node_modules/mongodb-memory-server-core": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/mongodb-memory-server-core/-/mongodb-memory-server-core-11.0.1.tgz", - "integrity": "sha512-IcIb2S9Xf7Lmz43Z1ZujMqNg7PU5Q7yn+4wOnu7l6pfeGPkEmlqzV1hIbroVx8s4vXhPB1oMGC1u8clW7aj3Xw==", - "dev": true, - "dependencies": { - "async-mutex": "^0.5.0", - "camelcase": "^6.3.0", - "debug": "^4.4.3", - "find-cache-dir": "^3.3.2", - "follow-redirects": "^1.15.11", - "https-proxy-agent": "^7.0.6", - "mongodb": "^7.0.0", - "new-find-package-json": "^2.0.0", - "semver": "^7.7.3", - "tar-stream": "^3.1.7", - "tslib": "^2.8.1", - "yauzl": "^3.2.0" - }, - "engines": { - "node": ">=20.19.0" - } - }, - "node_modules/mongodb-memory-server-core/node_modules/@types/whatwg-url": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz", - "integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==", - "dev": true, - "dependencies": { - "@types/webidl-conversions": "*" - } - }, - "node_modules/mongodb-memory-server-core/node_modules/bson": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/bson/-/bson-7.1.1.tgz", - "integrity": "sha512-TtJgBB+QyOlWjrbM+8bRgH84VM/xrDjyBFgSgGrfZF4xvt6gbEDtcswm27Tn9F9TWsjQybxT8b8VpCP/oJK4Dw==", - "dev": true, - "engines": { - "node": ">=20.19.0" - } - }, - "node_modules/mongodb-memory-server-core/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mongodb-memory-server-core/node_modules/mongodb": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.0.0.tgz", - "integrity": "sha512-vG/A5cQrvGGvZm2mTnCSz1LUcbOPl83hfB6bxULKQ8oFZauyox/2xbZOoGNl+64m8VBrETkdGCDBdOsCr3F3jg==", - "dev": true, - "dependencies": { - "@mongodb-js/saslprep": "^1.3.0", - "bson": "^7.0.0", - "mongodb-connection-string-url": "^7.0.0" - }, - "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "@aws-sdk/credential-providers": "^3.806.0", - "@mongodb-js/zstd": "^7.0.0", - "gcp-metadata": "^7.0.1", - "kerberos": "^7.0.0", - "mongodb-client-encryption": ">=7.0.0 <7.1.0", - "snappy": "^7.3.2", - "socks": "^2.8.6" - }, - "peerDependenciesMeta": { - "@aws-sdk/credential-providers": { - "optional": true - }, - "@mongodb-js/zstd": { - "optional": true - }, - "gcp-metadata": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "snappy": { - "optional": true - }, - "socks": { - "optional": true - } - } - }, - "node_modules/mongodb-memory-server-core/node_modules/mongodb-connection-string-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.0.tgz", - "integrity": "sha512-irhhjRVLE20hbkRl4zpAYLnDMM+zIZnp0IDB9akAFFUZp/3XdOfwwddc7y6cNvF2WCEtfTYRwYbIfYa2kVY0og==", - "dev": true, - "dependencies": { - "@types/whatwg-url": "^13.0.0", - "whatwg-url": "^14.1.0" - }, - "engines": { - "node": ">=20.19.0" - } - }, - "node_modules/mongodb-memory-server-core/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mongodb-memory-server-core/node_modules/tr46": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", - "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", - "dev": true, - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/mongodb-memory-server-core/node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", - "dev": true, - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/mongodb-memory-server-core/node_modules/yauzl": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz", - "integrity": "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==", - "dev": true, - "dependencies": { - "buffer-crc32": "~0.2.3", - "pend": "~1.2.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/ms": { "version": "2.1.3", "license": "MIT" @@ -10930,18 +10693,6 @@ "node": ">= 0.6" } }, - "node_modules/new-find-package-json": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/new-find-package-json/-/new-find-package-json-2.0.0.tgz", - "integrity": "sha512-lDcBsjBSMlj3LXH2v/FW3txlh2pYTjmbOXPYJD93HI5EwuLzI11tdHSIpUMmfq/IOsldj4Ps8M8flhm+pCK4Ew==", - "dev": true, - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">=12.22.0" - } - }, "node_modules/node-fetch": { "version": "2.7.0", "dev": true, @@ -12992,8 +12743,8 @@ }, "node_modules/sparse-bitfield": { "version": "3.0.3", - "devOptional": true, "license": "MIT", + "optional": true, "dependencies": { "memory-pager": "^1.0.2" } @@ -13109,17 +12860,6 @@ "stream-chain": "^2.2.5" } }, - "node_modules/streamx": { - "version": "2.23.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", - "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", - "dev": true, - "dependencies": { - "events-universal": "^1.0.0", - "fast-fifo": "^1.3.2", - "text-decoder": "^1.1.0" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "license": "MIT", @@ -13505,31 +13245,6 @@ "node": ">=12.17" } }, - "node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "dev": true, - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, - "node_modules/tar-stream/node_modules/b4a": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", - "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", - "dev": true, - "peerDependencies": { - "react-native-b4a": "*" - }, - "peerDependenciesMeta": { - "react-native-b4a": { - "optional": true - } - } - }, "node_modules/test-exclude": { "version": "6.0.0", "dev": true, @@ -13562,29 +13277,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/text-decoder": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", - "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", - "dev": true, - "dependencies": { - "b4a": "^1.6.4" - } - }, - "node_modules/text-decoder/node_modules/b4a": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", - "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", - "dev": true, - "peerDependencies": { - "react-native-b4a": "*" - }, - "peerDependenciesMeta": { - "react-native-b4a": { - "optional": true - } - } - }, "node_modules/text-extensions": { "version": "2.4.0", "dev": true, diff --git a/package.json b/package.json index 7cce070d1..42347a8ff 100644 --- a/package.json +++ b/package.json @@ -162,7 +162,6 @@ "globals": "^16.5.0", "husky": "^9.1.7", "lint-staged": "^16.2.6", - "mongodb-memory-server": "^11.0.1", "nyc": "^17.1.0", "prettier": "^3.6.2", "quicktype": "^23.2.6", diff --git a/test/globalSetup.ts b/test/globalSetup.ts deleted file mode 100644 index 526edb411..000000000 --- a/test/globalSetup.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { MongoMemoryServer } from 'mongodb-memory-server'; - -let mongoInstance: MongoMemoryServer | undefined; - -export async function setup() { - const mongoVersion = process.env.MONGODB_VERSION || '8.0.4'; - - mongoInstance = await MongoMemoryServer.create({ - binary: { - version: mongoVersion, - }, - }); - const uri = mongoInstance.getUri(); - - // Set the connection string for tests to use - process.env.MONGO_URI = uri.slice(0, uri.lastIndexOf('/')); - process.env.GIT_PROXY_MONGO_CONNECTION_STRING = `${process.env.MONGO_URI}/gitproxy`; - - console.log(`MongoDB Memory Server (v${mongoVersion}) started at ${process.env.MONGO_URI}`); -} - -export async function teardown() { - if (mongoInstance) { - await mongoInstance.stop(); - console.log('MongoDB Memory Server stopped'); - } -} diff --git a/vitest.config.ts b/vitest.config.ts index c1e310c71..3e8b1ac1c 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -2,7 +2,6 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { - globalSetup: './test/globalSetup.ts', pool: 'forks', poolOptions: { forks: { From 4343773ffb518a16dbd9a23e86b27efd36a6e134 Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Fri, 23 Jan 2026 17:42:21 +0100 Subject: [PATCH 14/16] fix: ignore cross-env unused dependency --- .github/workflows/unused-dependencies.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unused-dependencies.yml b/.github/workflows/unused-dependencies.yml index f40284ad5..00533c774 100644 --- a/.github/workflows/unused-dependencies.yml +++ b/.github/workflows/unused-dependencies.yml @@ -21,7 +21,7 @@ jobs: node-version: '22.x' - name: 'Run depcheck' run: | - npx depcheck --skip-missing --ignores="tsx,@babel/*,@commitlint/*,eslint,eslint-*,husky,ts-node,concurrently,nyc,prettier,typescript,tsconfig-paths,vite-tsconfig-paths,quicktype,history,@types/domutils,@vitest/coverage-v8" + npx depcheck --skip-missing --ignores="tsx,@babel/*,@commitlint/*,eslint,eslint-*,husky,ts-node,concurrently,nyc,prettier,typescript,tsconfig-paths,vite-tsconfig-paths,quicktype,history,@types/domutils,@vitest/coverage-v8,cross-env" echo $? if [[ $? == 1 ]]; then echo "Unused dependencies or devDependencies found" From 7450215116b9ba7feb6e3eba80916bf5c6962003 Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Tue, 3 Feb 2026 13:37:04 +0100 Subject: [PATCH 15/16] chore: address PR review feedback --- .github/workflows/ci.yml | 2 +- src/proxy/processors/push-action/preReceive.ts | 7 +++++++ test/preReceive/preReceive.test.ts | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 28a4f3028..2ed706edf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ name: CI on: push: - branches: [main, windows-failing-tests] + branches: [main] pull_request: branches: [main] diff --git a/src/proxy/processors/push-action/preReceive.ts b/src/proxy/processors/push-action/preReceive.ts index 1c3ad36b9..9c3ad1116 100644 --- a/src/proxy/processors/push-action/preReceive.ts +++ b/src/proxy/processors/push-action/preReceive.ts @@ -15,6 +15,13 @@ const exec = async ( const step = new Step('executeExternalPreReceiveHook'); let stderrTrimmed = ''; + // Pre-receive hooks execute Unix shell scripts, which is not supported on Windows + if (process.platform === 'win32') { + step.log('Warning: Pre-receive hooks are not supported on Windows, skipping execution.'); + action.addStep(step); + return action; + } + try { const resolvedPath = path.resolve(hookFilePath); const hookDir = path.dirname(resolvedPath); diff --git a/test/preReceive/preReceive.test.ts b/test/preReceive/preReceive.test.ts index 8c45c576e..eec7e5d17 100644 --- a/test/preReceive/preReceive.test.ts +++ b/test/preReceive/preReceive.test.ts @@ -6,6 +6,7 @@ import { exec } from '../../src/proxy/processors/push-action/preReceive'; // TODO: Replace with memfs to prevent test pollution issues vi.mock('fs', { spy: true }); +// Pre-receive hooks execute Unix shell scripts, which is not supported on Windows describe.skipIf(process.platform === 'win32')('Pre-Receive Hook Execution', () => { let action: any; let req: any; From ae07d487323bbe5a400f7eb73c5e5e84c109833e Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Tue, 3 Feb 2026 17:54:29 +0100 Subject: [PATCH 16/16] ci: restructure CI with MongoDB matrix for Ubuntu and single Windows build --- .github/workflows/ci.yml | 88 ++++++++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 26fa41257..e4f8f7e60 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,14 +13,15 @@ permissions: pull-requests: write jobs: - build: - runs-on: ${{ matrix.os }} + # Ubuntu build with MongoDB matrix (9 combinations: 3 Node × 3 MongoDB) + build-ubuntu: + runs-on: ubuntu-latest strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest] node-version: [20.x, 22.x, 24.x] + mongodb-version: ['6.0', '7.0', '8.0'] steps: - name: Harden Runner @@ -37,11 +38,10 @@ jobs: with: node-version: ${{ matrix.node-version }} - - name: Enable Windows Developer Mode - if: runner.os == 'Windows' - shell: powershell - run: | - reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1" + - name: Start MongoDB + uses: supercharge/mongodb-github-action@90004df786821b6308fb02299e5835d0dae05d0d # 1.12.0 + with: + mongodb-version: ${{ matrix.mongodb-version }} - name: Install dependencies run: npm ci @@ -57,40 +57,33 @@ jobs: - name: Test id: test - shell: bash run: | npm run test-coverage-ci npm run test-coverage-ci --workspaces --if-present - name: Upload test coverage report - if: runner.os == 'Linux' uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 with: files: ./coverage/lcov.info token: ${{ secrets.CODECOV_TOKEN }} - # - name: Exit if coverage condition not met - # if: ${{ steps.test.outputs.exit_code }} != 0 - # run: exit ${{ steps.test.outputs.exit_code }} - name: Build frontend run: npm run build-ui - name: Save build folder - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # ratchet:actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: - name: build-${{ matrix.os }}-node-${{ matrix.node-version }} + name: build-ubuntu-node-${{ matrix.node-version }}-mongo-${{ matrix.mongodb-version }} if-no-files-found: error path: build - name: Download the build folders - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # ratchet:actions/download-artifact@v5 + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5 with: - name: build-${{ matrix.os }}-node-${{ matrix.node-version }} + name: build-ubuntu-node-${{ matrix.node-version }}-mongo-${{ matrix.mongodb-version }} path: build - # Skip Cypress on Windows - e2e tests are properly handled by e2e.yml with Docker - name: Run cypress test - if: runner.os == 'Linux' uses: cypress-io/github-action@f790eee7a50d9505912f50c2095510be7de06aa7 # v6.10.9 with: start: npm start & @@ -98,35 +91,78 @@ jobs: wait-on-timeout: 120 command: npm run cypress:run + # Windows build - single combination for development support + build-windows: + runs-on: windows-latest + + steps: + - name: Harden Runner + uses: step-security/harden-runner@e3f713f2d8f53843e71c69a996d56f51aa9adfb9 # v2.14.1 + with: + egress-policy: audit + + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - name: Use Node.js 24.x + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + with: + node-version: 24.x + + - name: Enable Windows Developer Mode + shell: powershell + run: | + reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1" + + - name: Install dependencies + run: npm ci + + - name: Check Types (Server) + run: npm run check-types:server + + - name: Build TypeScript + run: npm run build-ts + + - name: Test + id: test + shell: bash + run: | + npm run test-coverage-ci + npm run test-coverage-ci --workspaces --if-present + + - name: Build frontend + run: npm run build-ui + # Execute a final job to collect the results and report a single check status results: if: ${{ always() }} runs-on: ubuntu-latest name: build result - needs: [build] + needs: [build-ubuntu, build-windows] steps: - name: Check build results run: | - result="${{ needs.build.result }}" - if [[ $result == "success" || $result == "skipped" ]]; then + ubuntu_result="${{ needs.build-ubuntu.result }}" + windows_result="${{ needs.build-windows.result }}" + if [[ ($ubuntu_result == "success" || $ubuntu_result == "skipped") && ($windows_result == "success" || $windows_result == "skipped") ]]; then echo "### ✅ All builds passed" >> $GITHUB_STEP_SUMMARY exit 0 else echo "### ❌ Some builds failed" >> $GITHUB_STEP_SUMMARY + echo "- Ubuntu: $ubuntu_result" >> $GITHUB_STEP_SUMMARY + echo "- Windows: $windows_result" >> $GITHUB_STEP_SUMMARY exit 1 fi - name: Parse failed matrix jobs - if: needs.build.result == 'failure' + if: needs.build-ubuntu.result == 'failure' || needs.build-windows.result == 'failure' run: | echo "## Failed Matrix Combinations" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| OS | Node Version | MongoDB Version | Status |" >> $GITHUB_STEP_SUMMARY echo "|----|--------------|-----------------|--------|" >> $GITHUB_STEP_SUMMARY - # Parse the matrix results from the build job - results='${{ toJSON(needs.build.outputs) }}' - # Since we can't directly get individual matrix job statuses, # we'll note that the build job failed echo "| Multiple | Multiple | Multiple | ❌ Failed |" >> $GITHUB_STEP_SUMMARY