From 29aa9b1b5f2d83d3f41c2f41cfef6b32a4cf747c Mon Sep 17 00:00:00 2001 From: Jinwoo-H Date: Sat, 30 May 2026 05:29:24 -0700 Subject: [PATCH] Add usage attribution boundary tests Co-authored-by: Orca --- src/main/codex-usage/scanner.test.ts | 29 +++++++++++++ src/main/opencode-usage/scanner.test.ts | 54 +++++++++++++++++++------ 2 files changed, 71 insertions(+), 12 deletions(-) diff --git a/src/main/codex-usage/scanner.test.ts b/src/main/codex-usage/scanner.test.ts index f95b05c8031..62777257b06 100644 --- a/src/main/codex-usage/scanner.test.ts +++ b/src/main/codex-usage/scanner.test.ts @@ -258,6 +258,35 @@ describe('attributeCodexUsageEvent', () => { expect(attributed?.worktreeId).toBe('repo-1::/workspace/repo') }) + it('does not attribute true parent-directory escapes to the worktree', async () => { + const attributed = await attributeCodexUsageEvent( + { + sessionId: 'session-1', + timestamp: '2026-04-09T10:00:00.000Z', + cwd: '/workspace/repo/../other/session', + model: 'gpt-5.2-codex', + hasInferredPricing: false, + inputTokens: 100, + cachedInputTokens: 10, + outputTokens: 25, + reasoningOutputTokens: 10, + totalTokens: 125 + }, + [ + { + repoId: 'repo-1', + worktreeId: 'repo-1::/workspace/repo', + path: '/workspace/repo', + displayName: 'Repo', + canonicalPath: '/workspace/repo' + } + ] + ) + + expect(attributed?.projectKey).toBe('cwd:/workspace/repo/../other/session') + expect(attributed?.worktreeId).toBeNull() + }) + it('does not treat different Windows drives as containing paths', async () => { const attributed = await attributeCodexUsageEvent( { diff --git a/src/main/opencode-usage/scanner.test.ts b/src/main/opencode-usage/scanner.test.ts index 781600abf6c..d1be3de7006 100644 --- a/src/main/opencode-usage/scanner.test.ts +++ b/src/main/opencode-usage/scanner.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-lines -- Why: OpenCode scanner tests cover multiple DB schema generations and attribution boundaries together so parser regressions stay auditable. */ import Database from 'better-sqlite3' import { mkdtempSync, rmSync } from 'fs' import { tmpdir } from 'os' @@ -32,6 +33,21 @@ function worktrees() { ] } +function usageEvent(cwd: string) { + return { + sessionId: 'session-1', + timestamp: '2026-04-09T10:00:00.000Z', + cwd, + model: 'anthropic/claude-sonnet-4-5', + estimatedCostUsd: 0.012, + inputTokens: 100, + cachedInputTokens: 10, + outputTokens: 25, + reasoningOutputTokens: 10, + totalTokens: 125 + } +} + describe('parseOpenCodeUsageRow', () => { it('reads assistant message tokens, cost, model, cwd, and timestamp', () => { const parsed = parseOpenCodeUsageRow({ @@ -79,18 +95,7 @@ describe('parseOpenCodeUsageRow', () => { describe('attributeOpenCodeUsageEvent', () => { it('attributes cwd paths under dotdot-prefixed child directories to the worktree', async () => { const attributed = await attributeOpenCodeUsageEvent( - { - sessionId: 'session-1', - timestamp: '2026-04-09T10:00:00.000Z', - cwd: `${WORKTREE}/..fixtures/session`, - model: 'anthropic/claude-sonnet-4-5', - estimatedCostUsd: 0.012, - inputTokens: 100, - cachedInputTokens: 10, - outputTokens: 25, - reasoningOutputTokens: 10, - totalTokens: 125 - }, + usageEvent(`${WORKTREE}/..fixtures/session`), worktrees() ) @@ -98,6 +103,31 @@ describe('attributeOpenCodeUsageEvent', () => { expect(attributed?.projectLabel).toBe('Repo') expect(attributed?.worktreeId).toBe('repo-1::/workspace/repo') }) + + it('does not attribute true parent-directory escapes to the worktree', async () => { + const attributed = await attributeOpenCodeUsageEvent( + usageEvent(`${WORKTREE}/../other/session`), + worktrees() + ) + + expect(attributed?.projectKey).toBe('cwd:/workspace/repo/../other/session') + expect(attributed?.worktreeId).toBeNull() + }) + + it('does not treat different Windows drives as containing paths', async () => { + const attributed = await attributeOpenCodeUsageEvent(usageEvent('D:\\other\\repo'), [ + { + repoId: 'repo-1', + worktreeId: 'repo-1::C:\\repo', + path: 'C:\\repo', + displayName: 'Repo', + canonicalPath: 'C:\\repo' + } + ]) + + expect(attributed?.projectKey).toBe('cwd:d:/other/repo') + expect(attributed?.worktreeId).toBeNull() + }) }) describe('parseOpenCodeUsageDatabase', () => {