docs: enterprise refactor plan with ralph specs

This commit is contained in:
debian
2026-03-04 16:17:03 -05:00
parent 4c92712d20
commit f8191133c8
204 changed files with 32722 additions and 422 deletions

View File

@@ -0,0 +1,133 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { MarkdownExporter } from '../../../src/plugins/exporters/MarkdownExporter';
import { IAnomaly, IAction } from '../../../src/core/interfaces';
function makeAnomaly(overrides: Partial<IAnomaly> = {}): IAnomaly {
const action: IAction = {
id: 'act-1',
type: 'navigate',
url: 'http://localhost:3000/register',
timestamp: 1700000000000,
seed: 42,
stateId: 's1',
};
return {
id: 'anom-md-001',
type: 'http_error',
severity: 'high',
observationId: 'obs-1',
actionTrace: [action],
description: 'Form submission returns HTTP 500 on empty email field',
evidence: {
screenshotPath: 'anom-md-001/screenshot.png',
domSnapshotPath: 'anom-md-001/dom.html',
httpLog: [{ url: '/api/register', status: 500, method: 'POST', durationMs: 234 }],
rawErrors: ['POST /api/register → 500 (234ms)'],
},
timestamp: 1700000000000,
...overrides,
};
}
describe('MarkdownExporter', () => {
let tmpDir: string;
beforeEach(() => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'abe-md-'));
});
afterEach(() => {
fs.rmSync(tmpDir, { recursive: true, force: true });
});
it('creates report.md in the output directory', async () => {
const exporter = new MarkdownExporter();
const outputDir = path.join(tmpDir, 'anom-md-001');
const filePath = await exporter.export(makeAnomaly(), outputDir);
expect(fs.existsSync(filePath)).toBe(true);
expect(filePath.endsWith('report.md')).toBe(true);
});
it('includes anomaly type and date in title', async () => {
const exporter = new MarkdownExporter();
const outputDir = path.join(tmpDir, 'title-test');
const filePath = await exporter.export(makeAnomaly(), outputDir);
const content = fs.readFileSync(filePath, 'utf8');
expect(content).toContain('# Bug Report');
expect(content).toContain('http_error');
});
it('includes severity section', async () => {
const exporter = new MarkdownExporter();
const outputDir = path.join(tmpDir, 'severity-test');
const filePath = await exporter.export(makeAnomaly(), outputDir);
const content = fs.readFileSync(filePath, 'utf8');
expect(content).toContain('## Severity');
expect(content).toContain('high');
});
it('includes reproduction steps with navigate action', async () => {
const exporter = new MarkdownExporter();
const outputDir = path.join(tmpDir, 'steps-test');
const filePath = await exporter.export(makeAnomaly(), outputDir);
const content = fs.readFileSync(filePath, 'utf8');
expect(content).toContain('## Reproduction Steps');
expect(content).toContain('Navigate to');
expect(content).toContain('http://localhost:3000/register');
});
it('includes seed and replay command', async () => {
const exporter = new MarkdownExporter();
const outputDir = path.join(tmpDir, 'seed-test');
const filePath = await exporter.export(makeAnomaly(), outputDir);
const content = fs.readFileSync(filePath, 'utf8');
expect(content).toContain('Seed used');
expect(content).toContain('42');
expect(content).toContain('Replay command');
expect(content).toContain('npm run replay');
});
it('includes evidence section with screenshot and dom paths', async () => {
const exporter = new MarkdownExporter();
const outputDir = path.join(tmpDir, 'evidence-test');
const filePath = await exporter.export(makeAnomaly(), outputDir);
const content = fs.readFileSync(filePath, 'utf8');
expect(content).toContain('## Evidence');
expect(content).toContain('screenshot.png');
expect(content).toContain('dom.html');
});
it('includes HTTP table when responses present', async () => {
const exporter = new MarkdownExporter();
const outputDir = path.join(tmpDir, 'http-test');
const filePath = await exporter.export(makeAnomaly(), outputDir);
const content = fs.readFileSync(filePath, 'utf8');
expect(content).toContain('500');
expect(content).toContain('/api/register');
expect(content).toContain('POST');
});
it('includes raw errors section', async () => {
const exporter = new MarkdownExporter();
const outputDir = path.join(tmpDir, 'errors-test');
const filePath = await exporter.export(makeAnomaly(), outputDir);
const content = fs.readFileSync(filePath, 'utf8');
expect(content).toContain('## Raw Errors');
expect(content).toContain('POST /api/register');
});
it('has format = markdown', () => {
expect(new MarkdownExporter().format).toBe('markdown');
});
it('handles anomaly with no action trace', async () => {
const exporter = new MarkdownExporter();
const outputDir = path.join(tmpDir, 'no-trace-test');
const anomaly = makeAnomaly({ actionTrace: [] });
const filePath = await exporter.export(anomaly, outputDir);
const content = fs.readFileSync(filePath, 'utf8');
expect(content).toContain('No steps recorded');
});
});