docs: enterprise refactor plan with ralph specs
This commit is contained in:
140
tests/db/repositories.test.ts
Normal file
140
tests/db/repositories.test.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* Unit tests for SessionRepository and AnomalyRepository using in-memory SQLite.
|
||||
*/
|
||||
|
||||
import Database from 'better-sqlite3';
|
||||
import { runMigrations } from '../../src/db/migrations';
|
||||
import { SessionRepository } from '../../src/db/SessionRepository';
|
||||
import { AnomalyRepository } from '../../src/db/AnomalyRepository';
|
||||
import { IAnomaly } from '../../src/core/interfaces';
|
||||
|
||||
function makeDb(): Database.Database {
|
||||
const db = new Database(':memory:');
|
||||
db.pragma('foreign_keys = ON');
|
||||
runMigrations(db);
|
||||
return db;
|
||||
}
|
||||
|
||||
function makeAnomaly(id: string): IAnomaly {
|
||||
return {
|
||||
id,
|
||||
type: 'http_error',
|
||||
severity: 'high',
|
||||
observationId: 'obs1',
|
||||
actionTrace: [],
|
||||
description: 'Test anomaly',
|
||||
evidence: { rawErrors: ['500 error'] },
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
describe('SessionRepository', () => {
|
||||
let db: Database.Database;
|
||||
let repo: SessionRepository;
|
||||
|
||||
beforeEach(() => {
|
||||
db = makeDb();
|
||||
repo = new SessionRepository(db);
|
||||
});
|
||||
|
||||
afterEach(() => db.close());
|
||||
|
||||
it('creates and retrieves a session', () => {
|
||||
repo.create({ id: 'sess1', url: 'http://x.com', seed: 42, maxStates: 50, startedAt: 1000 });
|
||||
const row = repo.findById('sess1');
|
||||
expect(row).toBeDefined();
|
||||
expect(row!.url).toBe('http://x.com');
|
||||
expect(row!.seed).toBe(42);
|
||||
expect(row!.status).toBe('running');
|
||||
});
|
||||
|
||||
it('returns undefined for unknown id', () => {
|
||||
expect(repo.findById('nope')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('findAll returns all sessions', () => {
|
||||
repo.create({ id: 's1', url: 'http://a.com', seed: 1, maxStates: 10, startedAt: 1000 });
|
||||
repo.create({ id: 's2', url: 'http://b.com', seed: 2, maxStates: 10, startedAt: 2000 });
|
||||
expect(repo.findAll()).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('updates fields', () => {
|
||||
repo.create({ id: 's1', url: 'http://a.com', seed: 1, maxStates: 10, startedAt: 1000 });
|
||||
repo.update('s1', { status: 'completed', statesVisited: 5, anomaliesFound: 2, finishedAt: 9999 });
|
||||
const row = repo.findById('s1')!;
|
||||
expect(row.status).toBe('completed');
|
||||
expect(row.states_visited).toBe(5);
|
||||
expect(row.anomalies_found).toBe(2);
|
||||
expect(row.finished_at).toBe(9999);
|
||||
});
|
||||
|
||||
it('deletes a session', () => {
|
||||
repo.create({ id: 's1', url: 'http://a.com', seed: 1, maxStates: 10, startedAt: 1000 });
|
||||
repo.delete('s1');
|
||||
expect(repo.findById('s1')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('AnomalyRepository', () => {
|
||||
let db: Database.Database;
|
||||
let sessionRepo: SessionRepository;
|
||||
let anomalyRepo: AnomalyRepository;
|
||||
|
||||
beforeEach(() => {
|
||||
db = makeDb();
|
||||
sessionRepo = new SessionRepository(db);
|
||||
anomalyRepo = new AnomalyRepository(db);
|
||||
sessionRepo.create({ id: 'sess1', url: 'http://x.com', seed: 42, maxStates: 50, startedAt: 1000 });
|
||||
sessionRepo.create({ id: 'sess2', url: 'http://y.com', seed: 43, maxStates: 50, startedAt: 2000 });
|
||||
});
|
||||
|
||||
afterEach(() => db.close());
|
||||
|
||||
it('creates and retrieves an anomaly', () => {
|
||||
const a = makeAnomaly('anom1');
|
||||
anomalyRepo.create(a, 'sess1');
|
||||
const found = anomalyRepo.findById('anom1');
|
||||
expect(found).toBeDefined();
|
||||
expect(found!.id).toBe('anom1');
|
||||
expect(found!.sessionId).toBe('sess1');
|
||||
expect(found!.severity).toBe('high');
|
||||
});
|
||||
|
||||
it('returns undefined for unknown id', () => {
|
||||
expect(anomalyRepo.findById('nope')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('findAll with sessionId filter', () => {
|
||||
anomalyRepo.create(makeAnomaly('a1'), 'sess1');
|
||||
anomalyRepo.create(makeAnomaly('a2'), 'sess2');
|
||||
const result = anomalyRepo.findAll({ sessionId: 'sess1' });
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]!.id).toBe('a1');
|
||||
});
|
||||
|
||||
it('findAll with severity filter', () => {
|
||||
const low = { ...makeAnomaly('a1'), severity: 'low' as const };
|
||||
const high = { ...makeAnomaly('a2'), severity: 'high' as const };
|
||||
anomalyRepo.create(low, 'sess1');
|
||||
anomalyRepo.create(high, 'sess1');
|
||||
const result = anomalyRepo.findAll({ severity: 'low' });
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]!.id).toBe('a1');
|
||||
});
|
||||
|
||||
it('count returns total', () => {
|
||||
anomalyRepo.create(makeAnomaly('a1'), 'sess1');
|
||||
anomalyRepo.create(makeAnomaly('a2'), 'sess2');
|
||||
expect(anomalyRepo.count()).toBe(2);
|
||||
});
|
||||
|
||||
it('countBySeverity returns correct count', () => {
|
||||
const high = { ...makeAnomaly('a1'), severity: 'high' as const };
|
||||
const critical = { ...makeAnomaly('a2'), severity: 'critical' as const };
|
||||
const low = { ...makeAnomaly('a3'), severity: 'low' as const };
|
||||
anomalyRepo.create(high, 'sess1');
|
||||
anomalyRepo.create(critical, 'sess1');
|
||||
anomalyRepo.create(low, 'sess1');
|
||||
expect(anomalyRepo.countBySeverity(['high', 'critical'])).toBe(2);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user