Files
zeekay 10e6fec4c0 debrand: the brand token is INSIGHTS_, and a wrong one stops the container
The rule table mapped HOG_ -> FN_ ahead of any POSTHOG rule, so POSTHOG_FOO
split into POST + HOG_ and came out POSTFN_FOO. Nothing sets that. The live
pods set INSIGHTS_SKIP_MIGRATION_CHECKS, which bin/migrate-check reads on the
container entrypoint, so the misspelling does not degrade a feature -- it runs
migrate --check against a database whose history this tree does not share and
the container never starts.

Both replacements are measured against the shipped tree rather than chosen:
every HOG_ name with a counterpart there is spelled INSIGHTS_ (HOG_QL ->
INSIGHTS_QL, HOG_FUNCTION_TEMPLATES -> INSIGHTS_FUNCTION_TEMPLATES, 12 of 12
determinate cases), and FN_ appears in it nowhere. Upstream carries 95 HOG_
tokens and zero FN_ ones, so repairing FN_ back out is exactly the inverse of
the bad rule and cannot collide with an upstream name.

The rule is fixed for the next convergence, and the tree it already produced is
repaired in the same commit -- the codemod is idempotent against its own output,
so it cannot undo this itself.
2026-08-01 17:42:28 -07:00

162 lines
6.5 KiB
TypeScript

