Files
Autonomous-Bug-Explorer/tests/modules/reporting.test.ts

199 lines
6.7 KiB
TypeScript

import { Report } from '../../src/modules/reporting/domain/entities/Report';
import { ReportFormat } from '../../src/modules/reporting/domain/value-objects/ReportFormat';
import { ReportStatus } from '../../src/modules/reporting/domain/value-objects/ReportStatus';
import { GenerateReportCommand } from '../../src/modules/reporting/application/commands/GenerateReportCommand';
import { IReportRepository } from '../../src/modules/reporting/domain/ports/IReportRepository';
import { EventBus } from '../../src/shared/application/EventBus';
import { DomainEvent } from '../../src/shared/domain/DomainEvent';
import { EventHandler } from '../../src/shared/application/EventHandler';
// ─── Mock Repository ──────────────────────────────────────────────────────────
class InMemoryReportRepository implements IReportRepository {
private store = new Map<string, Report>();
async save(report: Report): Promise<void> {
this.store.set(report.id.toString(), report);
}
async findById(id: string): Promise<Report | undefined> {
return this.store.get(id);
}
async findAll(): Promise<Report[]> {
return Array.from(this.store.values());
}
async update(report: Report): Promise<void> {
this.store.set(report.id.toString(), report);
}
}
// ─── Mock EventBus ────────────────────────────────────────────────────────────
class MockEventBus implements EventBus {
published: DomainEvent[] = [];
async publish(event: DomainEvent): Promise<void> {
this.published.push(event);
}
subscribe(_name: string, _handler: EventHandler): void {
// no-op
}
}
// ─── Report domain tests ───────────────────────────────────────────────────────
describe('ReportFormat', () => {
it('parses valid formats', () => {
expect(ReportFormat.fromString('html').value).toBe('html');
expect(ReportFormat.fromString('json').value).toBe('json');
expect(ReportFormat.fromString('pdf').value).toBe('pdf');
});
it('throws on invalid format', () => {
expect(() => ReportFormat.fromString('xml')).toThrow();
});
});
describe('ReportStatus', () => {
it('creates statuses', () => {
expect(ReportStatus.pending().value).toBe('pending');
expect(ReportStatus.generating().value).toBe('generating');
expect(ReportStatus.ready().value).toBe('ready');
expect(ReportStatus.failed().value).toBe('failed');
});
it('parses from string', () => {
expect(ReportStatus.fromString('ready').value).toBe('ready');
});
it('throws on unknown status', () => {
expect(() => ReportStatus.fromString('unknown')).toThrow();
});
});
describe('Report aggregate', () => {
it('creates a report with pending status and emits ReportRequested event', () => {
const report = Report.create({
title: 'Test Report',
format: ReportFormat.fromString('html'),
filters: { severity: 'high' },
});
expect(report.title).toBe('Test Report');
expect(report.status.value).toBe('pending');
expect(report.totalFindings).toBe(0);
const events = report.clearEvents();
expect(events).toHaveLength(1);
expect(events[0]!.eventName).toBe('reporting.report_requested');
});
it('marks report as generating', () => {
const report = Report.create({
title: 'T',
format: ReportFormat.fromString('json'),
filters: {},
});
report.clearEvents();
report.markGenerating();
expect(report.status.value).toBe('generating');
});
it('marks report as ready and emits ReportGenerated', () => {
const report = Report.create({
title: 'T',
format: ReportFormat.fromString('json'),
filters: {},
});
report.clearEvents();
report.markReady('/reports/123/report.json', 5);
expect(report.status.value).toBe('ready');
expect(report.filePath).toBe('/reports/123/report.json');
expect(report.totalFindings).toBe(5);
expect(report.completedAt).toBeInstanceOf(Date);
const events = report.clearEvents();
expect(events[0]!.eventName).toBe('reporting.report_generated');
});
it('marks report as failed and emits ReportFailed', () => {
const report = Report.create({
title: 'T',
format: ReportFormat.fromString('pdf'),
filters: {},
});
report.clearEvents();
report.markFailed('Playwright error');
expect(report.status.value).toBe('failed');
expect(report.errorMessage).toBe('Playwright error');
const events = report.clearEvents();
expect(events[0]!.eventName).toBe('reporting.report_failed');
});
});
// ─── GenerateReportCommand tests ───────────────────────────────────────────────
describe('GenerateReportCommand', () => {
let repo: InMemoryReportRepository;
let eventBus: MockEventBus;
let cmd: GenerateReportCommand;
beforeEach(() => {
repo = new InMemoryReportRepository();
eventBus = new MockEventBus();
cmd = new GenerateReportCommand(repo, eventBus);
});
it('creates and persists a report, returns Ok with reportId', async () => {
const result = await cmd.execute({ title: 'My Report', format: 'html' });
expect(result.ok).toBe(true);
if (!result.ok) return;
const { reportId, status } = result.value;
expect(status).toBe('pending');
const saved = await repo.findById(reportId);
expect(saved).toBeDefined();
expect(saved!.title).toBe('My Report');
expect(saved!.format.value).toBe('html');
});
it('publishes ReportRequested event after creation', async () => {
await cmd.execute({ title: 'Report', format: 'json' });
expect(eventBus.published).toHaveLength(1);
expect(eventBus.published[0]!.eventName).toBe('reporting.report_requested');
});
it('applies filters when provided', async () => {
const result = await cmd.execute({
title: 'Filtered',
format: 'pdf',
filters: { sessionId: 'abc-123', severity: 'critical' },
});
expect(result.ok).toBe(true);
if (!result.ok) return;
const saved = await repo.findById(result.value.reportId);
expect(saved!.filters.sessionId).toBe('abc-123');
expect(saved!.filters.severity).toBe('critical');
});
it('returns Err for invalid format', async () => {
const result = await cmd.execute({ title: 'Bad', format: 'xml' as 'html' });
expect(result.ok).toBe(false);
if (result.ok) return;
expect(result.error).toContain('Invalid format');
});
});