mirror of
https://github.com/jeffusion/gitea-ai-assistant.git
synced 2026-03-27 10:05:50 +00:00
- Add @biomejs/biome as dev dependency - Remove deprecated tslint dependency - Add biome.json with project-specific rules - Update lint script to use Biome - Apply Biome auto-fixes across codebase
138 lines
5.9 KiB
TypeScript
138 lines
5.9 KiB
TypeScript
import { describe, expect, test } from 'bun:test';
|
||
import { JudgeAgent } from '../agents/judge-agent';
|
||
import type { Finding } from '../types';
|
||
|
||
type TestFinding = Omit<Finding, 'id' | 'runId' | 'published'>;
|
||
|
||
function makeFinding(overrides: Partial<TestFinding> = {}): TestFinding {
|
||
return {
|
||
fingerprint: `fp-${Math.random().toString(36).slice(2, 8)}`,
|
||
category: 'correctness',
|
||
severity: 'medium',
|
||
confidence: 0.8,
|
||
path: 'src/foo.ts',
|
||
line: 10,
|
||
title: 'Test issue',
|
||
detail: 'Detail',
|
||
evidence: 'Evidence',
|
||
suggestion: 'Fix it',
|
||
...overrides,
|
||
};
|
||
}
|
||
|
||
describe('JudgeAgent', () => {
|
||
const judge = new JudgeAgent();
|
||
|
||
// ─── Empty input ───
|
||
test('empty findings → summary says 未发现', () => {
|
||
const result = judge.judge([]);
|
||
expect(result.findings).toHaveLength(0);
|
||
expect(result.summaryMarkdown).toContain('未发现');
|
||
});
|
||
|
||
// ─── Deduplication by fingerprint ───
|
||
test('duplicate fingerprints → keeps highest weighted', () => {
|
||
const fp = 'same-fingerprint';
|
||
const findings: TestFinding[] = [
|
||
makeFinding({ fingerprint: fp, severity: 'low', confidence: 0.9 }), // weight: 1 * 0.9 = 0.9
|
||
makeFinding({ fingerprint: fp, severity: 'high', confidence: 0.5 }), // weight: 3 * 0.5 = 1.5 ← winner
|
||
makeFinding({ fingerprint: fp, severity: 'medium', confidence: 0.6 }), // weight: 2 * 0.6 = 1.2
|
||
];
|
||
const result = judge.judge(findings);
|
||
expect(result.findings).toHaveLength(1);
|
||
expect(result.findings[0].severity).toBe('high');
|
||
expect(result.findings[0].confidence).toBe(0.5);
|
||
});
|
||
|
||
test('same fingerprint same weight → first one wins (no override)', () => {
|
||
const fp = 'dup-fp';
|
||
const findings: TestFinding[] = [
|
||
makeFinding({ fingerprint: fp, severity: 'high', confidence: 0.5, title: 'First' }),
|
||
makeFinding({ fingerprint: fp, severity: 'high', confidence: 0.5, title: 'Second' }),
|
||
];
|
||
const result = judge.judge(findings);
|
||
expect(result.findings).toHaveLength(1);
|
||
// Same weight → second does NOT override (currentWeight > existingWeight is strict >)
|
||
expect(result.findings[0].title).toBe('First');
|
||
});
|
||
|
||
// ─── Sorting by severity × confidence ───
|
||
test('findings sorted by weight descending', () => {
|
||
const findings: TestFinding[] = [
|
||
makeFinding({ fingerprint: 'a', severity: 'low', confidence: 0.9 }), // 1 * 0.9 = 0.9
|
||
makeFinding({ fingerprint: 'b', severity: 'high', confidence: 0.8 }), // 3 * 0.8 = 2.4
|
||
makeFinding({ fingerprint: 'c', severity: 'medium', confidence: 0.7 }), // 2 * 0.7 = 1.4
|
||
];
|
||
const result = judge.judge(findings);
|
||
expect(result.findings).toHaveLength(3);
|
||
expect(result.findings[0].fingerprint).toBe('b'); // weight 2.4
|
||
expect(result.findings[1].fingerprint).toBe('c'); // weight 1.4
|
||
expect(result.findings[2].fingerprint).toBe('a'); // weight 0.9
|
||
});
|
||
|
||
// ─── Summary text ───
|
||
test('summary counts by severity', () => {
|
||
const findings: TestFinding[] = [
|
||
makeFinding({ fingerprint: 'a', severity: 'high', confidence: 0.9 }),
|
||
makeFinding({ fingerprint: 'b', severity: 'high', confidence: 0.85 }),
|
||
makeFinding({ fingerprint: 'c', severity: 'medium', confidence: 0.8 }),
|
||
makeFinding({ fingerprint: 'd', severity: 'low', confidence: 0.7 }),
|
||
];
|
||
const result = judge.judge(findings);
|
||
expect(result.summaryMarkdown).toContain('4 个问题');
|
||
expect(result.summaryMarkdown).toContain('high 2');
|
||
expect(result.summaryMarkdown).toContain('medium 1');
|
||
expect(result.summaryMarkdown).toContain('low 1');
|
||
});
|
||
|
||
test('single finding → counts correctly', () => {
|
||
const findings: TestFinding[] = [
|
||
makeFinding({ fingerprint: 'x', severity: 'medium', confidence: 0.8 }),
|
||
];
|
||
const result = judge.judge(findings);
|
||
expect(result.summaryMarkdown).toContain('1 个问题');
|
||
expect(result.summaryMarkdown).toContain('high 0');
|
||
expect(result.summaryMarkdown).toContain('medium 1');
|
||
expect(result.summaryMarkdown).toContain('low 0');
|
||
});
|
||
|
||
// ─── Dedup + sort combined ───
|
||
test('dedup then sort: complex scenario', () => {
|
||
const findings: TestFinding[] = [
|
||
makeFinding({ fingerprint: 'x', severity: 'low', confidence: 0.3 }), // weight 0.3 — will be overridden
|
||
makeFinding({ fingerprint: 'y', severity: 'high', confidence: 0.9 }), // weight 2.7 — unique
|
||
makeFinding({ fingerprint: 'x', severity: 'medium', confidence: 0.8 }), // weight 1.6 — overrides x
|
||
makeFinding({ fingerprint: 'z', severity: 'high', confidence: 0.5 }), // weight 1.5 — unique
|
||
];
|
||
const result = judge.judge(findings);
|
||
expect(result.findings).toHaveLength(3); // x, y, z (deduped)
|
||
// Sorted by weight: y(2.7) > x(1.6) > z(1.5)
|
||
expect(result.findings[0].fingerprint).toBe('y');
|
||
expect(result.findings[1].fingerprint).toBe('x');
|
||
expect(result.findings[1].severity).toBe('medium'); // overridden version
|
||
expect(result.findings[2].fingerprint).toBe('z');
|
||
});
|
||
|
||
// ─── All same severity ───
|
||
test('all high severity → sorted by confidence descending', () => {
|
||
const findings: TestFinding[] = [
|
||
makeFinding({ fingerprint: 'a', severity: 'high', confidence: 0.5 }),
|
||
makeFinding({ fingerprint: 'b', severity: 'high', confidence: 0.9 }),
|
||
makeFinding({ fingerprint: 'c', severity: 'high', confidence: 0.7 }),
|
||
];
|
||
const result = judge.judge(findings);
|
||
expect(result.findings[0].fingerprint).toBe('b');
|
||
expect(result.findings[1].fingerprint).toBe('c');
|
||
expect(result.findings[2].fingerprint).toBe('a');
|
||
});
|
||
|
||
// ─── Return type structure ───
|
||
test('result has summaryMarkdown and findings', () => {
|
||
const result = judge.judge([]);
|
||
expect(result).toHaveProperty('summaryMarkdown');
|
||
expect(result).toHaveProperty('findings');
|
||
expect(typeof result.summaryMarkdown).toBe('string');
|
||
expect(Array.isArray(result.findings)).toBe(true);
|
||
});
|
||
});
|