mirror of
https://github.com/stablyai/orca.git
synced 2026-09-26 16:02:43 +00:00
The three usage scanners and their stores, plus the renderer usage-overview model, each carried a file-level `eslint-disable max-lines` and had grown to 338-769 counted lines against a 300-line budget. AGENTS.md calls for splitting rather than suppressing, and config/max-lines-baseline.txt is a shrink-only ratchet, so this removes all seven suppressions and prunes their entries (341 -> 334). Each file is cut along the seams it already had -- and that several of the suppression comments named out loud: filesystem discovery / record parsing / attribution / aggregation for the scanners, and pricing policy / scope filters / rollups / session rows / automation attribution for the stores. Pure move, no behavior change. Code is relocated verbatim; the only edits are import plumbing and, where a private class method became a free function, the mechanical `this.state` -> `state` parameter threading. Every converted call site passes `this.state` at call time and the automation path takes a live `getState: () => this.state` getter, so no state is snapshotted. No barrel exports: each new module owns real logic and importers point at the owner. Verified: oxlint clean, ratchet passes, typecheck clean, full unit suite green (remaining failures are pre-existing load flakes in untouched files, each green when re-run serially), no import cycles among the 64 affected modules, and a statement-level diff of every split confirms the moves are verbatim.
325 lines
8.7 KiB
TypeScript
325 lines
8.7 KiB
TypeScript
import { describe, expect, it, vi } from 'vitest'
|
|
|
|
const { getPathMock } = vi.hoisted(() => ({
|
|
getPathMock: vi.fn<(name: string) => string>()
|
|
}))
|
|
|
|
vi.mock('electron', () => ({
|
|
app: {
|
|
getPath: getPathMock
|
|
}
|
|
}))
|
|
|
|
import { attributeCodexUsageEvent } from './codex-usage-event-attribution'
|
|
import { parseCodexUsageRecord } from './codex-usage-record-parser'
|
|
|
|
describe('parseCodexUsageRecord', () => {
|
|
it('uses token totals only as a duplicate baseline', () => {
|
|
const context = {
|
|
sessionId: 'session-1',
|
|
sessionCwd: null,
|
|
currentCwd: null,
|
|
currentModel: null,
|
|
previousTotals: null
|
|
}
|
|
|
|
expect(
|
|
parseCodexUsageRecord(
|
|
JSON.stringify({
|
|
type: 'session_meta',
|
|
payload: { id: 'session-1', cwd: '/workspace/repo' }
|
|
}),
|
|
context
|
|
)
|
|
).toBeNull()
|
|
|
|
expect(
|
|
parseCodexUsageRecord(
|
|
JSON.stringify({
|
|
type: 'turn_context',
|
|
payload: { cwd: '/workspace/repo/packages/app', model: 'gpt-5.2-codex' }
|
|
}),
|
|
context
|
|
)
|
|
).toBeNull()
|
|
|
|
const first = parseCodexUsageRecord(
|
|
JSON.stringify({
|
|
timestamp: '2026-04-09T10:00:00.000Z',
|
|
type: 'event_msg',
|
|
payload: {
|
|
type: 'token_count',
|
|
info: {
|
|
total_token_usage: {
|
|
input_tokens: 1000,
|
|
cached_input_tokens: 400,
|
|
output_tokens: 250,
|
|
reasoning_output_tokens: 100,
|
|
total_tokens: 1250
|
|
},
|
|
last_token_usage: {
|
|
input_tokens: 1000,
|
|
cached_input_tokens: 400,
|
|
output_tokens: 250,
|
|
reasoning_output_tokens: 100,
|
|
total_tokens: 1250
|
|
}
|
|
}
|
|
}
|
|
}),
|
|
context
|
|
)
|
|
|
|
const duplicate = parseCodexUsageRecord(
|
|
JSON.stringify({
|
|
timestamp: '2026-04-09T10:00:01.000Z',
|
|
type: 'event_msg',
|
|
payload: {
|
|
type: 'token_count',
|
|
info: {
|
|
total_token_usage: {
|
|
input_tokens: 1000,
|
|
cached_input_tokens: 400,
|
|
output_tokens: 250,
|
|
reasoning_output_tokens: 100,
|
|
total_tokens: 1250
|
|
},
|
|
last_token_usage: {
|
|
input_tokens: 1000,
|
|
cached_input_tokens: 400,
|
|
output_tokens: 250,
|
|
reasoning_output_tokens: 100,
|
|
total_tokens: 1250
|
|
}
|
|
}
|
|
}
|
|
}),
|
|
context
|
|
)
|
|
|
|
expect(first).toEqual({
|
|
sessionId: 'session-1',
|
|
timestamp: '2026-04-09T10:00:00.000Z',
|
|
eventKey: expect.any(String),
|
|
cwd: '/workspace/repo/packages/app',
|
|
model: 'gpt-5.2-codex',
|
|
hasInferredPricing: false,
|
|
inputTokens: 1000,
|
|
cachedInputTokens: 400,
|
|
outputTokens: 250,
|
|
reasoningOutputTokens: 100,
|
|
totalTokens: 1250
|
|
})
|
|
expect(duplicate).toBeNull()
|
|
})
|
|
|
|
it('preserves unknown model metadata instead of assigning fallback pricing', () => {
|
|
const context = {
|
|
sessionId: 'session-1',
|
|
sessionCwd: '/workspace/repo',
|
|
currentCwd: '/workspace/repo',
|
|
currentModel: null,
|
|
previousTotals: null
|
|
}
|
|
|
|
const parsed = parseCodexUsageRecord(
|
|
JSON.stringify({
|
|
timestamp: '2026-04-09T10:00:00.000Z',
|
|
type: 'event_msg',
|
|
payload: {
|
|
type: 'token_count',
|
|
info: {
|
|
last_token_usage: {
|
|
input_tokens: 120,
|
|
cached_input_tokens: 20,
|
|
output_tokens: 50,
|
|
reasoning_output_tokens: 10,
|
|
total_tokens: 170
|
|
}
|
|
}
|
|
}
|
|
}),
|
|
context
|
|
)
|
|
|
|
expect(parsed?.model).toBeNull()
|
|
expect(parsed?.hasInferredPricing).toBe(true)
|
|
})
|
|
|
|
it('uses last token usage for the first resumed-session event', () => {
|
|
const context = {
|
|
sessionId: 'session-1',
|
|
sessionCwd: '/workspace/repo',
|
|
currentCwd: '/workspace/repo',
|
|
currentModel: 'gpt-5.5',
|
|
previousTotals: null
|
|
}
|
|
|
|
const parsed = parseCodexUsageRecord(
|
|
JSON.stringify({
|
|
timestamp: '2026-04-09T10:00:00.000Z',
|
|
type: 'event_msg',
|
|
payload: {
|
|
type: 'token_count',
|
|
info: {
|
|
total_token_usage: {
|
|
input_tokens: 50_000,
|
|
cached_input_tokens: 40_000,
|
|
output_tokens: 5_000,
|
|
reasoning_output_tokens: 1_000,
|
|
total_tokens: 55_000
|
|
},
|
|
last_token_usage: {
|
|
input_tokens: 2_000,
|
|
cached_input_tokens: 1_500,
|
|
output_tokens: 500,
|
|
reasoning_output_tokens: 100,
|
|
total_tokens: 2_500
|
|
}
|
|
}
|
|
}
|
|
}),
|
|
context
|
|
)
|
|
|
|
expect(parsed).toMatchObject({
|
|
inputTokens: 2_000,
|
|
cachedInputTokens: 1_500,
|
|
outputTokens: 500,
|
|
reasoningOutputTokens: 100,
|
|
totalTokens: 2_500
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('attributeCodexUsageEvent', () => {
|
|
it('attributes nested cwd paths to the nearest containing worktree', async () => {
|
|
const attributed = await attributeCodexUsageEvent(
|
|
{
|
|
sessionId: 'session-1',
|
|
timestamp: '2026-04-09T10:00:00.000Z',
|
|
eventKey: 'event-1',
|
|
cwd: '/workspace/repo/app2/subdir',
|
|
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/app',
|
|
path: '/workspace/repo/app',
|
|
displayName: 'App',
|
|
canonicalPath: '/workspace/repo/app'
|
|
},
|
|
{
|
|
repoId: 'repo-2',
|
|
worktreeId: 'repo-2::/workspace/repo/app2',
|
|
path: '/workspace/repo/app2',
|
|
displayName: 'App 2',
|
|
canonicalPath: '/workspace/repo/app2'
|
|
}
|
|
]
|
|
)
|
|
|
|
expect(attributed?.projectKey).toBe('worktree:repo-2::/workspace/repo/app2')
|
|
expect(attributed?.projectLabel).toBe('App 2')
|
|
expect(attributed?.worktreeId).toBe('repo-2::/workspace/repo/app2')
|
|
})
|
|
|
|
it('attributes cwd paths under dotdot-prefixed child directories to the worktree', async () => {
|
|
const attributed = await attributeCodexUsageEvent(
|
|
{
|
|
sessionId: 'session-1',
|
|
timestamp: '2026-04-09T10:00:00.000Z',
|
|
eventKey: 'event-1',
|
|
cwd: '/workspace/repo/..fixtures/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('worktree:repo-1::/workspace/repo')
|
|
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 attributeCodexUsageEvent(
|
|
{
|
|
sessionId: 'session-1',
|
|
timestamp: '2026-04-09T10:00:00.000Z',
|
|
eventKey: 'event-1',
|
|
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(
|
|
{
|
|
sessionId: 'session-1',
|
|
timestamp: '2026-04-09T10:00:00.000Z',
|
|
eventKey: 'event-1',
|
|
cwd: 'D:\\other\\repo',
|
|
model: 'gpt-5.2-codex',
|
|
hasInferredPricing: false,
|
|
inputTokens: 100,
|
|
cachedInputTokens: 10,
|
|
outputTokens: 25,
|
|
reasoningOutputTokens: 10,
|
|
totalTokens: 125
|
|
},
|
|
[
|
|
{
|
|
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()
|
|
})
|
|
})
|