From 639901946e9b37b311e6edd2ffa295988f745b87 Mon Sep 17 00:00:00 2001 From: k4its1t <306379390+k4its1t@users.noreply.github.com> Date: Sun, 23 Aug 2026 19:35:36 +0800 Subject: [PATCH 1/3] test(app): replace unsafe mock assertions with shoehorn --- packages/app/package.json | 1 + ...BRowSidePanel.viewTraceTimeFilter.test.tsx | 10 +- .../components/__tests__/DBTimeChart.test.tsx | 102 ++++++++++-------- .../__tests__/MetricTableModelForm.test.tsx | 18 ++-- scripts/ci/ratchet-baseline.json | 6 +- yarn.lock | 8 ++ 6 files changed, 85 insertions(+), 60 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index 5a0c996dfd..4ec70e9553 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -116,6 +116,7 @@ "@testing-library/jest-dom": "^6.4.2", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.5.2", + "@total-typescript/shoehorn": "^0.1.2", "@types/crypto-js": "^4", "@types/flat": "^5.0.5", "@types/identity-obj-proxy": "^3", diff --git a/packages/app/src/components/__tests__/DBRowSidePanel.viewTraceTimeFilter.test.tsx b/packages/app/src/components/__tests__/DBRowSidePanel.viewTraceTimeFilter.test.tsx index 16d0dd4055..b22a0467b1 100644 --- a/packages/app/src/components/__tests__/DBRowSidePanel.viewTraceTimeFilter.test.tsx +++ b/packages/app/src/components/__tests__/DBRowSidePanel.viewTraceTimeFilter.test.tsx @@ -1,7 +1,8 @@ import React from 'react'; -import { TSource } from '@hyperdx/common-utils/dist/types'; +import { SourceKind, TSource } from '@hyperdx/common-utils/dist/types'; import { MantineProvider } from '@mantine/core'; import { fireEvent, render, screen } from '@testing-library/react'; +import { fromPartial } from '@total-typescript/shoehorn'; // Controlled, in-memory replacement for nuqs' useQueryState so each side-panel // URL param can be seeded and its setter inspected independently. Values are @@ -157,14 +158,13 @@ import { DBRowSidePanelInner } from '@/components/DBRowSidePanel'; import useSidePanelStack from '@/hooks/useSidePanelStack'; import { getRowLookupWindow } from '@/utils/rowTimestamps'; -// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -const ROOT_SOURCE = { +const ROOT_SOURCE = fromPartial({ id: 'log-src', - kind: 'log', + kind: SourceKind.Log, traceSourceId: 'trace-src', timestampValueExpression: 'Timestamp', resourceAttributesExpression: 'ResourceAttributes', -} as TSource; +}); const TRACE_ID = '7316d5a2ab0dc2efa72258f64a98a405'; const SPAN_ID = 'e3748131832d6176'; diff --git a/packages/app/src/components/__tests__/DBTimeChart.test.tsx b/packages/app/src/components/__tests__/DBTimeChart.test.tsx index 380e832757..e03e671ea1 100644 --- a/packages/app/src/components/__tests__/DBTimeChart.test.tsx +++ b/packages/app/src/components/__tests__/DBTimeChart.test.tsx @@ -1,8 +1,10 @@ import React from 'react'; +import { DisplayType } from '@hyperdx/common-utils/dist/types'; import { MantineProvider } from '@mantine/core'; import { Notifications } from '@mantine/notifications'; import { screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import { fromPartial } from '@total-typescript/shoehorn'; import api from '@/api'; import { ChartKeyJoiner } from '@/ChartUtils'; @@ -155,9 +157,11 @@ describe('DBTimeChart', () => { it('passes the same config to useMVOptimizationExplanation, useQueriedChartConfig, and MVOptimizationIndicator', () => { // Mock useSource to return a source so MVOptimizationIndicator is rendered - jest.mocked(useSource).mockReturnValue({ - data: { id: 'test-source', name: 'Test Source' }, - } as any); + jest.mocked(useSource).mockReturnValue( + fromPartial>({ + data: { id: 'test-source', name: 'Test Source' }, + }), + ); renderWithMantine(); @@ -183,9 +187,11 @@ describe('DBTimeChart', () => { }); it('disables the MV-optimization query when both MV and date-range indicators are hidden', () => { - jest.mocked(useSource).mockReturnValue({ - data: { id: 'test-source', name: 'Test Source' }, - } as any); + jest.mocked(useSource).mockReturnValue( + fromPartial>({ + data: { id: 'test-source', name: 'Test Source' }, + }), + ); renderWithMantine( { }); it('keeps the MV-optimization query enabled when only the date-range indicator is shown', () => { - jest.mocked(useSource).mockReturnValue({ - data: { id: 'test-source', name: 'Test Source' }, - } as any); + jest.mocked(useSource).mockReturnValue( + fromPartial>({ + data: { id: 'test-source', name: 'Test Source' }, + }), + ); renderWithMantine( { }; // Mock useMVOptimizationExplanation to return an optimized config with aligned date range - jest.mocked(useMVOptimizationExplanation).mockReturnValue({ - data: { - optimizedConfig: { - ...config, - dateRange: [alignedStartDate, alignedEndDate] as [Date, Date], - }, - explanations: [ - { - success: true, - mvConfig: { - minGranularity: '1 minute', - tableName: 'metrics_rollup_1m', - }, + jest.mocked(useMVOptimizationExplanation).mockReturnValue( + fromPartial>({ + data: { + optimizedConfig: { + ...config, + dateRange: [alignedStartDate, alignedEndDate] as [Date, Date], }, - ], - }, - isLoading: false, - isPlaceholderData: false, - } as any); + explanations: [ + { + success: true, + mvConfig: { + minGranularity: '1 minute', + tableName: 'metrics_rollup_1m', + }, + }, + ], + }, + isLoading: false, + isPlaceholderData: false, + }), + ); renderWithMantine(); @@ -477,14 +487,16 @@ describe('DBTimeChart', () => { }; // Mock useMVOptimizationExplanation to return no optimized config - jest.mocked(useMVOptimizationExplanation).mockReturnValue({ - data: { - optimizedConfig: undefined, - explanations: [], - }, - isLoading: false, - isPlaceholderData: false, - } as any); + jest.mocked(useMVOptimizationExplanation).mockReturnValue( + fromPartial>({ + data: { + optimizedConfig: undefined, + explanations: [], + }, + isLoading: false, + isPlaceholderData: false, + }), + ); renderWithMantine(); @@ -511,7 +523,7 @@ describe('DBTimeChart', () => { sqlTemplate: 'SELECT toStartOfInterval(ts, INTERVAL {intervalSeconds:Int64} SECOND) AS ts, count() AS count FROM logs GROUP BY ts ORDER BY ts ASC', connection: 'test-connection', - displayType: 'line' as any, + displayType: DisplayType.Line, dateRange: [new Date('2024-01-01'), new Date('2024-01-02')] as [ Date, Date, @@ -669,14 +681,16 @@ describe('DBTimeChart', () => { it('does not render DateRangeIndicator when MV optimization has no optimized date range and showDateRangeIndicator is false', () => { // Mock useMVOptimizationExplanation to return data without an optimized config - jest.mocked(useMVOptimizationExplanation).mockReturnValue({ - data: { - optimizedConfig: undefined, - explanations: [], - }, - isLoading: false, - isPlaceholderData: false, - } as any); + jest.mocked(useMVOptimizationExplanation).mockReturnValue( + fromPartial>({ + data: { + optimizedConfig: undefined, + explanations: [], + }, + isLoading: false, + isPlaceholderData: false, + }), + ); renderWithMantine( , diff --git a/packages/app/src/components/__tests__/MetricTableModelForm.test.tsx b/packages/app/src/components/__tests__/MetricTableModelForm.test.tsx index 2f1ce9abab..109f844abc 100644 --- a/packages/app/src/components/__tests__/MetricTableModelForm.test.tsx +++ b/packages/app/src/components/__tests__/MetricTableModelForm.test.tsx @@ -3,6 +3,7 @@ import { useForm } from 'react-hook-form'; import { SourceKind, TSource } from '@hyperdx/common-utils/dist/types'; import { MantineProvider } from '@mantine/core'; import { render, waitFor } from '@testing-library/react'; +import { fromPartial } from '@total-typescript/shoehorn'; import { MetricTableModelForm } from '@/components/Sources/SourceForm/MetricTableModelForm'; @@ -122,8 +123,7 @@ function autofilledTables() { .map(([path, value]) => [path, value]); } -// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -const SAVED_SOURCE: TSource = { +const SAVED_SOURCE = fromPartial({ id: 'metric-source-1', kind: SourceKind.Metric, name: 'Metrics', @@ -134,7 +134,7 @@ const SAVED_SOURCE: TSource = { gauge: 'otel_metrics_gauge', sum: 'otel_metrics_sum', }, -} as any; +}); describe('MetricTableModelForm metric table autofill', () => { beforeEach(() => { @@ -237,12 +237,14 @@ describe('MetricTableModelForm metric table autofill', () => { // A saved source of another kind switched over to OTEL Metrics has no metric // tables to preserve, so it autofills like a new source. it('autofills for an existing source switched to the metrics kind', async () => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - savedSource = { - ...SAVED_SOURCE, + savedSource = fromPartial({ + id: SAVED_SOURCE.id, kind: SourceKind.Log, - metricTables: undefined, - } as any; + name: SAVED_SOURCE.name, + connection: SAVED_SOURCE.connection, + from: SAVED_SOURCE.from, + timestampValueExpression: SAVED_SOURCE.timestampValueExpression, + }); renderHarness( , diff --git a/scripts/ci/ratchet-baseline.json b/scripts/ci/ratchet-baseline.json index 3fd8aa4812..e161daf04f 100644 --- a/scripts/ci/ratchet-baseline.json +++ b/scripts/ci/ratchet-baseline.json @@ -5,9 +5,9 @@ "eslint-disable": 31 }, "app": { - "as-any": 215, + "as-any": 206, "ts-ignore": 0, - "eslint-disable": 144 + "eslint-disable": 141 }, "cli": { "as-any": 0, @@ -29,4 +29,4 @@ "ts-ignore": 0, "eslint-disable": 0 } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index bc64ac7dc8..e077cbb5c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4734,6 +4734,7 @@ __metadata: "@testing-library/jest-dom": "npm:^6.4.2" "@testing-library/react": "npm:^16.3.0" "@testing-library/user-event": "npm:^14.5.2" + "@total-typescript/shoehorn": "npm:^0.1.2" "@types/crypto-js": "npm:^4" "@types/flat": "npm:^5.0.5" "@types/identity-obj-proxy": "npm:^3" @@ -9982,6 +9983,13 @@ __metadata: languageName: node linkType: hard +"@total-typescript/shoehorn@npm:^0.1.2": + version: 0.1.2 + resolution: "@total-typescript/shoehorn@npm:0.1.2" + checksum: 10c0/e1bb904a3c46bd00a3a31a4f24a07a5de8f6c4aebb811f4ac108dfad066d2797c682b49bbcd09e98e2bd12e8b67db9bcb793e02e2ed8a996e1fe0d232a7efcf1 + languageName: node + linkType: hard + "@tsconfig/node10@npm:^1.0.7": version: 1.0.9 resolution: "@tsconfig/node10@npm:1.0.9" From 23f31ffc6f7856af9ffc53c85721283699835bae Mon Sep 17 00:00:00 2001 From: k4its1t <306379390+k4its1t@users.noreply.github.com> Date: Mon, 24 Aug 2026 12:44:11 +0800 Subject: [PATCH 2/3] test(app): derive switched source from saved fixture --- .../__tests__/MetricTableModelForm.test.tsx | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/app/src/components/__tests__/MetricTableModelForm.test.tsx b/packages/app/src/components/__tests__/MetricTableModelForm.test.tsx index 109f844abc..bebb68b97a 100644 --- a/packages/app/src/components/__tests__/MetricTableModelForm.test.tsx +++ b/packages/app/src/components/__tests__/MetricTableModelForm.test.tsx @@ -123,18 +123,20 @@ function autofilledTables() { .map(([path, value]) => [path, value]); } -const SAVED_SOURCE = fromPartial({ - id: 'metric-source-1', - kind: SourceKind.Metric, - name: 'Metrics', - connection: 'conn-1', - from: { databaseName: 'otel_v2', tableName: '' }, - timestampValueExpression: 'TimeUnix', - metricTables: { - gauge: 'otel_metrics_gauge', - sum: 'otel_metrics_sum', +const SAVED_SOURCE = fromPartial>( + { + id: 'metric-source-1', + kind: SourceKind.Metric, + name: 'Metrics', + connection: 'conn-1', + from: { databaseName: 'otel_v2', tableName: '' }, + timestampValueExpression: 'TimeUnix', + metricTables: { + gauge: 'otel_metrics_gauge', + sum: 'otel_metrics_sum', + }, }, -}); +); describe('MetricTableModelForm metric table autofill', () => { beforeEach(() => { @@ -237,13 +239,11 @@ describe('MetricTableModelForm metric table autofill', () => { // A saved source of another kind switched over to OTEL Metrics has no metric // tables to preserve, so it autofills like a new source. it('autofills for an existing source switched to the metrics kind', async () => { + const { metricTables: _metricTables, ...savedSourceWithoutMetricTables } = + SAVED_SOURCE; savedSource = fromPartial({ - id: SAVED_SOURCE.id, + ...savedSourceWithoutMetricTables, kind: SourceKind.Log, - name: SAVED_SOURCE.name, - connection: SAVED_SOURCE.connection, - from: SAVED_SOURCE.from, - timestampValueExpression: SAVED_SOURCE.timestampValueExpression, }); renderHarness( From 08ad3e0b3d7f4758164d4c0489735c88dd80cf92 Mon Sep 17 00:00:00 2001 From: k4its1t <306379390+k4its1t@users.noreply.github.com> Date: Mon, 24 Aug 2026 13:34:40 +0800 Subject: [PATCH 3/3] test(app): apply typed fixture review feedback --- ...BRowSidePanel.viewTraceTimeFilter.test.tsx | 2 +- .../__tests__/MetricTableModelForm.test.tsx | 30 ++++++++++--------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/app/src/components/__tests__/DBRowSidePanel.viewTraceTimeFilter.test.tsx b/packages/app/src/components/__tests__/DBRowSidePanel.viewTraceTimeFilter.test.tsx index b22a0467b1..47ec21f2d9 100644 --- a/packages/app/src/components/__tests__/DBRowSidePanel.viewTraceTimeFilter.test.tsx +++ b/packages/app/src/components/__tests__/DBRowSidePanel.viewTraceTimeFilter.test.tsx @@ -278,7 +278,7 @@ describe('DBRowSidePanelInner, "View Trace" row lookup time filter', () => { const compositeSource = { ...ROOT_SOURCE, timestampValueExpression: 'EventDate, EventTime', - } as TSource; + }; mockUseRowData.mockReturnValue( rowResult({ diff --git a/packages/app/src/components/__tests__/MetricTableModelForm.test.tsx b/packages/app/src/components/__tests__/MetricTableModelForm.test.tsx index bebb68b97a..d995eed787 100644 --- a/packages/app/src/components/__tests__/MetricTableModelForm.test.tsx +++ b/packages/app/src/components/__tests__/MetricTableModelForm.test.tsx @@ -1,6 +1,10 @@ import React, { useEffect } from 'react'; import { useForm } from 'react-hook-form'; -import { SourceKind, TSource } from '@hyperdx/common-utils/dist/types'; +import { + SourceKind, + TMetricSource, + TSource, +} from '@hyperdx/common-utils/dist/types'; import { MantineProvider } from '@mantine/core'; import { render, waitFor } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; @@ -123,20 +127,18 @@ function autofilledTables() { .map(([path, value]) => [path, value]); } -const SAVED_SOURCE = fromPartial>( - { - id: 'metric-source-1', - kind: SourceKind.Metric, - name: 'Metrics', - connection: 'conn-1', - from: { databaseName: 'otel_v2', tableName: '' }, - timestampValueExpression: 'TimeUnix', - metricTables: { - gauge: 'otel_metrics_gauge', - sum: 'otel_metrics_sum', - }, +const SAVED_SOURCE = fromPartial({ + id: 'metric-source-1', + kind: SourceKind.Metric, + name: 'Metrics', + connection: 'conn-1', + from: { databaseName: 'otel_v2', tableName: '' }, + timestampValueExpression: 'TimeUnix', + metricTables: { + gauge: 'otel_metrics_gauge', + sum: 'otel_metrics_sum', }, -); +}); describe('MetricTableModelForm metric table autofill', () => { beforeEach(() => {