mirror of
https://github.com/hanzoai/insights-js.git
synced 2026-10-30 18:34:19 +00:00
We own the ingestion server (hanzoai/insights: Django + rust/capture + nodejs plugin) AND this SDK, so there is no external wire contract to preserve. The server is already 100% posthog-free; this SDK was the last laggard. - $last_posthog_reset -> $last_insights_reset. This FIXES A LIVE MISMATCH: the server taxonomy (insights/taxonomy/taxonomy.py, insights/insightsql/ai.py) already expected the insights form while the SDK emitted the posthog form. - __PosthogExtensions__ -> __InsightsExtensions__; all PostHog/posthog/POSTHOG identifiers -> Insights/insights/INSIGHTS (src, tests, playground, playwright, scripts, rrweb subpackages, package.json keywords). - Hosts: DEFAULT_PLUGIN_HOST + DEFAULT_NUXT_HOST + the core host default pointed at PostHog's cloud (us.i.posthog.com) — a real bug. All -> insights.hanzo.ai. - Removed the us-assets/eu-assets CDN split in getRemoteConfig (PostHog-specific; Hanzo serves remote config from the same host) — it had degenerated to a no-op. - Fixed generate-docs writing to a stray references/@hanzo/ subdir. - Renamed the 3 posthog-named test files; dropped 58MB of stale reference snapshots named for upstream posthog versions we never shipped (they are generated output — references/ is now gitignored). Verified: typecheck errors 42 (HEAD) -> 18 (all pre-existing test-mock looseness); types package 0; every package.json valid with @hanzo/* names. Guard: grep -rioE 'posthog' packages ... | wc -l => 0
149 lines
5.5 KiB
TypeScript
149 lines
5.5 KiB
TypeScript
import { ErrorTracking } from '../src/error-tracking'
|
|
|
|
// Mock the utils to prevent actual global handler registration
|
|
jest.mock('../src/error-tracking/utils', () => ({
|
|
trackUncaughtExceptions: jest.fn(),
|
|
trackUnhandledRejections: jest.fn(),
|
|
trackConsole: jest.fn(),
|
|
}))
|
|
|
|
jest.mock('../src/utils', () => ({
|
|
isHermes: jest.fn(() => false),
|
|
getRemoteConfigBool: jest.requireActual('../src/utils').getRemoteConfigBool,
|
|
}))
|
|
|
|
import { trackUncaughtExceptions, trackUnhandledRejections, trackConsole } from '../src/error-tracking/utils'
|
|
import { createMockLogger, createMockInsights } from './test-utils'
|
|
|
|
const mockInsights = {
|
|
capture: jest.fn(),
|
|
flush: jest.fn(() => Promise.resolve()),
|
|
} as any
|
|
|
|
const mockLogger = {
|
|
info: jest.fn(),
|
|
warn: jest.fn(),
|
|
error: jest.fn(),
|
|
critical: jest.fn(),
|
|
createLogger: jest.fn(() => mockLogger),
|
|
}
|
|
|
|
describe('ErrorTracking remote config', () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks()
|
|
})
|
|
|
|
describe('onRemoteConfig', () => {
|
|
it('does not change state when errorTracking is undefined', () => {
|
|
const et = new ErrorTracking(mockInsights, { autocapture: true }, mockLogger as any)
|
|
|
|
// Get reference to the uncaught exception handler
|
|
const handler = (trackUncaughtExceptions as jest.Mock).mock.calls[0][0]
|
|
|
|
// Should capture before remote config
|
|
handler(new Error('test'), false)
|
|
expect(mockInsights.capture).toHaveBeenCalledTimes(1)
|
|
|
|
mockInsights.capture.mockClear()
|
|
|
|
// undefined should not change anything
|
|
et.onRemoteConfig(undefined)
|
|
handler(new Error('test2'), false)
|
|
expect(mockInsights.capture).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
it('does not change state when errorTracking is null', () => {
|
|
const et = new ErrorTracking(mockInsights, { autocapture: true }, mockLogger as any)
|
|
const handler = (trackUncaughtExceptions as jest.Mock).mock.calls[0][0]
|
|
|
|
et.onRemoteConfig(null as any)
|
|
handler(new Error('test'), false)
|
|
expect(mockInsights.capture).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
it('disables autocapture when errorTracking is false', () => {
|
|
const et = new ErrorTracking(mockInsights, { autocapture: true }, mockLogger as any)
|
|
const handler = (trackUncaughtExceptions as jest.Mock).mock.calls[0][0]
|
|
|
|
et.onRemoteConfig(false)
|
|
handler(new Error('test'), false)
|
|
expect(mockInsights.capture).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('enables autocapture when errorTracking is true', () => {
|
|
const et = new ErrorTracking(mockInsights, { autocapture: true }, mockLogger as any)
|
|
const handler = (trackUncaughtExceptions as jest.Mock).mock.calls[0][0]
|
|
|
|
// First disable
|
|
et.onRemoteConfig(false)
|
|
handler(new Error('test'), false)
|
|
expect(mockInsights.capture).not.toHaveBeenCalled()
|
|
|
|
// Then re-enable
|
|
et.onRemoteConfig(true)
|
|
handler(new Error('test2'), false)
|
|
expect(mockInsights.capture).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
it('enables autocapture when errorTracking map has autocaptureExceptions=true', () => {
|
|
const et = new ErrorTracking(mockInsights, { autocapture: true }, mockLogger as any)
|
|
const handler = (trackUncaughtExceptions as jest.Mock).mock.calls[0][0]
|
|
|
|
et.onRemoteConfig({ autocaptureExceptions: true })
|
|
handler(new Error('test'), false)
|
|
expect(mockInsights.capture).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
it('disables autocapture when errorTracking map has autocaptureExceptions=false', () => {
|
|
const et = new ErrorTracking(mockInsights, { autocapture: true }, mockLogger as any)
|
|
const handler = (trackUncaughtExceptions as jest.Mock).mock.calls[0][0]
|
|
|
|
et.onRemoteConfig({ autocaptureExceptions: false })
|
|
handler(new Error('test'), false)
|
|
expect(mockInsights.capture).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('disables autocapture when errorTracking map is missing autocaptureExceptions key', () => {
|
|
const et = new ErrorTracking(mockInsights, { autocapture: true }, mockLogger as any)
|
|
const handler = (trackUncaughtExceptions as jest.Mock).mock.calls[0][0]
|
|
|
|
et.onRemoteConfig({ otherKey: 'value' })
|
|
handler(new Error('test'), false)
|
|
expect(mockInsights.capture).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('gates unhandled rejection handler on remote config', () => {
|
|
const et = new ErrorTracking(mockInsights, { autocapture: { unhandledRejections: true } }, mockLogger as any)
|
|
const handler = (trackUnhandledRejections as jest.Mock).mock.calls[0][0]
|
|
|
|
// Enabled by default
|
|
handler(new Error('test'))
|
|
expect(mockInsights.capture).toHaveBeenCalledTimes(1)
|
|
mockInsights.capture.mockClear()
|
|
|
|
// Disable via remote config
|
|
et.onRemoteConfig(false)
|
|
handler(new Error('test2'))
|
|
expect(mockInsights.capture).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('gates console handler on remote config', () => {
|
|
const et = new ErrorTracking(mockInsights, { autocapture: { console: ['error'] } }, mockLogger as any)
|
|
const handler = (trackConsole as jest.Mock).mock.calls[0][0]
|
|
|
|
// trackConsole is called with (level, handler), get the handler
|
|
const consoleHandler = (trackConsole as jest.Mock).mock.calls[0][1]
|
|
|
|
// Enabled by default
|
|
consoleHandler(new Error('test'), false)
|
|
expect(mockInsights.capture).toHaveBeenCalledTimes(1)
|
|
mockInsights.capture.mockClear()
|
|
|
|
// Disable via remote config
|
|
et.onRemoteConfig(false)
|
|
consoleHandler(new Error('test2'), false)
|
|
expect(mockInsights.capture).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
})
|