import { expectLogic } from 'kea-test-utils'
import { initKeaTests } from '~/test/init'
import { AccessControlLevel, AccessControlResourceType, AppContext } from '~/types'
import { metricsSamplesCreate } from 'products/metrics/frontend/generated/api'
import { metricsSamplesLogic } from './metricsSamplesLogic'
import { metricsViewerLogic } from './metricsViewerLogic'
jest.mock('products/metrics/frontend/generated/api', () => ({
...jest.requireActual('products/metrics/frontend/generated/api'),
metricsSamplesCreate: jest.fn(),
}))
const mockSamplesCreate = metricsSamplesCreate as jest.MockedFunction<typeof metricsSamplesCreate>
const SAMPLE = {
timestamp: '2026-07-09T05:46:28.132600+00:00',
metric_name: 'demo_checkout_duration_ms',
metric_type: 'histogram',
value: 970.97,
count: 24,
unit: 'ms',
aggregation_temporality: 'cumulative',
is_monotonic: false,
service_name: 'checkout-demo',
trace_id: '4EE9645D1C55A19919C83FDD657C88A4',
span_id: 'F068A584A45A5EDA',
attributes: { endpoint: '/api/checkout' },
resource_attributes: { 'service.name': 'checkout-demo' },
}
const UNTRACED_SAMPLE = {
...SAMPLE,
timestamp: '2026-07-09T05:47:28.132600+00:00',
value: 12.5,
trace_id: '',
span_id: '',
}
describe('metricsSamplesLogic', () => {
let logic: ReturnType<typeof metricsSamplesLogic.build>
beforeEach(() => {
window.INSIGHTS_APP_CONTEXT = {
...window.INSIGHTS_APP_CONTEXT,
resource_access_control: {
...window.INSIGHTS_APP_CONTEXT?.resource_access_control,
[AccessControlResourceType.Metrics]: AccessControlLevel.Viewer,
},
} as AppContext
initKeaTests()
mockSamplesCreate.mockReset()
mockSamplesCreate.mockResolvedValue({ results: [SAMPLE] })
metricsViewerLogic.mount()
logic = metricsSamplesLogic()
logic.mount()
})
afterEach(() => {
logic?.unmount()
metricsViewerLogic.unmount()
})
// Regression: relative date strings ('-1h') sent to the API raw are a 400 —
// the loader must resolve them to ISO timestamps before the request.
it('activating the samples tab fetches emissions with resolved ISO dates', async () => {
metricsViewerLogic.actions.setMetricName('demo_checkout_duration_ms')
logic.actions.setActiveTab('samples')
await expectLogic(logic).toDispatchActions(['loadSamplesSuccess'])
expect(mockSamplesCreate).toHaveBeenCalledTimes(1)
const [, request] = mockSamplesCreate.mock.calls[0]
expect(request.query.metricName).toBe('demo_checkout_duration_ms')
expect(request.query.dateFrom).toMatch(/^\d{4}-\d{2}-\d{2}T/)
expect(logic.values.samples).toEqual([SAMPLE])
})
// Regression: firing the request with an empty metric name spams 400s on the
// empty state before a metric is picked.
it('does not call the API without a metric selected', async () => {
logic.actions.setActiveTab('samples')
await expectLogic(logic).toDispatchActions(['loadSamplesSuccess'])
expect(mockSamplesCreate).not.toHaveBeenCalled()
expect(logic.values.samples).toEqual([])
})
it('does not call the API without metrics viewer access', async () => {
window.INSIGHTS_APP_CONTEXT = {
...window.INSIGHTS_APP_CONTEXT,
resource_access_control: {
...window.INSIGHTS_APP_CONTEXT?.resource_access_control,
[AccessControlResourceType.Metrics]: AccessControlLevel.None,
},
} as AppContext
metricsViewerLogic.actions.setMetricName('demo_checkout_duration_ms')
logic.actions.setActiveTab('samples')
await expectLogic(logic).toDispatchActions(['loadSamplesSuccess'])
expect(mockSamplesCreate).not.toHaveBeenCalled()
expect(logic.values.samples).toEqual([])
})
// Regression: samples going stale when the selected metric changes while the
// tab is open — and conversely, needless requests while the tab is hidden.
it('refetches on metric change only while the samples tab is active', async () => {
metricsViewerLogic.actions.setMetricName('demo_checkout_duration_ms')
logic.actions.setActiveTab('samples')
await expectLogic(logic).toDispatchActions(['loadSamplesSuccess'])
expect(mockSamplesCreate).toHaveBeenCalledTimes(1)
metricsViewerLogic.actions.setMetricName('demo_checkout_requests_total')
await expectLogic(logic).toDispatchActions(['loadSamplesSuccess'])
expect(mockSamplesCreate).toHaveBeenCalledTimes(2)
expect(mockSamplesCreate.mock.calls[1][1].query.metricName).toBe('demo_checkout_requests_total')
logic.actions.setActiveTab('aggregates')
metricsViewerLogic.actions.setMetricName('demo_checkout_duration_ms')
await expectLogic(logic).delay(10)
expect(mockSamplesCreate).toHaveBeenCalledTimes(2)
})
// Regression: exemplar dots on the chart silently vanish if untraced emissions
// leak into (or traced ones are dropped from) the derived exemplar list.
it('derives traceExemplars from traced samples only', async () => {
mockSamplesCreate.mockResolvedValue({ results: [SAMPLE, UNTRACED_SAMPLE] })
metricsViewerLogic.actions.setMetricName('demo_checkout_duration_ms')
logic.actions.setActiveTab('samples')
await expectLogic(logic).toDispatchActions(['loadSamplesSuccess'])
expect(logic.values.traceExemplars).toEqual([
{
timestamp: SAMPLE.timestamp,
value: SAMPLE.value,
traceId: SAMPLE.trace_id,
spanId: SAMPLE.span_id,
},
])
})
// Regression: the chart's exemplar overlay starves (never fetches samples)
// unless the samples tab happens to be open — a chart redraw must refresh
// them in chart mode, and must not fire needless requests in stat mode.
it('refreshes samples on chart query success only in chart mode', async () => {
metricsViewerLogic.actions.setMetricName('demo_checkout_duration_ms')
metricsViewerLogic.actions.fetchQueryResultsSuccess([])
await expectLogic(logic).toDispatchActions(['loadSamplesSuccess'])
expect(mockSamplesCreate).toHaveBeenCalledTimes(1)
metricsViewerLogic.actions.setViewMode('stat')
metricsViewerLogic.actions.fetchQueryResultsSuccess([])
await expectLogic(logic).delay(10)
expect(mockSamplesCreate).toHaveBeenCalledTimes(1)
})
})