import { describe, it, expect } from 'vitest'
import { adaptSession, adaptProject } from './adapters'
import type { SessionDTO, ProjectDTO } from './adapters'
function makeDTO(overrides: Partial<SessionDTO> = {}): SessionDTO {
return {
owner: 'bigbes',
tool: 'claude-code',
host: 'laptop',
session_id: 'abc123',
started_at: 1_000_000,
ended_at: 1_001_000,
source_file: '/path/to/session.jsonl',
summary: 'A test session',
turn_count: 5,
tokens_in_total: 1234,
tokens_out_total: 5678,
...overrides,
}
}
describe('adaptSession', () => {
it('started_at=0 → started="1970-01-01T00:00:00.000Z"', () => {
const s = adaptSession(makeDTO({ started_at: 0, ended_at: 0 }))
expect(s.started).toBe('1970-01-01T00:00:00.000Z')
})
it('working_dir absent → cwd === ""', () => {
const s = adaptSession(makeDTO({ working_dir: undefined }))
expect(s.cwd).toBe('')
})
it('model absent → model === undefined (not null)', () => {
const s = adaptSession(makeDTO({ model: undefined }))
expect(s.model).toBeUndefined()
})
it('id is composite ${tool}/${host}/${session_id}', () => {
const s = adaptSession(makeDTO({ tool: 'opencode', host: 'workpc', session_id: 'xyz789' }))
expect(s.id).toBe('opencode/workpc/xyz789')
const parts = s.id.split('/')
expect(parts[0]).toBe('opencode')
expect(parts[1]).toBe('workpc')
expect(parts[2]).toBe('xyz789')
})
it('ended_at === started_at → ended === null', () => {
const ts = 1_000_000
const s = adaptSession(makeDTO({ started_at: ts, ended_at: ts }))
expect(s.ended).toBeNull()
})
it('ended_at > started_at → ended is the ISO of ended_at', () => {
const startedAt = 1_000_000
const endedAt = 1_001_000
const s = adaptSession(makeDTO({ started_at: startedAt, ended_at: endedAt }))
expect(s.ended).toBe(new Date(endedAt * 1000).toISOString())
})
})
function makeProjectDTO(overrides: Partial<ProjectDTO> = {}): ProjectDTO {
return {
cwd: '/home/user/code/project',
sessions: 3,
turn_count: 12,
tokens_in_total: 5000,
tokens_out_total: 8000,
last_active: 1_700_000_000,
hosts: ['laptop', 'workpc'],
tools: ['claude-code', 'opencode'],
top_tool: 'claude-code',
...overrides,
}
}
describe('adaptProject', () => {
it('passes through empty arrays unchanged', () => {
const p = adaptProject(makeProjectDTO({ hosts: [], tools: [] }))
expect(p.hosts).toEqual([])
expect(p.tools).toEqual([])
})
it('last_active=0 → lastActive === ""', () => {
const p = adaptProject(makeProjectDTO({ last_active: 0 }))
expect(p.lastActive).toBe('')
})
it('last_active=1700000000 → ISO string "2023-11-14T22:13:20.000Z"', () => {
const p = adaptProject(makeProjectDTO({ last_active: 1_700_000_000 }))
expect(p.lastActive).toBe(new Date(1_700_000_000 * 1000).toISOString())
// Verify the concrete value for determinism
expect(p.lastActive).toBe('2023-11-14T22:13:20.000Z')
})
it('maps snake_case fields to camelCase', () => {
const p = adaptProject(makeProjectDTO())
expect(p.turns).toBe(12)
expect(p.tokensIn).toBe(5000)
expect(p.tokensOut).toBe(8000)
expect(p.topTool).toBe('claude-code')
expect(p.sessions).toBe(3)
expect(p.cwd).toBe('/home/user/code/project')
})
it('passes through hosts and tools arrays', () => {
const p = adaptProject(makeProjectDTO({ hosts: ['laptop'], tools: ['claude-code', 'opencode'] }))
expect(p.hosts).toEqual(['laptop'])
expect(p.tools).toEqual(['claude-code', 'opencode'])
})
})