mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-08-04 05:41:29 +00:00
145 lines
4.7 KiB
TypeScript
145 lines
4.7 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import { aggregateSessions, renderJson, renderTable } from '../src/sessions-report.js'
|
|
import type { ClassifiedTurn, ProjectSummary, SessionSummary } from '../src/types.js'
|
|
|
|
function makeProject(): ProjectSummary {
|
|
const turn: ClassifiedTurn = {
|
|
userMessage: 'build it',
|
|
timestamp: '2026-07-10T10:00:00.000Z',
|
|
sessionId: 'session-1',
|
|
category: 'feature',
|
|
retries: 0,
|
|
hasEdits: true,
|
|
assistantCalls: [{
|
|
provider: 'claude',
|
|
model: 'claude-sonnet-4-5',
|
|
usage: {
|
|
inputTokens: 100,
|
|
outputTokens: 20,
|
|
cacheCreationInputTokens: 30,
|
|
cacheReadInputTokens: 400,
|
|
cachedInputTokens: 0,
|
|
reasoningTokens: 0,
|
|
webSearchRequests: 0,
|
|
},
|
|
costUSD: 0.12,
|
|
tools: [],
|
|
mcpTools: [],
|
|
skills: [],
|
|
subagentTypes: [],
|
|
hasAgentSpawn: false,
|
|
hasPlanMode: false,
|
|
speed: 'standard',
|
|
timestamp: '2026-07-10T10:01:00.000Z',
|
|
bashCommands: [],
|
|
deduplicationKey: 'call-1',
|
|
}],
|
|
}
|
|
const session: SessionSummary = {
|
|
sessionId: 'session-1',
|
|
project: 'codeburn',
|
|
firstTimestamp: '2026-07-10T10:00:00.000Z',
|
|
lastTimestamp: '2026-07-10T10:05:00.000Z',
|
|
totalCostUSD: 0.12,
|
|
totalSavingsUSD: 0.03,
|
|
totalInputTokens: 100,
|
|
totalOutputTokens: 20,
|
|
totalReasoningTokens: 0,
|
|
totalCacheReadTokens: 400,
|
|
totalCacheWriteTokens: 30,
|
|
apiCalls: 1,
|
|
turns: [turn],
|
|
modelBreakdown: {
|
|
'claude-sonnet-4-5': { calls: 1, costUSD: 0.12, tokens: turn.assistantCalls[0]!.usage, savingsUSD: 0.03 },
|
|
},
|
|
toolBreakdown: {},
|
|
mcpBreakdown: {},
|
|
bashBreakdown: {},
|
|
categoryBreakdown: {} as SessionSummary['categoryBreakdown'],
|
|
skillBreakdown: {},
|
|
subagentBreakdown: {},
|
|
}
|
|
return {
|
|
project: 'codeburn',
|
|
projectPath: '/tmp/codeburn',
|
|
sessions: [session],
|
|
totalCostUSD: 0.12,
|
|
totalSavingsUSD: 0.03,
|
|
totalApiCalls: 1,
|
|
totalProxiedCostUSD: 0,
|
|
}
|
|
}
|
|
|
|
describe('sessions JSON emitter', () => {
|
|
it('flattens SessionSummary fields into the exact JSON row shape', () => {
|
|
const rows = aggregateSessions([makeProject()])
|
|
const parsed = JSON.parse(renderJson(rows))
|
|
|
|
expect(parsed).toEqual([{
|
|
sessionId: 'session-1',
|
|
title: '',
|
|
project: 'codeburn',
|
|
provider: 'claude',
|
|
models: ['claude-sonnet-4-5'],
|
|
cost: 0.12,
|
|
savingsUSD: 0.03,
|
|
calls: 1,
|
|
turns: 1,
|
|
inputTokens: 100,
|
|
outputTokens: 20,
|
|
cacheReadTokens: 400,
|
|
cacheWriteTokens: 30,
|
|
startedAt: '2026-07-10T10:00:00.000Z',
|
|
endedAt: '2026-07-10T10:05:00.000Z',
|
|
durationMs: 300_000,
|
|
}])
|
|
})
|
|
|
|
it('renders a simple table', () => {
|
|
const output = renderTable(aggregateSessions([makeProject()]), { terminalWidth: 120 })
|
|
expect(output).toContain('Session')
|
|
expect(output).toContain('session-1')
|
|
expect(output).toContain('Sonnet 4.5')
|
|
expect(output).toContain('1 sessions')
|
|
})
|
|
|
|
it('hides home-directory slugs and renders compact agent/worktree names', () => {
|
|
const rows = aggregateSessions([makeProject()])
|
|
rows[0]!.sessionId = 'agent-a72cc958e305e4957'
|
|
rows[0]!.project = '-Users-torukmakto-Projects-eywa-eywa--claude-worktrees-issue-131'
|
|
const output = renderTable(rows, { terminalWidth: 160 })
|
|
|
|
expect(output).toContain('Agent a72cc958')
|
|
expect(output).toContain('eywa · issue-131')
|
|
expect(output).not.toContain('Users-torukmakto')
|
|
})
|
|
|
|
it('normalizes Claude dot-worktree slugs without exposing the home directory', () => {
|
|
const rows = aggregateSessions([makeProject()])
|
|
rows[0]!.project = 'Users-torukmakto-codeburn-.claude-worktrees-agent-a213f7c77871f483f'
|
|
const output = renderTable(rows, { terminalWidth: 120 })
|
|
|
|
expect(output).toContain('codeburn · agent a213f7c7')
|
|
expect(output).not.toContain('Users-torukmakto')
|
|
expect(output).not.toContain('.claude-worktrees')
|
|
})
|
|
|
|
it('uses captured titles, sorts newest first, and fits the requested width', () => {
|
|
const rows = aggregateSessions([makeProject()])
|
|
const older = { ...rows[0]!, sessionId: 'old', title: 'Older task', startedAt: '2026-07-01T10:00:00.000Z' }
|
|
const newer = {
|
|
...rows[0]!,
|
|
sessionId: 'new',
|
|
title: 'Review the authentication migration without exposing filesystem details',
|
|
project: '-Users-private-Projects-codeburn',
|
|
startedAt: '2026-07-20T10:00:00.000Z',
|
|
}
|
|
const output = renderTable([older, newer], { terminalWidth: 80 })
|
|
const lines = output.split('\n')
|
|
|
|
expect(output.indexOf('Review the')).toBeLessThan(output.indexOf('Older task'))
|
|
expect(output).not.toContain('Users-private')
|
|
expect(Math.max(...lines.slice(0, -1).map(line => line.length))).toBeLessThanOrEqual(80)
|
|
})
|
|
})
|