diff --git a/packages/api/.env.test b/packages/api/.env.test index 84478335..6b8a60d3 100644 --- a/packages/api/.env.test +++ b/packages/api/.env.test @@ -8,3 +8,5 @@ MONGO_URI=mongodb://localhost:29999/hyperdx-test NODE_ENV=test PORT=9000 OPAMP_PORT=4320 +# Default to only logging warnings/errors. Adjust if you need more verbosity +HYPERDX_LOG_LEVEL=warn \ No newline at end of file diff --git a/packages/api/jest.setup.ts b/packages/api/jest.setup.ts index ecc163a4..759e2e38 100644 --- a/packages/api/jest.setup.ts +++ b/packages/api/jest.setup.ts @@ -1,2 +1,21 @@ // @eslint-disable @typescript-eslint/no-var-requires jest.retryTimes(1, { logErrorsBeforeRetry: true }); + +global.console = { + ...console, + // Turn off console.debug logs in tests (useful since we log db queries aggressively) + debug: jest.fn(), +}; + +// Mock alert notification functions to prevent HTTP calls during tests +jest.mock('@/utils/slack', () => ({ + ...jest.requireActual('@/utils/slack'), + postMessageToWebhook: jest.fn().mockResolvedValue(null), +})); + +// Mock global fetch for generic webhook calls +global.fetch = jest.fn().mockResolvedValue({ + ok: true, + text: jest.fn().mockResolvedValue(''), + json: jest.fn().mockResolvedValue({}), +} as any); diff --git a/packages/api/package.json b/packages/api/package.json index 9b2b3f7a..e343c1c6 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -73,7 +73,7 @@ "supertest": "^6.3.1", "swagger-jsdoc": "^6.2.8", "swagger-ui-express": "^5.0.1", - "ts-jest": "^28.0.5", + "ts-jest": "^29.4.5", "ts-node": "^10.8.1", "tsc-alias": "^1.8.8", "tsconfig-paths": "^4.2.0", diff --git a/packages/api/src/api-app.ts b/packages/api/src/api-app.ts index e06aa947..08833450 100644 --- a/packages/api/src/api-app.ts +++ b/packages/api/src/api-app.ts @@ -54,7 +54,9 @@ if (!config.IS_LOCAL_APP_MODE) { app.use(passport.session()); } -app.use(expressLogger); +if (!config.IS_CI) { + app.use(expressLogger); +} // Allows timing data from frontend package // see: https://github.com/expressjs/cors/issues/102 app.use(function (req, res, next) { diff --git a/packages/api/src/clickhouse/__tests__/renderChartConfig.test.ts b/packages/api/src/clickhouse/__tests__/renderChartConfig.test.ts index 228cbe03..ac80c063 100644 --- a/packages/api/src/clickhouse/__tests__/renderChartConfig.test.ts +++ b/packages/api/src/clickhouse/__tests__/renderChartConfig.test.ts @@ -108,7 +108,6 @@ describe('renderChartConfig', () => { }); afterEach(async () => { - console.log('running db cleanup code'); await server.clearDBs(); jest.clearAllMocks(); }); diff --git a/packages/api/src/routers/api/__tests__/team.test.ts b/packages/api/src/routers/api/__tests__/team.test.ts index f6b1a7b4..5ad06217 100644 --- a/packages/api/src/routers/api/__tests__/team.test.ts +++ b/packages/api/src/routers/api/__tests__/team.test.ts @@ -1,4 +1,5 @@ import _ from 'lodash'; +import { ObjectId } from 'mongodb'; import mongoose from 'mongoose'; import { getLoggedInAgent, getServer } from '@/fixtures'; @@ -60,7 +61,7 @@ Object { team: team.id, kind: 'log', name: 'My New Source', - connection: 'local', + connection: new ObjectId().toString(), from: { databaseName: 'system', tableName: 'query_log', diff --git a/packages/api/src/routers/external-api/__tests__/alerts.test.ts b/packages/api/src/routers/external-api/__tests__/alerts.test.ts index 8d5c7e92..0ce3e963 100644 --- a/packages/api/src/routers/external-api/__tests__/alerts.test.ts +++ b/packages/api/src/routers/external-api/__tests__/alerts.test.ts @@ -231,6 +231,11 @@ describe('External API Alerts', () => { }); it('should handle validation errors when creating alerts', async () => { + // Spy on console.error to suppress error output in tests + const consoleErrorSpy = jest + .spyOn(console, 'error') + .mockImplementation(() => {}); + // Missing required fields const invalidInput = { name: 'Invalid Alert', @@ -243,6 +248,9 @@ describe('External API Alerts', () => { .expect(500); expect(response.body).toHaveProperty('message'); + + // Restore console.error + consoleErrorSpy.mockRestore(); }); it('should create multiple alerts for different tiles', async () => { diff --git a/packages/api/src/routers/external-api/__tests__/charts.test.ts b/packages/api/src/routers/external-api/__tests__/charts.test.ts index f996a1e8..4ca187ed 100644 --- a/packages/api/src/routers/external-api/__tests__/charts.test.ts +++ b/packages/api/src/routers/external-api/__tests__/charts.test.ts @@ -414,6 +414,11 @@ describe('External API v2 Charts', () => { }); it('should handle lucene query errors gracefully', async () => { + // Spy on console.error to suppress expected error output + const consoleErrorSpy = jest + .spyOn(console, 'error') + .mockImplementation(() => {}); + const payload = createSeriesRequestPayload(logSource.id.toString(), { series: [{ aggFn: 'count', where: '(invalid query', groupBy: [] }], }); @@ -421,9 +426,17 @@ describe('External API v2 Charts', () => { .send(payload) .expect(500); expect(response.body).toHaveProperty('error'); + + // Restore console.error + consoleErrorSpy.mockRestore(); }); it('should handle sql query errors gracefully', async () => { + // Spy on console.error to suppress expected error output + const consoleErrorSpy = jest + .spyOn(console, 'error') + .mockImplementation(() => {}); + const payload = createSeriesRequestPayload(logSource.id.toString(), { series: [ { @@ -438,6 +451,9 @@ describe('External API v2 Charts', () => { .send(payload) .expect(500); expect(response.body).toHaveProperty('error'); + + // Restore console.error + consoleErrorSpy.mockRestore(); }); it('should return data grouped by a single field', async () => { diff --git a/packages/api/src/tasks/checkAlerts/__tests__/checkAlerts.test.ts b/packages/api/src/tasks/checkAlerts/__tests__/checkAlerts.test.ts index 1cd4a32e..d19ffbbe 100644 --- a/packages/api/src/tasks/checkAlerts/__tests__/checkAlerts.test.ts +++ b/packages/api/src/tasks/checkAlerts/__tests__/checkAlerts.test.ts @@ -123,6 +123,37 @@ describe('checkAlerts', () => { }); describe('Alert Templates', () => { + // Create a mock metadata object with the necessary methods + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + const mockMetadata = { + getColumn: jest.fn().mockImplementation(({ column }) => { + // Provide basic column definitions for common columns to avoid warnings + const columnMap = { + Timestamp: { name: 'Timestamp', type: 'DateTime' }, + Body: { name: 'Body', type: 'String' }, + SeverityText: { name: 'SeverityText', type: 'String' }, + ServiceName: { name: 'ServiceName', type: 'String' }, + }; + return Promise.resolve(columnMap[column]); + }), + getColumns: jest.fn().mockResolvedValue([]), + getMapKeys: jest.fn().mockResolvedValue([]), + getMapValues: jest.fn().mockResolvedValue([]), + getAllFields: jest.fn().mockResolvedValue([]), + getTableMetadata: jest.fn().mockResolvedValue({}), + getClickHouseSettings: jest.fn().mockReturnValue({}), + setClickHouseSettings: jest.fn(), + } as any; + + // Create a mock clickhouse client + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + const mockClickhouseClient = { + query: jest.fn().mockResolvedValue({ + json: jest.fn().mockResolvedValue({ data: [] }), + text: jest.fn().mockResolvedValue(''), + }), + } as any; + const defaultSearchView: AlertMessageTemplateDefaultView = { alert: { thresholdType: AlertThresholdType.ABOVE, @@ -525,8 +556,8 @@ describe('checkAlerts', () => { await renderAlertTemplate({ alertProvider, - clickhouseClient: {} as any, - metadata: {} as any, + clickhouseClient: mockClickhouseClient, + metadata: mockMetadata, state: AlertState.ALERT, template: 'Custom body @webhook-My_Web', // partial name should work view: { @@ -560,8 +591,8 @@ describe('checkAlerts', () => { await renderAlertTemplate({ alertProvider, - clickhouseClient: {} as any, - metadata: {} as any, + clickhouseClient: mockClickhouseClient, + metadata: mockMetadata, state: AlertState.ALERT, template: 'Custom body @webhook-My_Web', // partial name should work view: { @@ -617,8 +648,8 @@ describe('checkAlerts', () => { await renderAlertTemplate({ alertProvider, - clickhouseClient: {} as any, - metadata: {} as any, + clickhouseClient: mockClickhouseClient, + metadata: mockMetadata, state: AlertState.ALERT, template: 'Custom body @webhook-{{attributes.webhookName}}', // partial name should work view: { @@ -687,8 +718,8 @@ describe('checkAlerts', () => { await renderAlertTemplate({ alertProvider, - clickhouseClient: {} as any, - metadata: {} as any, + clickhouseClient: mockClickhouseClient, + metadata: mockMetadata, state: AlertState.ALERT, template: ` {{#is_match "attributes.k8s.pod.name" "otel-collector-123"}} @@ -725,8 +756,8 @@ describe('checkAlerts', () => { // @webhook should not be called await renderAlertTemplate({ alertProvider, - clickhouseClient: {} as any, - metadata: {} as any, + clickhouseClient: mockClickhouseClient, + metadata: mockMetadata, state: AlertState.ALERT, template: '{{#is_match "attributes.host" "web"}} @webhook-My_Web {{/is_match}}', // partial name should work @@ -818,8 +849,8 @@ describe('checkAlerts', () => { await renderAlertTemplate({ alertProvider, - clickhouseClient: {} as any, - metadata: {} as any, + clickhouseClient: mockClickhouseClient, + metadata: mockMetadata, state: AlertState.OK, // Resolved state template: '@webhook-My_Webhook', view: { diff --git a/packages/api/src/tasks/checkAlerts/providers/__tests__/default.test.ts b/packages/api/src/tasks/checkAlerts/providers/__tests__/default.test.ts index cecc95a0..945589e7 100644 --- a/packages/api/src/tasks/checkAlerts/providers/__tests__/default.test.ts +++ b/packages/api/src/tasks/checkAlerts/providers/__tests__/default.test.ts @@ -18,6 +18,16 @@ const MOCK_SAVED_SEARCH: any = { id: 'fake-saved-search-id', }; +// Mock logger to suppress error output in tests for this file +jest.mock('@/utils/logger', () => ({ + __esModule: true, + default: { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }, +})); describe('DefaultAlertProvider', () => { let provider: AlertProvider; const server = getServer(); diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json index d75504e8..f2d94a5a 100644 --- a/packages/api/tsconfig.json +++ b/packages/api/tsconfig.json @@ -6,6 +6,7 @@ "@/*": ["./*"] }, "outDir": "build", + "isolatedModules": true, "skipLibCheck": true, "sourceMap": true, "strict": true, diff --git a/packages/common-utils/package.json b/packages/common-utils/package.json index b29ce2bf..f14fd5df 100644 --- a/packages/common-utils/package.json +++ b/packages/common-utils/package.json @@ -41,7 +41,7 @@ "nodemon": "^2.0.20", "rimraf": "^4.4.1", "supertest": "^6.3.1", - "ts-jest": "^28.0.5", + "ts-jest": "^29.4.5", "ts-node": "^10.8.1", "tsc-alias": "^1.8.8", "tsconfig-paths": "^4.2.0", diff --git a/yarn.lock b/yarn.lock index 812b75dd..08754575 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4298,7 +4298,7 @@ __metadata: supertest: "npm:^6.3.1" swagger-jsdoc: "npm:^6.2.8" swagger-ui-express: "npm:^5.0.1" - ts-jest: "npm:^28.0.5" + ts-jest: "npm:^29.4.5" ts-node: "npm:^10.8.1" tsc-alias: "npm:^1.8.8" tsconfig-paths: "npm:^4.2.0" @@ -4483,7 +4483,7 @@ __metadata: sqlstring: "npm:^2.3.3" store2: "npm:^2.14.4" supertest: "npm:^6.3.1" - ts-jest: "npm:^28.0.5" + ts-jest: "npm:^29.4.5" ts-node: "npm:^10.8.1" tsc-alias: "npm:^1.8.8" tsconfig-paths: "npm:^4.2.0" @@ -11592,7 +11592,7 @@ __metadata: languageName: node linkType: hard -"bs-logger@npm:0.x, bs-logger@npm:^0.2.6": +"bs-logger@npm:^0.2.6": version: 0.2.6 resolution: "bs-logger@npm:0.2.6" dependencies: @@ -18711,7 +18711,7 @@ __metadata: languageName: node linkType: hard -"jest-util@npm:^28.0.0, jest-util@npm:^28.1.3": +"jest-util@npm:^28.1.3": version: 28.1.3 resolution: "jest-util@npm:28.1.3" dependencies: @@ -19085,7 +19085,7 @@ __metadata: languageName: node linkType: hard -"json5@npm:^2.1.2, json5@npm:^2.2.1, json5@npm:^2.2.2, json5@npm:^2.2.3": +"json5@npm:^2.1.2, json5@npm:^2.2.2, json5@npm:^2.2.3": version: 2.2.3 resolution: "json5@npm:2.2.3" bin: @@ -19534,7 +19534,7 @@ __metadata: languageName: node linkType: hard -"lodash.memoize@npm:4.x, lodash.memoize@npm:^4.1.2": +"lodash.memoize@npm:^4.1.2": version: 4.1.2 resolution: "lodash.memoize@npm:4.1.2" checksum: 10c0/c8713e51eccc650422716a14cece1809cfe34bc5ab5e242b7f8b4e2241c2483697b971a604252807689b9dd69bfe3a98852e19a5b89d506b000b4187a1285df8 @@ -19759,7 +19759,7 @@ __metadata: languageName: node linkType: hard -"make-error@npm:1.x, make-error@npm:^1.1.1, make-error@npm:^1.3.6": +"make-error@npm:^1.1.1, make-error@npm:^1.3.6": version: 1.3.6 resolution: "make-error@npm:1.3.6" checksum: 10c0/171e458d86854c6b3fc46610cfacf0b45149ba043782558c6875d9f42f222124384ad0b468c92e996d815a8a2003817a710c0a160e49c1c394626f76fa45396f @@ -25215,17 +25215,6 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.x, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.2": - version: 7.5.2 - resolution: "semver@npm:7.5.2" - dependencies: - lru-cache: "npm:^6.0.0" - bin: - semver: bin/semver.js - checksum: 10c0/d151207ab762a8067f6302076edc04e5b8da2362eb9e3f21c2567ceadfd415064936d215b4aae7791da118c230649d29089be979ffa49c5b56a6bcf82147efdd - languageName: node - linkType: hard - "semver@npm:^6.0.0, semver@npm:^6.3.0, semver@npm:^6.3.1": version: 6.3.1 resolution: "semver@npm:6.3.1" @@ -25246,6 +25235,17 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.2": + version: 7.5.2 + resolution: "semver@npm:7.5.2" + dependencies: + lru-cache: "npm:^6.0.0" + bin: + semver: bin/semver.js + checksum: 10c0/d151207ab762a8067f6302076edc04e5b8da2362eb9e3f21c2567ceadfd415064936d215b4aae7791da118c230649d29089be979ffa49c5b56a6bcf82147efdd + languageName: node + linkType: hard + "semver@npm:^7.6.0, semver@npm:^7.6.2": version: 7.6.2 resolution: "semver@npm:7.6.2" @@ -27186,39 +27186,6 @@ __metadata: languageName: node linkType: hard -"ts-jest@npm:^28.0.5": - version: 28.0.8 - resolution: "ts-jest@npm:28.0.8" - dependencies: - bs-logger: "npm:0.x" - fast-json-stable-stringify: "npm:2.x" - jest-util: "npm:^28.0.0" - json5: "npm:^2.2.1" - lodash.memoize: "npm:4.x" - make-error: "npm:1.x" - semver: "npm:7.x" - yargs-parser: "npm:^21.0.1" - peerDependencies: - "@babel/core": ">=7.0.0-beta.0 <8" - "@jest/types": ^28.0.0 - babel-jest: ^28.0.0 - jest: ^28.0.0 - typescript: ">=4.3" - peerDependenciesMeta: - "@babel/core": - optional: true - "@jest/types": - optional: true - babel-jest: - optional: true - esbuild: - optional: true - bin: - ts-jest: cli.js - checksum: 10c0/4f6d7c8dbf6deaf56f4490ae819071077e8ed30c1a3c87c7d2e21b3103e6d12aaa53d2776cb5c947bac3f3a05cd9f8dea2aedc4c6550c14fbf639c1368a0fbc9 - languageName: node - linkType: hard - "ts-jest@npm:^29.4.5": version: 29.4.5 resolution: "ts-jest@npm:29.4.5" @@ -28978,7 +28945,7 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:21.1.1, yargs-parser@npm:^21.0.1, yargs-parser@npm:^21.1.1": +"yargs-parser@npm:21.1.1, yargs-parser@npm:^21.1.1": version: 21.1.1 resolution: "yargs-parser@npm:21.1.1" checksum: 10c0/f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2