Files
orca/src/main/ipc/telemetry.test.ts
T
Neil 77f23b013f refactor(shared): drop the shared/types barrel and import from the real modules (#14447)
#14397 split `shared/types.ts` into 46 per-domain modules but kept the path as
a re-export barrel so the import sites did not have to change. This removes
the barrel: every consumer now imports from the module that actually declares
the type, and `src/shared/types.ts` is deleted.

Barrels hide where a type lives, make every consumer look like it depends on
the whole domain, and let an unrelated edit invalidate a module that ~2,000
files transitively import.

2,323 import declarations across 2,321 files. Rewritten mechanically: each
specifier was resolved to an absolute path via the TypeScript AST and
recomputed, rather than string-substituted, so alias forms (`@/../../shared/
types`) and per-specifier `type` modifiers survive.

Four cases the mechanical pass had to handle, each found by a gate rather than
by reading the diff:

- Modules inside `src/shared` import the barrel as `./types`, not
  `shared/types`. A pre-filter on the latter string skipped 176 of them and
  left imports dangling at a deleted file, which surfaced as confusing
  `Property 'x' is optional in type 'Repo' but required in Pick<Repo, ...>`
  errors rather than "module not found".
- The barrel RENAMED one type on the way through
  (`WorkspaceSource as WorkspaceCreateTelemetrySource`), so the original name
  in the owning module has to be re-aliased at each consumer.
- Three test files put `;(globalThis as ...)` on the line after the import.
  TypeScript parses that `;` as the import statement's terminator, so
  replacing through `statement.getEnd()` deletes it and breaks ASI. The
  rewrite now stops at the module specifier.
- A file that already imported directly from a module got a SECOND import
  from it, because the barrel re-exported those same names — which trips
  `import/no-duplicates` under `--deny-warnings`. A post-pass merges
  declarations sharing a specifier and type-only-ness; the `import type` plus
  `import` pair from one module is left alone, since that form is allowed.

Splitting one barrel import into several genuinely adds lines, which pushed
`terminal-layout-pty-ownership.ts` to 301 counted lines: its 107-character
import must wrap, and neither local type collapses onto one line (101 and 116
characters). Rather than contort a type declaration to fit a line budget,
`collectLeafIds` and `pruneLeaves` move to `terminal-pane-layout-tree.ts` —
they are pure structural operations on the layout tree and independent of PTY
ownership. `visible-worktrees.ts` similarly loses its own mini-barrel
re-export of `isDefaultBranchWorkspace`, with the four real consumers
repointed at the declaring module. No `max-lines` bypass added.

Verified: cold `tsc --noEmit` green on node, cli, and web (buildinfo deleted
first — these projects are `composite: true` and reuse stale caches); the full
`pnpm lint` green, not just bare oxlint — the narrower local check is what let
the duplicate imports reach CI; max-lines ratchet OK at 344.
2026-08-13 22:48:24 -07:00

469 lines
19 KiB
TypeScript

// Telemetry IPC boundary tests: the renderer is in the threat model, so handlers drop malformed calls, cap consent mutations, and derive `via` main-side.
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { GlobalSettings } from '../../shared/global-settings-types'
import type { Store } from '../persistence'
const handlers = new Map<string, (_event: unknown, ...args: unknown[]) => unknown>()
const {
handleMock,
trackMock,
setOptInMock,
persistBannerAcknowledgeMock,
consumeConsentMutationTokenMock,
getCohortAtEmitMock,
getOnboardingCohortAtEmitMock
} = vi.hoisted(() => ({
handleMock: vi.fn(),
trackMock: vi.fn(),
setOptInMock: vi.fn(),
persistBannerAcknowledgeMock: vi.fn(),
consumeConsentMutationTokenMock: vi.fn(),
getCohortAtEmitMock: vi.fn(),
getOnboardingCohortAtEmitMock: vi.fn()
}))
vi.mock('electron', () => ({ ipcMain: { handle: handleMock } }))
vi.mock('../telemetry/client', () => ({
track: trackMock,
setOptIn: setOptInMock,
persistBannerAcknowledgeWithoutEmitting: persistBannerAcknowledgeMock
}))
vi.mock('../telemetry/burst-cap', () => ({
consumeConsentMutationToken: consumeConsentMutationTokenMock
}))
vi.mock('../telemetry/cohort-classifier', () => ({
getCohortAtEmit: getCohortAtEmitMock
}))
vi.mock('../telemetry/onboarding-cohort-classifier', () => ({
getOnboardingCohortAtEmit: getOnboardingCohortAtEmitMock
}))
import { _resetStoreForTests, registerTelemetryHandlers } from './telemetry'
function captureHandlers(): void {
handlers.clear()
for (const call of handleMock.mock.calls) {
const [channel, handler] = call as [
string,
typeof handlers extends Map<string, infer V> ? V : never
]
handlers.set(channel, handler)
}
}
// Fake Store whose `telemetry` block tests reassign between invocations to seed derivation states.
type FakeStoreState = { settings: GlobalSettings }
function makeFakeStore(telemetry: GlobalSettings['telemetry']): {
store: Store
state: FakeStoreState
} {
const state: FakeStoreState = { settings: { telemetry } as unknown as GlobalSettings }
const store = {
getSettings: vi.fn(() => state.settings),
updateSettings: vi.fn((updates: Partial<GlobalSettings>) => {
state.settings = { ...state.settings, ...updates } as GlobalSettings
return state.settings
})
} as unknown as Store
return { store, state }
}
function registerWith(telemetry: GlobalSettings['telemetry']): FakeStoreState {
const { store, state } = makeFakeStore(telemetry)
registerTelemetryHandlers(store)
captureHandlers()
return state
}
describe('telemetry IPC handlers', () => {
beforeEach(() => {
handleMock.mockReset()
trackMock.mockReset()
setOptInMock.mockReset()
persistBannerAcknowledgeMock.mockReset()
consumeConsentMutationTokenMock.mockReset()
consumeConsentMutationTokenMock.mockReturnValue(true)
getCohortAtEmitMock.mockReset()
getCohortAtEmitMock.mockReturnValue({ nth_repo_added: 0 })
getOnboardingCohortAtEmitMock.mockReset()
getOnboardingCohortAtEmitMock.mockReturnValue({ cohort: undefined })
_resetStoreForTests()
})
afterEach(() => {
vi.restoreAllMocks()
})
it('registers all four channels', () => {
registerWith({
installId: 'x',
existedBeforeTelemetryRelease: false,
optedIn: true
})
expect(handlers.has('telemetry:track')).toBe(true)
expect(handlers.has('telemetry:setOptIn')).toBe(true)
expect(handlers.has('telemetry:acknowledgeBanner')).toBe(true)
expect(handlers.has('telemetry:getConsentState')).toBe(true)
})
// ── telemetry:track ──────────────────────────────────────────────────
it('forwards a well-typed track call to track() and injects cohort for COHORT_EXTENDED events', () => {
registerWith({ installId: 'x', existedBeforeTelemetryRelease: false, optedIn: true })
getCohortAtEmitMock.mockReturnValue({ nth_repo_added: 2 })
const handler = handlers.get('telemetry:track')!
handler({}, 'app_opened', {})
expect(trackMock).toHaveBeenCalledTimes(1)
expect(trackMock).toHaveBeenCalledWith('app_opened', { nth_repo_added: 2 })
})
// Schemas are `.strict()`, so injecting `nth_repo_added` on a non-cohort event would drop the whole event at the validator.
it('does NOT inject cohort on events outside COHORT_EXTENDED', () => {
registerWith({ installId: 'x', existedBeforeTelemetryRelease: false, optedIn: true })
const handler = handlers.get('telemetry:track')!
handler({}, 'settings_changed', { setting_key: 'editorAutoSave', value_kind: 'bool' })
expect(trackMock).toHaveBeenCalledTimes(1)
expect(trackMock).toHaveBeenCalledWith('settings_changed', {
setting_key: 'editorAutoSave',
value_kind: 'bool'
})
expect(getCohortAtEmitMock).not.toHaveBeenCalled()
})
// Renderer-only Setup-step events depend on the handler injecting cohort so call sites stay synchronous.
it('injects cohort for add_repo_setup_step_action (renderer-only event)', () => {
registerWith({ installId: 'x', existedBeforeTelemetryRelease: false, optedIn: true })
getCohortAtEmitMock.mockReturnValue({ nth_repo_added: 1 })
const handler = handlers.get('telemetry:track')!
handler({}, 'add_repo_setup_step_action', { action: 'skip' })
expect(trackMock).toHaveBeenCalledWith('add_repo_setup_step_action', {
action: 'skip',
nth_repo_added: 1
})
})
it('drops main-owned events from renderer telemetry IPC', () => {
registerWith({ installId: 'x', existedBeforeTelemetryRelease: false, optedIn: true })
const handler = handlers.get('telemetry:track')!
handler({}, 'app_starred_orca', { source: 'settings' })
handler({}, 'star_nag_outcome', {
outcome: 'shown',
source: 'threshold',
mode: 'gh',
threshold: 35,
agents_since_baseline: 35,
agents_since_baseline_bucket: '35-69'
})
handler({}, 'feature_interaction_usage_bucket_reached', {
feature_id: 'tasks',
feature_category: 'task_management',
count_bucket: 'count_1',
bucket_source: 'crossed_now'
})
handler({}, 'daemon_audit_eligibility', {})
expect(trackMock).not.toHaveBeenCalled()
expect(getCohortAtEmitMock).not.toHaveBeenCalled()
})
it('injects cohort for setup script prompt events', () => {
registerWith({ installId: 'x', existedBeforeTelemetryRelease: false, optedIn: true })
getCohortAtEmitMock.mockReturnValue({ nth_repo_added: 3 })
const handler = handlers.get('telemetry:track')!
handler({}, 'setup_script_prompt_shown', {
mode: 'import_available',
provider: 'codex',
file_count_bucket: '1',
unsupported_field_count_bucket: '0',
has_shared_hooks: false
})
handler({}, 'setup_script_prompt_action', {
action: 'configure_clicked',
mode: 'configure_needed',
file_count_bucket: '0',
unsupported_field_count_bucket: '0',
has_shared_hooks: true
})
expect(trackMock).toHaveBeenCalledWith('setup_script_prompt_shown', {
mode: 'import_available',
provider: 'codex',
file_count_bucket: '1',
unsupported_field_count_bucket: '0',
has_shared_hooks: false,
nth_repo_added: 3
})
expect(trackMock).toHaveBeenCalledWith('setup_script_prompt_action', {
action: 'configure_clicked',
mode: 'configure_needed',
file_count_bucket: '0',
unsupported_field_count_bucket: '0',
has_shared_hooks: true,
nth_repo_added: 3
})
})
// Fail-soft: `nth_repo_added` is optional, so an undefined cohort still validates.
it('forwards undefined cohort when the classifier returns undefined', () => {
registerWith({ installId: 'x', existedBeforeTelemetryRelease: false, optedIn: true })
getCohortAtEmitMock.mockReturnValue({ nth_repo_added: undefined })
const handler = handlers.get('telemetry:track')!
handler({}, 'app_opened', {})
expect(trackMock).toHaveBeenCalledWith('app_opened', { nth_repo_added: undefined })
})
// Security: same spread-order invariant as cohort — main-derived `nth_repo_added` overrides any renderer-forged value.
it('main-derived nth_repo_added overrides renderer-supplied value', () => {
registerWith({ installId: 'x', existedBeforeTelemetryRelease: false, optedIn: true })
getCohortAtEmitMock.mockReturnValue({ nth_repo_added: 2 })
const handler = handlers.get('telemetry:track')!
handler({}, 'app_opened', { nth_repo_added: 99 })
expect(trackMock).toHaveBeenCalledWith('app_opened', { nth_repo_added: 2 })
})
// ── Onboarding cohort injection (mirrors the nth_repo_added pattern) ──
it('injects onboarding cohort on events whose schema declares cohort', () => {
registerWith({ installId: 'x', existedBeforeTelemetryRelease: false, optedIn: true })
getOnboardingCohortAtEmitMock.mockReturnValue({ cohort: 'fresh_install' })
const handler = handlers.get('telemetry:track')!
handler({}, 'onboarding_step_viewed', { step: 1, value_kind: 'agent' })
expect(trackMock).toHaveBeenCalledWith('onboarding_step_viewed', {
step: 1,
value_kind: 'agent',
cohort: 'fresh_install'
})
})
it('does NOT inject onboarding cohort on non-onboarding events', () => {
registerWith({ installId: 'x', existedBeforeTelemetryRelease: false, optedIn: true })
const handler = handlers.get('telemetry:track')!
handler({}, 'settings_changed', { setting_key: 'editorAutoSave', value_kind: 'bool' })
expect(getOnboardingCohortAtEmitMock).not.toHaveBeenCalled()
})
it('forwards undefined onboarding cohort fail-soft', () => {
registerWith({ installId: 'x', existedBeforeTelemetryRelease: true, optedIn: null })
getOnboardingCohortAtEmitMock.mockReturnValue({ cohort: undefined })
const handler = handlers.get('telemetry:track')!
handler({}, 'onboarding_started', {})
expect(trackMock).toHaveBeenCalledWith('onboarding_started', { cohort: undefined })
})
// Security: main spreads cohort after caller props so main wins; flipping the order would let a renderer forge `cohort`.
it('main-derived cohort overrides renderer-supplied cohort', () => {
registerWith({ installId: 'x', existedBeforeTelemetryRelease: false, optedIn: true })
getOnboardingCohortAtEmitMock.mockReturnValue({ cohort: 'fresh_install' })
const handler = handlers.get('telemetry:track')!
// Caller tries to forge cohort='upgrade_backfill'; main must overwrite.
handler({}, 'onboarding_started', { cohort: 'upgrade_backfill' })
expect(trackMock).toHaveBeenCalledWith('onboarding_started', {
cohort: 'fresh_install'
})
})
// Security: a fail-soft undefined cohort must still overwrite a forged value; a conditional-assign refactor would regress this.
it('main-derived undefined cohort overrides renderer-supplied cohort (degraded classifier)', () => {
registerWith({ installId: 'x', existedBeforeTelemetryRelease: true, optedIn: true })
getOnboardingCohortAtEmitMock.mockReturnValue({ cohort: undefined })
const handler = handlers.get('telemetry:track')!
// Forged cohort='upgrade_backfill' is stripped by the explicit-undefined spread.
handler({}, 'onboarding_started', { cohort: 'upgrade_backfill' })
expect(trackMock).toHaveBeenCalledWith('onboarding_started', {
cohort: undefined
})
})
it('drops track calls with a non-string name', () => {
registerWith({ installId: 'x', existedBeforeTelemetryRelease: false, optedIn: true })
const handler = handlers.get('telemetry:track')!
handler({}, 42, {})
handler({}, null, {})
handler({}, { event: 'app_opened' }, {})
expect(trackMock).not.toHaveBeenCalled()
})
it('drops track calls with non-object props', () => {
registerWith({ installId: 'x', existedBeforeTelemetryRelease: false, optedIn: true })
const handler = handlers.get('telemetry:track')!
handler({}, 'app_opened', 'string-not-object')
handler({}, 'app_opened', 42)
expect(trackMock).not.toHaveBeenCalled()
})
it('treats null/undefined props as an empty object', () => {
registerWith({ installId: 'x', existedBeforeTelemetryRelease: false, optedIn: true })
getCohortAtEmitMock.mockReturnValue({ nth_repo_added: 0 })
const handler = handlers.get('telemetry:track')!
handler({}, 'app_opened', null)
handler({}, 'app_opened', undefined)
expect(trackMock).toHaveBeenCalledTimes(2)
expect(trackMock).toHaveBeenNthCalledWith(1, 'app_opened', { nth_repo_added: 0 })
expect(trackMock).toHaveBeenNthCalledWith(2, 'app_opened', { nth_repo_added: 0 })
})
// ── telemetry:setOptIn — input narrowing ─────────────────────────────
it('drops setOptIn with non-boolean optedIn', () => {
registerWith({ installId: 'x', existedBeforeTelemetryRelease: false, optedIn: true })
const handler = handlers.get('telemetry:setOptIn')!
handler({}, 'true')
handler({}, 1)
handler({}, null)
handler({}, undefined)
expect(setOptInMock).not.toHaveBeenCalled()
// None of these should have consumed a mutation token either.
expect(consumeConsentMutationTokenMock).not.toHaveBeenCalled()
})
it('drops setOptIn past the consent-mutation rate limit', () => {
registerWith({ installId: 'x', existedBeforeTelemetryRelease: false, optedIn: true })
const handler = handlers.get('telemetry:setOptIn')!
consumeConsentMutationTokenMock.mockReturnValue(false)
handler({}, true)
expect(setOptInMock).not.toHaveBeenCalled()
})
// ── telemetry:setOptIn — `via` derivation ────────────────────────────
it("derives via='first_launch_banner' for an existing user with optedIn=null clicking Turn off", () => {
// Only path where an existing user (optedIn=null) flips to false: the notice's "Turn off" button.
registerWith({
installId: 'x',
existedBeforeTelemetryRelease: true,
optedIn: null
})
const handler = handlers.get('telemetry:setOptIn')!
handler({}, false)
expect(setOptInMock).toHaveBeenCalledWith('first_launch_banner', false)
})
it("derives via='settings' (not 'first_launch_banner') for a defensive opt-in call from the pre-notice state", () => {
// Security: refuse via='first_launch_banner' on a forged setOptIn(true) pre-notice; fall through to 'settings'.
registerWith({
installId: 'x',
existedBeforeTelemetryRelease: true,
optedIn: null
})
const handler = handlers.get('telemetry:setOptIn')!
handler({}, true)
expect(setOptInMock).toHaveBeenCalledWith('settings', true)
})
it("derives via='settings' for a new user toggling off from Settings (no first-launch surface exists)", () => {
// New users (existed=false) see no first-launch surface, so any opt-out routes through Settings.
registerWith({
installId: 'x',
existedBeforeTelemetryRelease: false,
optedIn: true
})
const handler = handlers.get('telemetry:setOptIn')!
handler({}, false)
expect(setOptInMock).toHaveBeenCalledWith('settings', false)
})
it("derives via='settings' for an opt-in toggle flip after a prior opt-out", () => {
// Re-opt-in from Settings: neither cohort marker nor notice state triggers a first-launch tag.
registerWith({
installId: 'x',
existedBeforeTelemetryRelease: true,
optedIn: false
})
const handler = handlers.get('telemetry:setOptIn')!
handler({}, true)
expect(setOptInMock).toHaveBeenCalledWith('settings', true)
})
it("derives via='settings' for a new user flipping Settings off→on (not a first-launch interaction)", () => {
registerWith({
installId: 'x',
existedBeforeTelemetryRelease: false,
optedIn: false
})
const handler = handlers.get('telemetry:setOptIn')!
handler({}, true)
expect(setOptInMock).toHaveBeenCalledWith('settings', true)
})
it("derives via='settings' when the telemetry block is missing (defensive)", () => {
// Should never happen post-migration; handler must fall through to 'settings', not throw.
registerWith(undefined)
const handler = handlers.get('telemetry:setOptIn')!
handler({}, true)
expect(setOptInMock).toHaveBeenCalledWith('settings', true)
})
// ── telemetry:acknowledgeBanner — silent-persist path ────────────────
it('routes banner ✕ through persistBannerAcknowledgeWithoutEmitting without invoking setOptIn', () => {
// Why the separate channel: reaching setOptIn would derive a `via` and fire `telemetry_opted_in`; acknowledge must persist silently.
registerWith({
installId: 'x',
existedBeforeTelemetryRelease: true,
optedIn: null
})
const handler = handlers.get('telemetry:acknowledgeBanner')!
handler({})
expect(persistBannerAcknowledgeMock).toHaveBeenCalledTimes(1)
expect(setOptInMock).not.toHaveBeenCalled()
expect(trackMock).not.toHaveBeenCalled()
})
it('acknowledgeBanner consumes a consent-mutation token and drops past the cap', () => {
registerWith({
installId: 'x',
existedBeforeTelemetryRelease: true,
optedIn: null
})
const handler = handlers.get('telemetry:acknowledgeBanner')!
consumeConsentMutationTokenMock.mockReturnValue(false)
handler({})
expect(persistBannerAcknowledgeMock).not.toHaveBeenCalled()
})
// ── telemetry:acknowledgeBanner — state-precondition guard ───────────
// Security: guard passes only (existed=true, optedIn=null) and runs before token consumption, blocking a compromised renderer from flipping resolved consent.
it('acknowledgeBanner rejects an existing user who already opted in', () => {
registerWith({
installId: 'x',
existedBeforeTelemetryRelease: true,
optedIn: true
})
const handler = handlers.get('telemetry:acknowledgeBanner')!
handler({})
expect(persistBannerAcknowledgeMock).not.toHaveBeenCalled()
expect(consumeConsentMutationTokenMock).not.toHaveBeenCalled()
})
it('acknowledgeBanner rejects an existing user who already opted out', () => {
registerWith({
installId: 'x',
existedBeforeTelemetryRelease: true,
optedIn: false
})
const handler = handlers.get('telemetry:acknowledgeBanner')!
handler({})
expect(persistBannerAcknowledgeMock).not.toHaveBeenCalled()
expect(consumeConsentMutationTokenMock).not.toHaveBeenCalled()
})
it('acknowledgeBanner rejects the new-user cohort regardless of optedIn', () => {
registerWith({
installId: 'x',
existedBeforeTelemetryRelease: false,
optedIn: true
})
const handler = handlers.get('telemetry:acknowledgeBanner')!
handler({})
expect(persistBannerAcknowledgeMock).not.toHaveBeenCalled()
expect(consumeConsentMutationTokenMock).not.toHaveBeenCalled()
})
it('acknowledgeBanner rejects a missing telemetry block', () => {
registerWith(undefined)
const handler = handlers.get('telemetry:acknowledgeBanner')!
handler({})
expect(persistBannerAcknowledgeMock).not.toHaveBeenCalled()
expect(consumeConsentMutationTokenMock).not.toHaveBeenCalled()
})
})