Files
insights-js/packages/next/tests/insightsMiddleware.test.ts
zeekay ec6f6b50c1 chore: kill ALL posthog — zero refs, zero filenames (1452 -> 0)
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
2026-07-16 09:09:18 -07:00

464 lines
17 KiB
TypeScript

jest.mock('server-only', () => ({}))
import { insightsMiddleware } from '../src/middleware/insightsMiddleware'
// Mock identity module so we can control the generated ID
const mockGenerateAnonymousId = jest.fn(() => 'mock-anon-id')
jest.mock('../src/shared/identity', () => ({
generateAnonymousId: () => mockGenerateAnonymousId(),
}))
// Mock NextRequest and NextResponse
class MockNextRequest {
url: string
nextUrl: { pathname: string; search: string }
cookies: {
get: (name: string) => { name: string; value: string } | undefined
}
headers: Map<string, string>
constructor(url: string, cookies: Record<string, string> = {}) {
this.url = url
const parsed = new URL(url)
this.nextUrl = { pathname: parsed.pathname, search: parsed.search }
const cookieEntries = Object.entries(cookies)
this.cookies = {
get: (name: string) => {
const entry = cookieEntries.find(([k]) => k === name)
return entry ? { name: entry[0], value: entry[1] } : undefined
},
}
this.headers = new Map()
}
}
const mockCookiesSet = jest.fn()
const mockCookiesDelete = jest.fn()
const mockNextResponseNext = jest.fn(() => ({
headers: new Map(),
cookies: { set: mockCookiesSet, delete: mockCookiesDelete },
}))
const mockNextResponseRewrite = jest.fn((url: URL) => ({
headers: new Map(),
cookies: { set: jest.fn() },
_rewriteUrl: url,
}))
jest.mock('next/server.js', () => ({
NextResponse: {
next: (...args: any[]) => mockNextResponseNext(...args),
rewrite: (url: URL) => mockNextResponseRewrite(url),
},
}))
const COOKIE_NAME = 'ph_phc_test123_insights'
describe('insightsMiddleware', () => {
const originalEnv = process.env
beforeEach(() => {
jest.clearAllMocks()
mockGenerateAnonymousId.mockReturnValue('mock-anon-id')
process.env = { ...originalEnv }
})
afterAll(() => {
process.env = originalEnv
})
it('returns a middleware function', () => {
const middleware = insightsMiddleware({ apiKey: 'phc_test123' })
expect(typeof middleware).toBe('function')
})
it('calls NextResponse.next()', async () => {
const middleware = insightsMiddleware({ apiKey: 'phc_test123' })
const req = new MockNextRequest('https://example.com/page')
await middleware(req as any)
expect(mockNextResponseNext).toHaveBeenCalled()
})
describe('apiKey resolution', () => {
it('reads apiKey from NEXT_PUBLIC_INSIGHTS_KEY when not in config', async () => {
process.env.NEXT_PUBLIC_INSIGHTS_KEY = 'phc_from_env'
const middleware = insightsMiddleware({})
const req = new MockNextRequest('https://example.com/')
await middleware(req as any)
expect(mockCookiesSet).toHaveBeenCalledWith(
'ph_phc_from_env_insights',
expect.any(String),
expect.any(Object)
)
})
it('prefers config apiKey over env var', async () => {
process.env.NEXT_PUBLIC_INSIGHTS_KEY = 'phc_from_env'
const middleware = insightsMiddleware({ apiKey: 'phc_test123' })
const req = new MockNextRequest('https://example.com/')
await middleware(req as any)
expect(mockCookiesSet).toHaveBeenCalledWith(COOKIE_NAME, expect.any(String), expect.any(Object))
})
it('trims apiKey before seeding cookies', async () => {
const middleware = insightsMiddleware({ apiKey: ' phc_test123\n' })
const req = new MockNextRequest('https://example.com/')
await middleware(req as any)
expect(mockCookiesSet).toHaveBeenCalledWith(COOKIE_NAME, expect.any(String), expect.any(Object))
})
it('warns and skips cookie seeding when neither config nor env var provides apiKey', async () => {
delete process.env.NEXT_PUBLIC_INSIGHTS_KEY
const warnSpy = jest.spyOn(console, 'warn').mockImplementation()
const middleware = insightsMiddleware({})
const req = new MockNextRequest('https://example.com/')
await middleware(req as any)
expect(warnSpy).toHaveBeenCalledWith('[Insights Next.js] apiKey is required — Insights will not be initialized')
expect(mockCookiesSet).not.toHaveBeenCalled()
expect(mockNextResponseNext).toHaveBeenCalled()
warnSpy.mockRestore()
})
})
describe('cookie seeding', () => {
it('seeds cookie on first visit', async () => {
const middleware = insightsMiddleware({ apiKey: 'phc_test123' })
const req = new MockNextRequest('https://example.com/')
await middleware(req as any)
expect(mockCookiesSet).toHaveBeenCalledWith(
COOKIE_NAME,
expect.any(String),
expect.objectContaining({
path: '/',
sameSite: 'lax',
maxAge: 365 * 24 * 60 * 60,
httpOnly: false,
})
)
// Verify the cookie value is valid Insights cookie JSON
const cookieValue = mockCookiesSet.mock.calls[0][1]
const parsed = JSON.parse(cookieValue)
expect(parsed.distinct_id).toBe('mock-anon-id')
expect(parsed.$device_id).toBe('mock-anon-id')
})
it('uses the generated anonymous ID in the cookie', async () => {
mockGenerateAnonymousId.mockReturnValue('seeded-uuid-v7')
const middleware = insightsMiddleware({ apiKey: 'phc_test123' })
const req = new MockNextRequest('https://example.com/')
await middleware(req as any)
const cookieValue = mockCookiesSet.mock.calls[0][1]
const parsed = JSON.parse(cookieValue)
expect(parsed.distinct_id).toBe('seeded-uuid-v7')
})
it('uses custom cookieMaxAgeSeconds when provided', async () => {
const middleware = insightsMiddleware({ apiKey: 'phc_test123', cookieMaxAgeSeconds: 3600 })
const req = new MockNextRequest('https://example.com/')
await middleware(req as any)
expect(mockCookiesSet).toHaveBeenCalledWith(
COOKIE_NAME,
expect.any(String),
expect.objectContaining({ maxAge: 3600 })
)
})
it('does not overwrite existing cookie', async () => {
const middleware = insightsMiddleware({ apiKey: 'phc_test123' })
const req = new MockNextRequest('https://example.com/', {
[COOKIE_NAME]: JSON.stringify({
distinct_id: 'existing-user',
$device_id: 'existing-device',
}),
})
await middleware(req as any)
expect(mockCookiesSet).not.toHaveBeenCalled()
})
})
describe('composable response', () => {
it('uses the provided response instead of creating one', async () => {
const providedCookiesSet = jest.fn()
const providedResponse = {
headers: new Map(),
cookies: { set: providedCookiesSet },
}
const middleware = insightsMiddleware({
apiKey: 'phc_test123',
response: providedResponse as any,
})
const req = new MockNextRequest('https://example.com/')
const result = await middleware(req as any)
expect(mockNextResponseNext).not.toHaveBeenCalled()
expect(result).toBe(providedResponse)
expect(providedCookiesSet).toHaveBeenCalledWith(
COOKIE_NAME,
expect.any(String),
expect.objectContaining({ path: '/', sameSite: 'lax' })
)
})
it('returns the provided response unmodified when cookie exists', async () => {
const providedCookiesSet = jest.fn()
const providedResponse = {
headers: new Map(),
cookies: { set: providedCookiesSet },
}
const middleware = insightsMiddleware({
apiKey: 'phc_test123',
response: providedResponse as any,
})
const req = new MockNextRequest('https://example.com/', {
[COOKIE_NAME]: JSON.stringify({
distinct_id: 'existing-user',
$device_id: 'existing-device',
}),
})
const result = await middleware(req as any)
expect(result).toBe(providedResponse)
expect(providedCookiesSet).not.toHaveBeenCalled()
})
})
describe('proxy', () => {
it('proxies ingest requests to Insights host', async () => {
const middleware = insightsMiddleware({
apiKey: 'phc_test123',
proxy: true,
})
const req = new MockNextRequest('https://example.com/ingest/e/')
await middleware(req as any)
expect(mockNextResponseRewrite).toHaveBeenCalledWith(expect.any(URL))
const rewriteUrl: URL = mockNextResponseRewrite.mock.calls[0][0]
expect(rewriteUrl.origin).toBe('https://insights.hanzo.ai')
expect(rewriteUrl.pathname).toBe('/e/')
})
it('preserves query string on proxy', async () => {
const middleware = insightsMiddleware({
apiKey: 'phc_test123',
proxy: true,
})
const req = new MockNextRequest('https://example.com/ingest/flags?v=2')
await middleware(req as any)
const rewriteUrl: URL = mockNextResponseRewrite.mock.calls[0][0]
expect(rewriteUrl.pathname).toBe('/flags')
expect(rewriteUrl.search).toBe('?v=2')
})
it('does not proxy non-ingest paths', async () => {
const middleware = insightsMiddleware({
apiKey: 'phc_test123',
proxy: true,
})
const req = new MockNextRequest('https://example.com/about')
await middleware(req as any)
expect(mockNextResponseRewrite).not.toHaveBeenCalled()
expect(mockNextResponseNext).toHaveBeenCalled()
})
it('does not seed cookie on proxied requests', async () => {
const middleware = insightsMiddleware({
apiKey: 'phc_test123',
proxy: true,
})
const req = new MockNextRequest('https://example.com/ingest/e/')
await middleware(req as any)
expect(mockCookiesSet).not.toHaveBeenCalled()
})
it('proxies exact prefix path to root', async () => {
const middleware = insightsMiddleware({
apiKey: 'phc_test123',
proxy: true,
})
const req = new MockNextRequest('https://example.com/ingest')
await middleware(req as any)
const rewriteUrl: URL = mockNextResponseRewrite.mock.calls[0][0]
expect(rewriteUrl.pathname).toBe('/')
})
it('supports custom pathPrefix', async () => {
const middleware = insightsMiddleware({
apiKey: 'phc_test123',
proxy: { pathPrefix: '/ph-proxy' },
})
const req = new MockNextRequest('https://example.com/ph-proxy/e/')
await middleware(req as any)
const rewriteUrl: URL = mockNextResponseRewrite.mock.calls[0][0]
expect(rewriteUrl.origin).toBe('https://insights.hanzo.ai')
expect(rewriteUrl.pathname).toBe('/e/')
})
it('normalizes pathPrefix without leading slash', async () => {
const middleware = insightsMiddleware({
apiKey: 'phc_test123',
proxy: { pathPrefix: 'ingest' },
})
const req = new MockNextRequest('https://example.com/ingest/e/')
await middleware(req as any)
expect(mockNextResponseRewrite).toHaveBeenCalled()
const rewriteUrl: URL = mockNextResponseRewrite.mock.calls[0][0]
expect(rewriteUrl.pathname).toBe('/e/')
})
it('supports custom host', async () => {
const middleware = insightsMiddleware({
apiKey: 'phc_test123',
proxy: { host: 'https://eu.insights.hanzo.ai' },
})
const req = new MockNextRequest('https://example.com/ingest/e/')
await middleware(req as any)
const rewriteUrl: URL = mockNextResponseRewrite.mock.calls[0][0]
expect(rewriteUrl.origin).toBe('https://eu.insights.hanzo.ai')
expect(rewriteUrl.pathname).toBe('/e/')
})
it('trims custom proxy host before rewriting', async () => {
const middleware = insightsMiddleware({
apiKey: 'phc_test123',
proxy: { host: ' https://eu.insights.hanzo.ai/\t ' },
})
const req = new MockNextRequest('https://example.com/ingest/e/')
await middleware(req as any)
const rewriteUrl: URL = mockNextResponseRewrite.mock.calls[0][0]
expect(rewriteUrl.origin).toBe('https://eu.insights.hanzo.ai')
expect(rewriteUrl.pathname).toBe('/e/')
})
it('has no effect when proxy is false', async () => {
const middleware = insightsMiddleware({
apiKey: 'phc_test123',
proxy: false,
})
const req = new MockNextRequest('https://example.com/ingest/e/')
await middleware(req as any)
expect(mockNextResponseRewrite).not.toHaveBeenCalled()
expect(mockNextResponseNext).toHaveBeenCalled()
})
it('has no effect when proxy is undefined', async () => {
const middleware = insightsMiddleware({
apiKey: 'phc_test123',
})
const req = new MockNextRequest('https://example.com/ingest/e/')
await middleware(req as any)
expect(mockNextResponseRewrite).not.toHaveBeenCalled()
expect(mockNextResponseNext).toHaveBeenCalled()
})
})
describe('consent awareness', () => {
const CONSENT_COOKIE = '__ph_opt_in_out_phc_test123'
it('does not seed identity cookie when opted out (consent cookie = 0)', async () => {
const middleware = insightsMiddleware({ apiKey: 'phc_test123' })
const req = new MockNextRequest('https://example.com/', {
[CONSENT_COOKIE]: '0',
})
await middleware(req as any)
expect(mockCookiesSet).not.toHaveBeenCalled()
})
it('seeds identity cookie when opted in (consent cookie = 1)', async () => {
const middleware = insightsMiddleware({ apiKey: 'phc_test123' })
const req = new MockNextRequest('https://example.com/', {
[CONSENT_COOKIE]: '1',
})
await middleware(req as any)
expect(mockCookiesSet).toHaveBeenCalledWith(COOKIE_NAME, expect.any(String), expect.any(Object))
})
it('does not seed when no consent cookie and seedAnonymousCookie is false', async () => {
const middleware = insightsMiddleware({
apiKey: 'phc_test123',
seedAnonymousCookie: false,
})
const req = new MockNextRequest('https://example.com/')
await middleware(req as any)
expect(mockCookiesSet).not.toHaveBeenCalled()
})
it('seeds when no consent cookie and seedAnonymousCookie is true (default)', async () => {
const middleware = insightsMiddleware({ apiKey: 'phc_test123' })
const req = new MockNextRequest('https://example.com/')
await middleware(req as any)
expect(mockCookiesSet).toHaveBeenCalled()
})
it('deletes existing identity cookie when opted out', async () => {
const middleware = insightsMiddleware({ apiKey: 'phc_test123' })
const req = new MockNextRequest('https://example.com/', {
[COOKIE_NAME]: JSON.stringify({ distinct_id: 'user_123', $device_id: 'device_abc' }),
[CONSENT_COOKIE]: '0',
})
await middleware(req as any)
expect(mockCookiesDelete).toHaveBeenCalledWith(COOKIE_NAME)
})
it('uses custom consentCookieName', async () => {
const middleware = insightsMiddleware({
apiKey: 'phc_test123',
consentCookieName: 'my_consent',
})
const req = new MockNextRequest('https://example.com/', {
my_consent: '0',
})
await middleware(req as any)
expect(mockCookiesSet).not.toHaveBeenCalled()
})
})
})