Files
zpan/server/services/instance-telemetry.test.ts
T
saltbo 53ca110218 feat(about): split runtime into runtime engine + deployment platform
Flatten the instance `runtime` object into two fields: `runtime` (the JS engine,
node | workerd) and `platform` (the deployment host). The About page shows each
as its own row with friendly labels (e.g. "workerd" + "Cloudflare Workers",
"Node.js" + "Docker").

- Detect the platform from the entry file (entry === target): each serverless
  entry declares it; entry-node sniffs Cloud Run (K_SERVICE) / Docker
  (ZPAN_RUNTIME, set in the Dockerfile) / bare node. Cloudflare is detected from
  the D1 binding.
- Decouple the cloud payload: zpan-cloud-sdk fixes runtime { provider, target },
  so CloudInstanceInfo keeps that shape and buildCloudInstanceInfo maps to it;
  buildInstanceInfo serves the richer flat shape to the About API.
- Migrate PostHog instance telemetry to the runtime/platform shape and merge the
  duplicate runtimeInfo in licensing-admin into the shared one.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 14:56:29 -04:00

201 lines
6.0 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
import { getOrCreateInstanceId } from '../licensing/instance-id'
import { getInstanceDisplayName } from '../licensing/instance-info'
import type { Database } from '../platform/interface'
import {
INSTANCE_TELEMETRY_CRON,
INSTANCE_TELEMETRY_EVENT,
INSTANCE_TELEMETRY_POSTHOG_HOST,
INSTANCE_TELEMETRY_POSTHOG_PROJECT_TOKEN,
reportInstanceTelemetry,
} from './instance-telemetry'
const posthogMocks = vi.hoisted(() => {
const captureImmediateMock = vi.fn()
const shutdownMock = vi.fn()
const PostHog = vi.fn(
class {
captureImmediate = captureImmediateMock
shutdown = shutdownMock
},
)
return { PostHog, captureImmediate: captureImmediateMock, shutdown: shutdownMock }
})
vi.mock('../licensing/instance-id', () => ({
getOrCreateInstanceId: vi.fn(),
}))
vi.mock('../licensing/instance-info', () => ({
getInstanceDisplayName: vi.fn(),
}))
vi.mock('posthog-node', () => ({
PostHog: posthogMocks.PostHog,
}))
describe('instance telemetry', () => {
beforeEach(() => {
vi.mocked(getOrCreateInstanceId).mockReset()
vi.mocked(getInstanceDisplayName).mockReset()
posthogMocks.PostHog.mockClear()
posthogMocks.captureImmediate.mockReset()
posthogMocks.shutdown.mockReset()
posthogMocks.captureImmediate.mockResolvedValue(undefined)
posthogMocks.shutdown.mockResolvedValue(undefined)
vi.mocked(getInstanceDisplayName).mockResolvedValue('Test Instance')
})
it('does not call the telemetry endpoint when PostHog project token is disabled', async () => {
const result = await reportInstanceTelemetry({
db: {} as Database,
config: { posthogProjectToken: '' },
cron: INSTANCE_TELEMETRY_CRON,
runtime: { runtime: 'workerd', platform: 'cloudflare-workers' },
})
expect(result).toEqual({ reported: false, reason: 'disabled' })
expect(posthogMocks.PostHog).not.toHaveBeenCalled()
expect(getOrCreateInstanceId).not.toHaveBeenCalled()
expect(getInstanceDisplayName).not.toHaveBeenCalled()
})
it('captures the expected telemetry event with built-in PostHog host and project token', async () => {
vi.mocked(getOrCreateInstanceId).mockResolvedValue('inst-1')
const result = await reportInstanceTelemetry({
db: {} as Database,
config: {
siteUrl: 'https://zpan.example.com/path',
},
cron: INSTANCE_TELEMETRY_CRON,
runtime: {
runtime: 'node',
platform: 'docker',
osPlatform: 'linux',
osArch: 'arm64',
osRelease: '6.8.0',
nodeVersion: 'v24.0.0',
},
now: new Date('2026-06-08T12:00:00.000Z'),
})
expect(result).toEqual({ reported: true })
expect(getOrCreateInstanceId).toHaveBeenCalledWith({})
expect(getInstanceDisplayName).toHaveBeenCalledWith({})
expect(posthogMocks.PostHog).toHaveBeenCalledWith(INSTANCE_TELEMETRY_POSTHOG_PROJECT_TOKEN, {
host: INSTANCE_TELEMETRY_POSTHOG_HOST,
flushAt: 1,
flushInterval: 0,
disableCompression: true,
disableGeoip: false,
})
expect(posthogMocks.captureImmediate).toHaveBeenCalledTimes(1)
expect(posthogMocks.captureImmediate).toHaveBeenCalledWith({
event: INSTANCE_TELEMETRY_EVENT,
distinctId: 'inst-1',
disableGeoip: false,
timestamp: new Date('2026-06-08T12:00:00.000Z'),
properties: {
instance: {
id: 'inst-1',
name: 'Test Instance',
url: 'https://zpan.example.com',
version: 'test-version',
runtime: 'node',
platform: 'docker',
server: {
os: {
platform: 'linux',
arch: 'arm64',
release: '6.8.0',
},
},
node: {
version: 'v24.0.0',
},
},
report_trigger: 'scheduled',
report_interval: '12h',
report_schedule: INSTANCE_TELEMETRY_CRON,
reported_at: '2026-06-08T12:00:00.000Z',
$current_url: 'https://zpan.example.com',
$set: {
instance: {
id: 'inst-1',
name: 'Test Instance',
url: 'https://zpan.example.com',
version: 'test-version',
runtime: 'node',
platform: 'docker',
server: {
os: {
platform: 'linux',
arch: 'arm64',
release: '6.8.0',
},
},
node: {
version: 'v24.0.0',
},
},
$current_url: 'https://zpan.example.com',
},
$set_once: {
initialInstance: {
id: 'inst-1',
name: 'Test Instance',
url: 'https://zpan.example.com',
version: 'test-version',
runtime: 'node',
platform: 'docker',
server: {
os: {
platform: 'linux',
arch: 'arm64',
release: '6.8.0',
},
},
node: {
version: 'v24.0.0',
},
},
initialReportedAt: '2026-06-08T12:00:00.000Z',
},
},
})
expect(posthogMocks.shutdown).toHaveBeenCalledTimes(1)
})
it('disables GeoIP when IP reporting is explicitly disabled', async () => {
vi.mocked(getOrCreateInstanceId).mockResolvedValue('inst-1')
await reportInstanceTelemetry({
db: {} as Database,
config: {
siteUrl: 'https://zpan.example.com',
allowIp: false,
},
cron: INSTANCE_TELEMETRY_CRON,
runtime: {
runtime: 'workerd',
platform: 'cloudflare-workers',
},
now: new Date('2026-06-08T12:00:00.000Z'),
})
expect(posthogMocks.PostHog).toHaveBeenCalledWith(INSTANCE_TELEMETRY_POSTHOG_PROJECT_TOKEN, {
host: INSTANCE_TELEMETRY_POSTHOG_HOST,
flushAt: 1,
flushInterval: 0,
disableCompression: true,
disableGeoip: true,
})
expect(posthogMocks.captureImmediate).toHaveBeenCalledWith(
expect.objectContaining({
disableGeoip: true,
}),
)
})
})