Files
DashDescomplicar/api/tests/sessions-route.test.ts
T
ealmeida f733998945 feat(sessions): indexação multi-fonte Hermes + OpenCode com fix TypeScript
- Novos indexadores hermes-indexer.ts e opencode-indexer.ts para unificar
  sessões Claude, Hermes Agent e OpenCode num único sessions.db
- SessionMeta alargado: source (obrigatório), model, input/output_tokens,
  estimated_cost; project_path/jsonl_path agora nullable
- Fix TS: tipos explícitos, guard jsonl_path null, dependências instaladas

Security Audit (Regra #47):
- npm audit executado — 0 vulnerabilities após fix
- vite 7→8.0.16 (breaking upgrade, resolve esbuild CVE GHSA-gv7w-rqvm-qjhr)
- vitest 4.0.18→4.1.9 (resolve esbuild interno CVE GHSA-gv7w-rqvm-qjhr)
- shell-quote override ^1.8.4 via package.json#overrides (CVE GHSA-w7jw-789q-3m8p)
- react-router, joi, qs, form-data, ip-address, js-yaml resolvidos via npm audit fix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 19:42:05 +01:00

74 lines
2.3 KiB
TypeScript

/**
* Testes da rota /api/sessions (validação Zod + integração com SessionsDb).
* @author Descomplicar® | Projecto Observabilidade (Espelho)
*/
import { describe, it, expect, beforeAll } from 'vitest'
import express from 'express'
import request from 'supertest'
import { mkdtempSync } from 'fs'
import { tmpdir } from 'os'
import { join } from 'path'
import { openSessionsDb } from '../services/sessions/db.js'
import { createSessionsRouter } from '../routes/sessions.js'
import type { SessionMeta } from '../types/session.js'
function meta(overrides: Partial<SessionMeta> = {}): SessionMeta {
return {
session_id: 's1',
source: 'claude',
project_path: '/tmp/p',
project_slug: 'p',
jsonl_path: '/tmp/p/s1.jsonl',
model: null,
started_at: new Date().toISOString(),
ended_at: null,
duration_sec: 60,
event_count: 10,
user_messages: 2,
assistant_msgs: 5,
tool_calls: 3,
input_tokens: null,
output_tokens: null,
estimated_cost: null,
first_prompt: 'teste',
tools_used: ['Bash'],
skills_invoked: [],
outcome: 'completed',
permission_mode: 'default',
file_size: 1000,
indexed_at: new Date().toISOString(),
...overrides,
}
}
describe('GET /api/sessions', () => {
let app: express.Express
beforeAll(() => {
const dbPath = join(mkdtempSync(join(tmpdir(), 'obs-r-')), 'sessions.db')
const db = openSessionsDb(dbPath)
db.upsertSession(meta({ session_id: 's1', project_slug: 'alpha', jsonl_path: '/tmp/p/s1.jsonl' }))
db.upsertSession(meta({ session_id: 's2', project_slug: 'beta', jsonl_path: '/tmp/p/s2.jsonl' }))
app = express()
app.use('/api/sessions', createSessionsRouter(db))
})
it('lista todas as sessões por omissão', async () => {
const res = await request(app).get('/api/sessions')
expect(res.status).toBe(200)
expect(res.body.total).toBe(2)
expect(res.body.items).toHaveLength(2)
})
it('filtra por projecto', async () => {
const res = await request(app).get('/api/sessions').query({ project: 'alpha' })
expect(res.status).toBe(200)
expect(res.body.total).toBe(1)
expect(res.body.items[0].project_slug).toBe('alpha')
})
it('rejeita limit inválido', async () => {
const res = await request(app).get('/api/sessions').query({ limit: '9999' })
expect(res.status).toBe(400)
})
})