diff --git a/bun.lock b/bun.lock index e02f946..069929a 100644 --- a/bun.lock +++ b/bun.lock @@ -7,23 +7,37 @@ "valibot": "^1.1.0", }, "devDependencies": { - "@jest/globals": "^29.7.0", "@testcontainers/mongodb": "^10.7.2", "@testcontainers/postgresql": "^10.7.2", - "@types/jest": "^29.5.12", "@types/node": "^20.12.12", "@types/supertest": "^6.0.2", - "jest": "^29.7.0", - "jest-extended": "^4.0.2", - "jest-mock-extended": "^3.0.5", + "bun-types": "^1.2.15", "mongodb-memory-server": "^9.1.6", "pg-mem": "^2.8.1", "supertest": "^6.3.4", - "ts-jest": "^29.1.2", "turbo": "^2.5.4", "typescript": "^5.4.5", }, }, + "apps/integration-services/ib-websocket-gateway": { + "name": "@stock-bot/ib-websocket-gateway", + "version": "1.0.0", + "dependencies": { + "@hono/node-server": "^1.12.2", + "@stock-bot/logger": "workspace:*", + "eventemitter3": "^5.0.1", + "hono": "^4.6.8", + "uuid": "^10.0.0", + "ws": "^8.18.0", + }, + "devDependencies": { + "@types/node": "^20.12.12", + "@types/uuid": "^10.0.0", + "@types/ws": "^8.5.12", + "tsx": "^4.19.1", + "typescript": "^5.4.5", + }, + }, "apps/interface-services/trading-dashboard": { "name": "trading-dashboard", "version": "0.0.0", @@ -397,6 +411,8 @@ "@grpc/proto-loader": ["@grpc/proto-loader@0.7.15", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.2.5", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ=="], + "@hono/node-server": ["@hono/node-server@1.14.3", "", { "peerDependencies": { "hono": "^4" } }, "sha512-KuDMwwghtFYSmIpr4WrKs1VpelTrptvJ+6x6mbUcZnFcc213cumTF5BdqfHyW93B19TNI4Vaev14vOI2a0Ie3w=="], + "@humanwhocodes/config-array": ["@humanwhocodes/config-array@0.13.0", "", { "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" } }, "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw=="], "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], @@ -695,6 +711,8 @@ "@stock-bot/http-client": ["@stock-bot/http-client@workspace:libs/http-client"], + "@stock-bot/ib-websocket-gateway": ["@stock-bot/ib-websocket-gateway@workspace:apps/integration-services/ib-websocket-gateway"], + "@stock-bot/logger": ["@stock-bot/logger@workspace:libs/logger"], "@stock-bot/mongodb-client": ["@stock-bot/mongodb-client@workspace:libs/mongodb-client"], @@ -795,10 +813,14 @@ "@types/supertest": ["@types/supertest@6.0.3", "", { "dependencies": { "@types/methods": "^1.1.4", "@types/superagent": "^8.1.0" } }, "sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w=="], + "@types/uuid": ["@types/uuid@10.0.0", "", {}, "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ=="], + "@types/webidl-conversions": ["@types/webidl-conversions@7.0.3", "", {}, "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="], "@types/whatwg-url": ["@types/whatwg-url@11.0.5", "", { "dependencies": { "@types/webidl-conversions": "*" } }, "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ=="], + "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], + "@types/yargs": ["@types/yargs@17.0.33", "", { "dependencies": { "@types/yargs-parser": "*" } }, "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA=="], "@types/yargs-parser": ["@types/yargs-parser@21.0.3", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="], @@ -1263,6 +1285,8 @@ "get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + "get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="], + "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], @@ -1291,6 +1315,8 @@ "help-me": ["help-me@5.0.0", "", {}, "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg=="], + "hono": ["hono@4.7.11", "", {}, "sha512-rv0JMwC0KALbbmwJDEnxvQCeJh+xbS3KEWW5PC9cMJ08Ur9xgatI0HmtgYZfOdOSOeYsp5LO2cOhdI8cLEbDEQ=="], + "hosted-git-info": ["hosted-git-info@8.1.0", "", { "dependencies": { "lru-cache": "^10.0.1" } }, "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw=="], "html-escaper": ["html-escaper@2.0.2", "", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="], @@ -1401,8 +1427,6 @@ "jest-environment-node": ["jest-environment-node@29.7.0", "", { "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "jest-mock": "^29.7.0", "jest-util": "^29.7.0" } }, "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw=="], - "jest-extended": ["jest-extended@4.0.2", "", { "dependencies": { "jest-diff": "^29.0.0", "jest-get-type": "^29.0.0" }, "peerDependencies": { "jest": ">=27.2.5" }, "optionalPeers": ["jest"] }, "sha512-FH7aaPgtGYHc9mRjriS0ZEHYM5/W69tLrFTIdzm+yJgeoCmmrSB/luSfMSqWP9O29QWHPEmJ4qmU6EwsZideog=="], - "jest-get-type": ["jest-get-type@29.6.3", "", {}, "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw=="], "jest-haste-map": ["jest-haste-map@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA=="], @@ -1415,8 +1439,6 @@ "jest-mock": ["jest-mock@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "jest-util": "^29.7.0" } }, "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw=="], - "jest-mock-extended": ["jest-mock-extended@3.0.7", "", { "dependencies": { "ts-essentials": "^10.0.0" }, "peerDependencies": { "jest": "^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 || ^29.0.0", "typescript": "^3.0.0 || ^4.0.0 || ^5.0.0" } }, "sha512-7lsKdLFcW9B9l5NzZ66S/yTQ9k8rFtnwYdCNuRU/81fqDWicNDVhitTSPnrGmNeNm0xyw0JHexEOShrIKRCIRQ=="], - "jest-pnp-resolver": ["jest-pnp-resolver@1.2.3", "", { "peerDependencies": { "jest-resolve": "*" }, "optionalPeers": ["jest-resolve"] }, "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w=="], "jest-regex-util": ["jest-regex-util@29.6.3", "", {}, "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg=="], @@ -1857,6 +1879,8 @@ "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + "resolve.exports": ["resolve.exports@2.0.3", "", {}, "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A=="], "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], @@ -2033,12 +2057,12 @@ "ts-api-utils": ["ts-api-utils@1.4.3", "", { "peerDependencies": { "typescript": ">=4.2.0" } }, "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw=="], - "ts-essentials": ["ts-essentials@10.0.4", "", { "peerDependencies": { "typescript": ">=4.5.0" }, "optionalPeers": ["typescript"] }, "sha512-lwYdz28+S4nicm+jFi6V58LaAIpxzhg9rLdgNC1VsdP/xiFBseGhF1M/shwCk6zMmwahBZdXcl34LVHrEang3A=="], - "ts-jest": ["ts-jest@29.3.4", "", { "dependencies": { "bs-logger": "^0.2.6", "ejs": "^3.1.10", "fast-json-stable-stringify": "^2.1.0", "jest-util": "^29.0.0", "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", "semver": "^7.7.2", "type-fest": "^4.41.0", "yargs-parser": "^21.1.1" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", "@jest/transform": "^29.0.0", "@jest/types": "^29.0.0", "babel-jest": "^29.0.0", "jest": "^29.0.0", "typescript": ">=4.3 <6" }, "optionalPeers": ["@babel/core", "@jest/transform", "@jest/types", "babel-jest"], "bin": { "ts-jest": "cli.js" } }, "sha512-Iqbrm8IXOmV+ggWHOTEbjwyCf2xZlUMv5npExksXohL+tk8va4Fjhb+X2+Rt9NBmgO7bJ8WpnMLOwih/DnMlFA=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "tsx": ["tsx@4.19.4", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q=="], + "tuf-js": ["tuf-js@3.0.1", "", { "dependencies": { "@tufjs/models": "3.0.1", "debug": "^4.3.6", "make-fetch-happen": "^14.0.1" } }, "sha512-+68OP1ZzSF84rTckf3FA95vJ1Zlx/uaXyiiKyPd1pA4rZNkpEvDAKmsu1xUSmbF/chCRYgZ6UZkDwC7PmzmAyA=="], "turbo": ["turbo@2.5.4", "", { "optionalDependencies": { "turbo-darwin-64": "2.5.4", "turbo-darwin-arm64": "2.5.4", "turbo-linux-64": "2.5.4", "turbo-linux-arm64": "2.5.4", "turbo-windows-64": "2.5.4", "turbo-windows-arm64": "2.5.4" }, "bin": { "turbo": "bin/turbo" } }, "sha512-kc8ZibdRcuWUG1pbYSBFWqmIjynlD8Lp7IB6U3vIzvOv9VG+6Sp8bzyeBWE3Oi8XV5KsQrznyRTBPvrf99E4mA=="], @@ -2127,7 +2151,7 @@ "write-file-atomic": ["write-file-atomic@4.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" } }, "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg=="], - "ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="], + "ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="], "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], @@ -2267,6 +2291,8 @@ "engine.io/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + "engine.io/ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="], + "ent/punycode": ["punycode@1.4.1", "", {}, "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ=="], "envalid/tslib": ["tslib@2.6.2", "", {}, "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="], @@ -2369,6 +2395,8 @@ "socket.io-adapter/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + "socket.io-adapter/ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="], + "socket.io-parser/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], "ssh-remote-port-forward/@types/ssh2": ["@types/ssh2@0.5.52", "", { "dependencies": { "@types/node": "*", "@types/ssh2-streams": "*" } }, "sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg=="], diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..3bb3a73 --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,20 @@ +# Root bunfig.toml for Stock Bot Trading Platform +# Configures Bun for the entire monorepo workspace + +[test] +# Configure coverage and test behavior +coverage = true +timeout = "30s" + +# Configure test environment +preload = ["./test/setup.ts"] + +# Environment variables for tests +[test.env] +NODE_ENV = "test" + +# Module path resolution +[bun] +paths = { + "@stock-bot/*" = ["./libs/*/src"] +} diff --git a/jest.setup.ts b/jest.setup.ts deleted file mode 100644 index 23d3dc7..0000000 --- a/jest.setup.ts +++ /dev/null @@ -1,159 +0,0 @@ -/** - * Jest Setup File for Stock Bot Trading Platform - * - * Global test configuration and utilities available across all tests. - * This file is executed before each test file runs. - */ - -import 'jest-extended'; - -// Increase test timeout for integration tests -jest.setTimeout(30000); - -// Mock console methods to reduce noise during tests -// but allow them to be restored if needed -const originalConsole = global.console; - -global.console = { - ...originalConsole, - log: jest.fn(), - debug: jest.fn(), - info: jest.fn(), - warn: jest.fn(), - error: jest.fn(), -}; - -// Global test utilities available in all test files -declare global { - var testHelpers: { - sleep: (ms: number) => Promise; - mockTimestamp: () => Date; - generateTestOHLCV: (symbol?: string, overrides?: any) => any; - generateTestTrade: (symbol?: string, overrides?: any) => any; - generateTestQuote: (symbol?: string, overrides?: any) => any; - mockLogger: () => any; - restoreConsole: () => void; - }; -} - -global.testHelpers = { - /** - * Sleep utility for async tests - */ - sleep: (ms: number) => new Promise(resolve => setTimeout(resolve, ms)), - - /** - * Consistent mock timestamp for tests - */ - mockTimestamp: () => new Date('2024-01-01T12:00:00Z'), - - /** - * Generate test OHLCV data - */ - generateTestOHLCV: (symbol: string = 'AAPL', overrides: any = {}) => ({ - symbol, - timestamp: new Date('2024-01-01T12:00:00Z'), - open: 150.00, - high: 152.00, - low: 149.50, - close: 151.50, - volume: 1000000, - source: 'test', - ...overrides - }), - - /** - * Generate test trade data - */ - generateTestTrade: (symbol: string = 'AAPL', overrides: any = {}) => ({ - symbol, - timestamp: new Date('2024-01-01T12:00:00Z'), - price: 151.50, - quantity: 100, - side: 'buy', - trade_id: 'test_trade_1', - source: 'test', - ...overrides - }), - - /** - * Generate test quote data - */ - generateTestQuote: (symbol: string = 'AAPL', overrides: any = {}) => ({ - symbol, - timestamp: new Date('2024-01-01T12:00:00Z'), - bid_price: 151.49, - ask_price: 151.51, - bid_size: 100, - ask_size: 200, - source: 'test', - ...overrides - }), - - /** - * Create a mock logger - */ - mockLogger: () => ({ - info: jest.fn(), - error: jest.fn(), - warn: jest.fn(), - debug: jest.fn(), - trace: jest.fn() - }), - - /** - * Restore original console methods - */ - restoreConsole: () => { - global.console = originalConsole; - } -}; - -// Environment setup for tests -process.env.NODE_ENV = 'test'; -process.env.LOG_LEVEL = 'error'; - -// Set default test environment variables -process.env.QUESTDB_HOST = process.env.QUESTDB_HOST || 'localhost'; -process.env.QUESTDB_HTTP_PORT = process.env.QUESTDB_HTTP_PORT || '9000'; -process.env.QUESTDB_PG_PORT = process.env.QUESTDB_PG_PORT || '8812'; -process.env.QUESTDB_INFLUX_PORT = process.env.QUESTDB_INFLUX_PORT || '9009'; - -process.env.POSTGRES_HOST = process.env.POSTGRES_HOST || 'localhost'; -process.env.POSTGRES_PORT = process.env.POSTGRES_PORT || '5432'; -process.env.POSTGRES_DB = process.env.POSTGRES_DB || 'trading_bot_test'; -process.env.POSTGRES_USER = process.env.POSTGRES_USER || 'trading_admin'; -process.env.POSTGRES_PASSWORD = process.env.POSTGRES_PASSWORD || 'trading_pass_test'; - -process.env.MONGODB_HOST = process.env.MONGODB_HOST || 'localhost'; -process.env.MONGODB_PORT = process.env.MONGODB_PORT || '27017'; -process.env.MONGODB_DATABASE = process.env.MONGODB_DATABASE || 'trading_bot_test'; -process.env.MONGODB_USERNAME = process.env.MONGODB_USERNAME || 'trading_admin'; -process.env.MONGODB_PASSWORD = process.env.MONGODB_PASSWORD || 'trading_mongo_test'; - -// Mock Date.now() for consistent test results -const mockNow = new Date('2024-01-01T12:00:00Z').getTime(); -jest.spyOn(Date, 'now').mockReturnValue(mockNow); - -// Global test cleanup -beforeEach(() => { - // Clear all mocks before each test - jest.clearAllMocks(); -}); - -afterEach(() => { - // Reset any module mocks after each test - jest.resetModules(); -}); - -// Handle unhandled promise rejections in tests -process.on('unhandledRejection', (reason, promise) => { - console.error('Unhandled Rejection at:', promise, 'reason:', reason); - throw reason; -}); - -// Handle uncaught exceptions in tests -process.on('uncaughtException', (error) => { - console.error('Uncaught Exception:', error); - throw error; -}); diff --git a/libs/questdb-client/bunfig.toml b/libs/questdb-client/bunfig.toml new file mode 100644 index 0000000..0bd9480 --- /dev/null +++ b/libs/questdb-client/bunfig.toml @@ -0,0 +1,14 @@ +# QuestDB Client Library Bun Test Configuration + +[test] +# Configure path mapping for tests +preload = ["./test/setup.ts"] + +# Test configuration +timeout = 5000 + +# Enable TypeScript paths resolution +[bun] +paths = { + "@/*" = ["./src/*"] +} diff --git a/libs/questdb-client/test/setup.ts b/libs/questdb-client/test/setup.ts index 557761b..862d91b 100644 --- a/libs/questdb-client/test/setup.ts +++ b/libs/questdb-client/test/setup.ts @@ -45,7 +45,8 @@ beforeAll(() => { return result; } }); // Mock QuestDB HTTP client - (global as any).fetch = jest.fn(); + (global as any).fetch = () => {}; // Using Bun's built-in spyOn utilities + global.spyOn(global, 'fetch'); }); beforeEach(() => { @@ -63,7 +64,7 @@ beforeEach(() => { } } // Reset fetch mock if ((global as any).fetch) { - ((global as any).fetch as jest.Mock).mockClear(); + ((global as any).fetch as any).mockClear?.(); } }); @@ -85,7 +86,7 @@ export const questdbTestHelpers = { /** * Mock successful QuestDB HTTP response */ mockQuestDBHttpSuccess: (data: any) => { - ((global as any).fetch as jest.Mock).mockResolvedValueOnce({ + ((global as any).fetch as any).mockResolvedValue?.({ ok: true, status: 200, json: async () => data, @@ -95,9 +96,8 @@ export const questdbTestHelpers = { /** * Mock QuestDB HTTP error - */ - mockQuestDBHttpError: (status: number, message: string) => { - ((global as any).fetch as jest.Mock).mockResolvedValueOnce({ + */ mockQuestDBHttpError: (status: number, message: string) => { + ((global as any).fetch as any).mockResolvedValue?.({ ok: false, status, json: async () => ({ error: message }), @@ -107,9 +107,8 @@ export const questdbTestHelpers = { /** * Mock InfluxDB line protocol response - */ - mockInfluxDBSuccess: () => { - ((global as any).fetch as jest.Mock).mockResolvedValueOnce({ + */ mockInfluxDBSuccess: () => { + ((global as any).fetch as any).mockResolvedValue?.({ ok: true, status: 204, text: async () => '' @@ -201,15 +200,21 @@ export const questdbTestHelpers = { /** * Mock connection pool - */ - createMockPool: () => ({ - connect: jest.fn().mockResolvedValue({ - query: jest.fn().mockResolvedValue({ rows: [], rowCount: 0 }), - release: jest.fn() - }), - end: jest.fn().mockResolvedValue(undefined), - totalCount: 0, - idleCount: 0, - waitingCount: 0 - }) + */ createMockPool: () => { + const mockQuery = () => Promise.resolve({ rows: [], rowCount: 0 }); + const mockRelease = () => {}; + const mockConnect = () => Promise.resolve({ + query: mockQuery, + release: mockRelease + }); + const mockEnd = () => Promise.resolve(undefined); + + return { + connect: mockConnect, + end: mockEnd, + totalCount: 0, + idleCount: 0, + waitingCount: 0 + }; + } }; diff --git a/package.json b/package.json index 1a1accb..924d996 100644 --- a/package.json +++ b/package.json @@ -8,11 +8,11 @@ "build": "turbo run build", "build:libs": "pwsh ./scripts/build-libs.ps1", "test": "turbo run test", - "test:watch": "jest --watch", - "test:coverage": "jest --coverage", - "test:unit": "jest --testPathPattern=unit", - "test:integration": "jest --testPathPattern=integration", - "test:e2e": "jest --testPathPattern=e2e", + "test:watch": "bun test --watch", + "test:coverage": "bun test --coverage", + "test:unit": "bun test test/unit", + "test:integration": "bun test test/integration", + "test:e2e": "bun test test/e2e", "test:libs": "turbo run test --filter=./libs/*", "test:apps": "turbo run test --filter=./apps/*/*", "lint": "turbo run lint", @@ -41,12 +41,7 @@ "@types/node": "^20.12.12", "turbo": "^2.5.4", "typescript": "^5.4.5", - "@types/jest": "^29.5.12", - "jest": "^29.7.0", - "ts-jest": "^29.1.2", - "@jest/globals": "^29.7.0", - "jest-extended": "^4.0.2", - "jest-mock-extended": "^3.0.5", + "bun-types": "^1.2.15", "@testcontainers/postgresql": "^10.7.2", "@testcontainers/mongodb": "^10.7.2", "mongodb-memory-server": "^9.1.6", diff --git a/test/setup.ts b/test/setup.ts new file mode 100644 index 0000000..0bfb8d4 --- /dev/null +++ b/test/setup.ts @@ -0,0 +1,136 @@ +/** + * Bun Test Setup File for Stock Bot Trading Platform + * + * Global test configuration and utilities available across all tests. + * This file is executed before each test via bunfig.toml preload. + */ + +// Increase test timeout if needed (already configured in bunfig.toml) +// Bun.timeout = 30000; + +// Store original console methods to allow restoration +const originalConsole = global.console; + +// Mock console methods to reduce noise during tests +// These can be restored with testHelpers.restoreConsole() +console.log = () => {}; +console.debug = () => {}; +console.info = () => {}; +console.warn = () => {}; +console.error = () => {}; + +// Global test utilities available in all test files +declare global { + var testHelpers: { + sleep: (ms: number) => Promise; + mockTimestamp: () => Date; + generateTestOHLCV: (symbol?: string, overrides?: any) => any; + generateTestTrade: (symbol?: string, overrides?: any) => any; + generateTestQuote: (symbol?: string, overrides?: any) => any; + mockLogger: () => any; + restoreConsole: () => void; + }; +} + +global.testHelpers = { + /** + * Sleep utility for async tests + */ + sleep: (ms: number) => new Promise(resolve => setTimeout(resolve, ms)), + + /** + * Consistent mock timestamp for tests + */ + mockTimestamp: () => new Date('2024-01-01T12:00:00Z'), + + /** + * Generate test OHLCV data + */ + generateTestOHLCV: (symbol = 'AAPL', overrides = {}) => ({ + symbol, + open: 150.0, + high: 153.0, + low: 149.0, + close: 152.5, + volume: 10000, + timestamp: global.testHelpers.mockTimestamp(), + ...overrides + }), + + /** + * Generate test trade data + */ + generateTestTrade: (symbol = 'AAPL', overrides = {}) => ({ + symbol, + price: 152.5, + size: 100, + timestamp: global.testHelpers.mockTimestamp(), + exchange: 'NASDAQ', + conditions: ['@', 'T'], + ...overrides + }), + + /** + * Generate test quote data + */ + generateTestQuote: (symbol = 'AAPL', overrides = {}) => ({ + symbol, + bidPrice: 152.45, + bidSize: 200, + askPrice: 152.55, + askSize: 150, + timestamp: global.testHelpers.mockTimestamp(), + ...overrides + }), + + /** + * Create a mock logger + */ + mockLogger: () => ({ + debug: () => {}, + info: () => {}, + warn: () => {}, + error: () => {}, + critical: () => {}, + }), + + /** + * Restore console methods + */ + restoreConsole: () => { + global.console = originalConsole; + } +}; + +// Set up spyOn utilities +// Similar to jest.spyOn but using Bun's built-in spy functionality +// This makes it easier to migrate from Jest +global.spyOn = function(object: any, method: string) { + const original = object[method]; + const mock = function(...args: any[]) { + mock.mock.calls.push(args); + return mock.mockImplementation ? mock.mockImplementation(...args) : original.apply(object, args); + }; + + mock.mock = { calls: [] }; + mock.mockClear = () => { mock.mock.calls = []; return mock; }; + mock.mockReset = () => { + mock.mock.calls = []; + mock.mockImplementation = null; + return mock; + }; + mock.mockImplementation = null; + mock.mockReturnValue = (value: any) => { + mock.mockImplementation = () => value; + return mock; + }; + mock.mockResolvedValue = (value: any) => { + return mock.mockReturnValue(Promise.resolve(value)); + }; + mock.mockRejectedValue = (value: any) => { + return mock.mockReturnValue(Promise.reject(value)); + }; + + object[method] = mock; + return mock; +};