Files
Dragan Spiridonov 126667257a feat: multi-language Phase 5 — coverage parsers, language routing, MCP schemas (#324)
Add 6 multi-language coverage format parsers:
- JaCoCo XML (Java/Kotlin) with line/branch/method/instruction counters
- dotcover XML (C#/.NET) with statement-level coverage
- Tarpaulin JSON (Rust) with trace-based line coverage
- Go cover text format with block-level statement tracking
- Kover XML (Kotlin/JVM) via JaCoCo-compatible parsing
- xcresult JSON (Swift/iOS) with function-level detail

Add language-aware agent routing:
- New `languages` field on AgentCapabilityProfile
- 0.15 score boost for agents matching request language
- Configurable language weight in RoutingWeights
- Case-insensitive, backward-compatible

Update MCP tool schemas:
- Add `language` and `coverageFormat` params to coverage analyze/gaps tools
- Auto-detect format from filename and content signatures
- Extended findAndParseCoverage with multi-language file paths

24 new tests, all passing. 30 existing routing tests unaffected.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 11:02:41 +00:00

197 lines
5.7 KiB
TypeScript

/**
* Agent Routing Language Boost Tests
*
* Tests for Phase 5 language-aware routing:
* - Language match boost in calculateAgentScores
* - Language field in AgentCapabilityProfile
* - Backward compatibility (no language = no boost)
*
* @see Issue #324 — Multi-language Phase 5
*/
import { describe, it, expect } from 'vitest';
import {
calculateAgentScores,
AGENT_CAPABILITIES,
type RoutingWeights,
type AgentCapabilityProfile,
} from '../../../src/learning/agent-routing.js';
const DEFAULT_WEIGHTS: RoutingWeights = {
similarity: 1.0,
performance: 1.0,
capabilities: 1.0,
language: 1.0,
};
const emptyPatterns = new Map<string, number>();
describe('Language-Aware Agent Routing', () => {
it('should boost agents with matching language', () => {
const scoresWithLang = calculateAgentScores(
['test-generation'],
undefined,
emptyPatterns,
DEFAULT_WEIGHTS,
AGENT_CAPABILITIES,
'java',
);
const scoresWithoutLang = calculateAgentScores(
['test-generation'],
undefined,
emptyPatterns,
DEFAULT_WEIGHTS,
AGENT_CAPABILITIES,
);
// Find the test-generator agent in both results
const withLang = scoresWithLang.find(s => s.agent === 'qe-test-generator');
const withoutLang = scoresWithoutLang.find(s => s.agent === 'qe-test-generator');
expect(withLang).toBeDefined();
expect(withoutLang).toBeDefined();
expect(withLang!.score).toBeGreaterThan(withoutLang!.score);
expect(withLang!.reasoning).toContain('Language match: java');
});
it('should not boost agents without language capability', () => {
const customCapabilities: Record<string, AgentCapabilityProfile> = {
'agent-with-lang': {
domains: ['test-generation'],
capabilities: ['test-generation'],
performanceScore: 0.8,
languages: ['java', 'kotlin'],
},
'agent-without-lang': {
domains: ['test-generation'],
capabilities: ['test-generation'],
performanceScore: 0.8,
// No languages field
},
};
const scores = calculateAgentScores(
['test-generation'],
undefined,
emptyPatterns,
DEFAULT_WEIGHTS,
customCapabilities,
'java',
);
const withLang = scores.find(s => s.agent === 'agent-with-lang')!;
const withoutLang = scores.find(s => s.agent === 'agent-without-lang')!;
expect(withLang.score).toBeGreaterThan(withoutLang.score);
});
it('should not apply boost when language does not match', () => {
const customCapabilities: Record<string, AgentCapabilityProfile> = {
'ts-only-agent': {
domains: ['test-generation'],
capabilities: ['test-generation'],
performanceScore: 0.8,
languages: ['typescript'],
},
};
const scoresWithRust = calculateAgentScores(
['test-generation'],
undefined,
emptyPatterns,
DEFAULT_WEIGHTS,
customCapabilities,
'rust',
);
const scoresNoLang = calculateAgentScores(
['test-generation'],
undefined,
emptyPatterns,
DEFAULT_WEIGHTS,
customCapabilities,
);
const withRust = scoresWithRust.find(s => s.agent === 'ts-only-agent')!;
const noLang = scoresNoLang.find(s => s.agent === 'ts-only-agent')!;
// No boost when language doesn't match
expect(withRust.score).toBe(noLang.score);
});
it('should respect language weight configuration', () => {
const fullWeight: RoutingWeights = { similarity: 1, performance: 1, capabilities: 1, language: 1.0 };
const halfWeight: RoutingWeights = { similarity: 1, performance: 1, capabilities: 1, language: 0.5 };
const scoresFull = calculateAgentScores(
['test-generation'],
undefined,
emptyPatterns,
fullWeight,
AGENT_CAPABILITIES,
'java',
);
const scoresHalf = calculateAgentScores(
['test-generation'],
undefined,
emptyPatterns,
halfWeight,
AGENT_CAPABILITIES,
'java',
);
const full = scoresFull.find(s => s.agent === 'qe-test-generator')!;
const half = scoresHalf.find(s => s.agent === 'qe-test-generator')!;
// Full weight should give higher score than half weight
expect(full.score).toBeGreaterThan(half.score);
});
it('should be case-insensitive for language matching', () => {
const scores = calculateAgentScores(
['test-generation'],
undefined,
emptyPatterns,
DEFAULT_WEIGHTS,
AGENT_CAPABILITIES,
'Java', // uppercase
);
const generator = scores.find(s => s.agent === 'qe-test-generator')!;
expect(generator.reasoning).toContain('Language match: Java');
});
it('should work with backward-compatible RoutingWeights (no language field)', () => {
const oldStyleWeights: RoutingWeights = {
similarity: 1.0,
performance: 1.0,
capabilities: 1.0,
// no language field — should default to 1.0
};
const scores = calculateAgentScores(
['test-generation'],
undefined,
emptyPatterns,
oldStyleWeights,
AGENT_CAPABILITIES,
'python',
);
const generator = scores.find(s => s.agent === 'qe-test-generator')!;
expect(generator.reasoning).toContain('Language match: python');
});
it('should include languages in AGENT_CAPABILITIES for key agents', () => {
// Verify the static capabilities map has language data
expect(AGENT_CAPABILITIES['qe-test-generator'].languages).toBeDefined();
expect(AGENT_CAPABILITIES['qe-test-generator'].languages).toContain('java');
expect(AGENT_CAPABILITIES['qe-test-generator'].languages).toContain('rust');
expect(AGENT_CAPABILITIES['qe-coverage-analyzer'].languages).toContain('go');
expect(AGENT_CAPABILITIES['qe-coverage-analyzer'].languages).toContain('swift');
});
});