@clduab11/gemini-flow
Version:
Revolutionary AI agent swarm coordination platform with Google Services integration, multimedia processing, and production-ready monitoring. Features 8 Google AI services, quantum computing capabilities, and enterprise-grade security.
769 lines (636 loc) • 25.6 kB
text/typescript
/**
* A2A Protocol Compliance Tests
* Comprehensive validation of all Agent-to-Agent communication message types
*/
import {
A2AComplianceTestSuite,
A2ATestDataBuilder,
A2ATestUtils,
MockAgent,
MockA2AMessageBus,
A2AMessage,
A2AResponse,
A2AErrorCode,
DirectCoordination,
BroadcastCoordination,
ConsensusCoordination,
PipelineCoordination,
SingleTarget,
MultipleTargets,
GroupTarget,
BroadcastTarget,
ConditionalTarget
} from './test-harness';
describe('A2A Protocol Compliance Tests', () => {
let testSuite: ProtocolComplianceTestSuite;
beforeEach(async () => {
testSuite = new ProtocolComplianceTestSuite();
await testSuite.setup();
});
afterEach(async () => {
await testSuite.teardown();
});
describe('Message Structure Compliance', () => {
it('should validate required message fields', async () => {
const message = A2ATestDataBuilder.createMessage();
const validation = A2ATestUtils.validateMessageCompliance(message);
expect(validation.valid).toBe(true);
expect(validation.errors).toHaveLength(0);
});
it('should reject messages with missing required fields', async () => {
const invalidMessage = A2ATestDataBuilder.createMessage({
id: undefined as any,
source: undefined as any
});
const validation = A2ATestUtils.validateMessageCompliance(invalidMessage);
expect(validation.valid).toBe(false);
expect(validation.errors).toContain('Missing required field: id');
expect(validation.errors).toContain('Missing required field: source.agentId');
});
it('should validate message expiration (TTL)', async () => {
const expiredMessage = A2ATestDataBuilder.createMessage({
timestamp: Date.now() - 60000, // 1 minute ago
ttl: 30000 // 30 second TTL
});
try {
await testSuite.messageBus.send(expiredMessage);
fail('Should have thrown error for expired message');
} catch (error) {
expect(error.message).toContain('Message expired');
}
});
it('should validate correlation ID chain', async () => {
const originalMessage = A2ATestDataBuilder.createMessage();
const response = await testSuite.messageBus.send(originalMessage);
expect(response.correlationId).toBe(originalMessage.id);
expect(response.messageId).toBe(originalMessage.id);
});
it('should validate conversation ID continuity', async () => {
const conversationId = 'conv-123';
const message1 = A2ATestDataBuilder.createMessage({ conversationId });
const message2 = A2ATestDataBuilder.createMessage({ conversationId });
const response1 = await testSuite.messageBus.send(message1);
const response2 = await testSuite.messageBus.send(message2);
// Both responses should reference the same conversation
expect(response1.correlationId).toContain(conversationId);
expect(response2.correlationId).toContain(conversationId);
});
});
describe('Direct Coordination Compliance', () => {
it('should handle direct 1-to-1 communication', async () => {
const coordination: DirectCoordination = {
mode: 'direct',
timeout: 5000,
retries: 3,
acknowledgment: true
};
const message = A2ATestDataBuilder.createMessage({
target: { type: 'single', agentId: testSuite.testAgents[0].id },
coordination
});
const response = await testSuite.messageBus.send(message);
expect(response.success).toBe(true);
expect(response.source.agentId).toBe(testSuite.testAgents[0].id);
expect(response.metadata.processingTime).toBeGreaterThan(0);
});
it('should enforce timeout limits', async () => {
const coordination: DirectCoordination = {
mode: 'direct',
timeout: 100, // Very short timeout
retries: 0,
acknowledgment: true
};
// Simulate slow agent
testSuite.testAgents[0].simulateFailure('timeout', 200);
const message = A2ATestDataBuilder.createMessage({
target: { type: 'single', agentId: testSuite.testAgents[0].id },
coordination
});
const startTime = Date.now();
const response = await testSuite.messageBus.send(message);
const duration = Date.now() - startTime;
expect(duration).toBeLessThan(150); // Should timeout quickly
expect(response.success).toBe(false);
expect(response.error?.code).toBe(A2AErrorCode.TIMEOUT);
});
it('should handle retry logic with exponential backoff', async () => {
const message = A2ATestDataBuilder.createMessage({
target: { type: 'single', agentId: testSuite.testAgents[0].id },
retryPolicy: {
maxRetries: 3,
backoffStrategy: 'exponential',
baseDelay: 100,
maxDelay: 1000,
retryableErrors: [A2AErrorCode.TIMEOUT]
}
});
// Simulate temporary failure
testSuite.testAgents[0].simulateFailure('timeout', 300);
const startTime = Date.now();
const response = await testSuite.messageBus.send(message);
const duration = Date.now() - startTime;
// Should eventually succeed after retries
expect(response.success).toBe(true);
expect(duration).toBeGreaterThan(300); // Should wait for failure to clear
});
it('should validate acknowledgment requirements', async () => {
const coordination: DirectCoordination = {
mode: 'direct',
timeout: 5000,
retries: 1,
acknowledgment: true
};
const message = A2ATestDataBuilder.createMessage({
target: { type: 'single', agentId: testSuite.testAgents[0].id },
coordination
});
const response = await testSuite.messageBus.send(message);
expect(response.success).toBe(true);
expect(response.correlationId).toBe(message.id);
expect(response.metadata).toBeDefined();
});
});
describe('Broadcast Coordination Compliance', () => {
it('should handle 1-to-many broadcast', async () => {
const coordination: BroadcastCoordination = {
mode: 'broadcast',
aggregation: 'all',
timeout: 10000,
partialSuccess: false
};
const message = A2ATestDataBuilder.createMessage({
target: { type: 'broadcast' },
coordination
});
const responses = await testSuite.messageBus.route(message);
expect(responses.length).toBe(testSuite.testAgents.length);
expect(responses.every(r => r.success)).toBe(true);
});
it('should handle partial success scenarios', async () => {
const coordination: BroadcastCoordination = {
mode: 'broadcast',
aggregation: 'majority',
timeout: 5000,
partialSuccess: true
};
// Simulate one agent failure
testSuite.testAgents[0].simulateFailure('tool', 10000);
const message = A2ATestDataBuilder.createMessage({
target: { type: 'broadcast' },
coordination
});
const responses = await testSuite.messageBus.route(message);
expect(responses.length).toBe(testSuite.testAgents.length);
const successCount = responses.filter(r => r.success).length;
const failureCount = responses.filter(r => !r.success).length;
expect(successCount).toBeGreaterThan(failureCount);
expect(successCount).toBeGreaterThanOrEqual(Math.ceil(testSuite.testAgents.length / 2));
});
it('should validate aggregation strategies', async () => {
const testStrategies: Array<BroadcastCoordination['aggregation']> = [
'all', 'majority', 'first', 'any'
];
for (const aggregation of testStrategies) {
const coordination: BroadcastCoordination = {
mode: 'broadcast',
aggregation,
timeout: 5000,
partialSuccess: aggregation !== 'all'
};
const message = A2ATestDataBuilder.createMessage({
target: { type: 'broadcast' },
coordination
});
const responses = await testSuite.messageBus.route(message);
switch (aggregation) {
case 'all':
expect(responses.every(r => r.success)).toBe(true);
break;
case 'majority':
expect(responses.filter(r => r.success).length)
.toBeGreaterThanOrEqual(Math.ceil(responses.length / 2));
break;
case 'first':
case 'any':
expect(responses.some(r => r.success)).toBe(true);
break;
}
}
});
it('should handle broadcast filtering', async () => {
const coordinatorAgent = A2ATestDataBuilder.createAgent(
'coordinator-special',
'coordinator',
['coordination', 'management']
);
testSuite.messageBus.registerAgent(coordinatorAgent);
const message = A2ATestDataBuilder.createMessage({
target: {
type: 'broadcast',
filter: { role: 'coordinator' },
excludeSource: true
}
});
const responses = await testSuite.messageBus.route(message);
// Should only target coordinator agents
expect(responses.length).toBe(1);
expect(responses[0].source.agentId).toBe('coordinator-special');
});
});
describe('Consensus Coordination Compliance', () => {
it('should handle unanimous consensus', async () => {
const coordination: ConsensusCoordination = {
mode: 'consensus',
consensusType: 'unanimous',
votingTimeout: 10000,
minimumParticipants: 3
};
const message = A2ATestDataBuilder.createMessage({
target: { type: 'group', role: 'worker' },
coordination,
toolName: 'mcp__ruv-swarm__daa_consensus'
});
// Add consensus tool to all agents
testSuite.testAgents.forEach(agent => {
agent.addTool('mcp__ruv-swarm__daa_consensus', async (params) => ({
vote: 'yes',
consensus: true,
params
}));
});
const responses = await testSuite.messageBus.route(message);
expect(responses.length).toBeGreaterThanOrEqual(3);
expect(responses.every(r => r.success)).toBe(true);
expect(responses.every(r => r.result?.vote === 'yes')).toBe(true);
});
it('should handle majority consensus', async () => {
const coordination: ConsensusCoordination = {
mode: 'consensus',
consensusType: 'majority',
votingTimeout: 5000,
minimumParticipants: 3
};
// Configure agents for mixed voting
testSuite.testAgents.forEach((agent, i) => {
agent.addTool('mcp__ruv-swarm__daa_consensus', async (params) => ({
vote: i % 2 === 0 ? 'yes' : 'no',
consensus: i % 2 === 0,
params
}));
});
const message = A2ATestDataBuilder.createMessage({
target: { type: 'group', role: 'worker' },
coordination,
toolName: 'mcp__ruv-swarm__daa_consensus'
});
const responses = await testSuite.messageBus.route(message);
const yesVotes = responses.filter(r => r.success && r.result?.vote === 'yes').length;
const totalVotes = responses.length;
expect(yesVotes).toBeGreaterThan(totalVotes / 2);
});
it('should handle consensus timeout', async () => {
const coordination: ConsensusCoordination = {
mode: 'consensus',
consensusType: 'unanimous',
votingTimeout: 1000, // Short timeout
minimumParticipants: 3
};
// Simulate slow voting
testSuite.testAgents.forEach(agent => {
agent.addTool('mcp__ruv-swarm__daa_consensus', async (params) => {
await new Promise(resolve => setTimeout(resolve, 1500)); // Longer than timeout
return { vote: 'yes', consensus: true, params };
});
});
const message = A2ATestDataBuilder.createMessage({
target: { type: 'group', role: 'worker' },
coordination,
toolName: 'mcp__ruv-swarm__daa_consensus'
});
try {
await testSuite.messageBus.route(message);
fail('Should have thrown timeout error');
} catch (error) {
expect(error.message).toContain('Consensus not reached');
}
});
it('should validate minimum participants requirement', async () => {
const coordination: ConsensusCoordination = {
mode: 'consensus',
consensusType: 'majority',
votingTimeout: 5000,
minimumParticipants: 10 // More than available agents
};
const message = A2ATestDataBuilder.createMessage({
target: { type: 'group', role: 'worker' },
coordination
});
try {
await testSuite.messageBus.route(message);
fail('Should have failed due to insufficient participants');
} catch (error) {
expect(error.message).toContain('participants');
}
});
});
describe('Pipeline Coordination Compliance', () => {
it('should handle sequential pipeline execution', async () => {
const coordination: PipelineCoordination = {
mode: 'pipeline',
stages: [
{
agentTarget: { type: 'single', agentId: testSuite.testAgents[0].id },
toolName: 'mcp__claude-flow__sparc_mode',
inputTransform: (input) => ({ ...input, stage: 1 }),
outputTransform: (output) => ({ ...output, processed: true })
},
{
agentTarget: { type: 'single', agentId: testSuite.testAgents[1].id },
toolName: 'mcp__claude-flow__sparc_mode',
inputTransform: (input) => ({ ...input, stage: 2 })
}
],
failureStrategy: 'abort',
statePassthrough: true
};
// Add pipeline tools
testSuite.testAgents.forEach((agent, i) => {
agent.addTool('mcp__claude-flow__sparc_mode', async (params) => ({
stage: params.stage,
agentId: agent.id,
processed: params.processed || false,
result: `stage-${params.stage}-complete`
}));
});
const message = A2ATestDataBuilder.createMessage({
coordination,
parameters: { data: 'pipeline-test' }
});
const responses = await testSuite.messageBus.route(message);
expect(responses).toHaveLength(2);
expect(responses[0].result.stage).toBe(1);
expect(responses[0].result.processed).toBe(true);
expect(responses[1].result.stage).toBe(2);
});
it('should handle pipeline failure strategies', async () => {
const strategies: Array<PipelineCoordination['failureStrategy']> = [
'abort', 'skip', 'retry'
];
for (const strategy of strategies) {
const coordination: PipelineCoordination = {
mode: 'pipeline',
stages: [
{
agentTarget: { type: 'single', agentId: testSuite.testAgents[0].id },
toolName: 'mcp__claude-flow__sparc_mode'
},
{
agentTarget: { type: 'single', agentId: testSuite.testAgents[1].id },
toolName: 'mcp__claude-flow__sparc_mode'
}
],
failureStrategy: strategy,
statePassthrough: true
};
// Configure first agent to fail
testSuite.testAgents[0].removeTool('mcp__claude-flow__sparc_mode');
testSuite.testAgents[1].addTool('mcp__claude-flow__sparc_mode', async () => ({
success: true
}));
const message = A2ATestDataBuilder.createMessage({ coordination });
const responses = await testSuite.messageBus.route(message);
switch (strategy) {
case 'abort':
expect(responses).toHaveLength(1);
expect(responses[0].success).toBe(false);
break;
case 'skip':
case 'retry':
// Implementation would continue to next stage
expect(responses.length).toBeGreaterThanOrEqual(1);
break;
}
// Reset agent tools
testSuite.testAgents[0].addTool('mcp__claude-flow__sparc_mode');
}
});
it('should validate state passthrough', async () => {
const coordination: PipelineCoordination = {
mode: 'pipeline',
stages: [
{
agentTarget: { type: 'single', agentId: testSuite.testAgents[0].id },
toolName: 'mcp__claude-flow__memory_usage',
outputTransform: (output) => ({ ...output, stateData: 'from-stage-1' })
},
{
agentTarget: { type: 'single', agentId: testSuite.testAgents[1].id },
toolName: 'mcp__claude-flow__memory_usage'
}
],
failureStrategy: 'abort',
statePassthrough: true
};
testSuite.testAgents.forEach(agent => {
agent.addTool('mcp__claude-flow__memory_usage', async (params) => ({
receivedState: params.stateData || null,
agentId: agent.id
}));
});
const message = A2ATestDataBuilder.createMessage({ coordination });
const responses = await testSuite.messageBus.route(message);
expect(responses).toHaveLength(2);
expect(responses[1].result.receivedState).toBe('from-stage-1');
});
});
describe('Target Resolution Compliance', () => {
it('should resolve single target correctly', async () => {
const target: SingleTarget = {
type: 'single',
agentId: testSuite.testAgents[0].id
};
const message = A2ATestDataBuilder.createMessage({ target });
const response = await testSuite.messageBus.send(message);
expect(response.source.agentId).toBe(testSuite.testAgents[0].id);
});
it('should resolve multiple targets correctly', async () => {
const target: MultipleTargets = {
type: 'multiple',
agentIds: [testSuite.testAgents[0].id, testSuite.testAgents[1].id],
coordinationMode: 'parallel'
};
const message = A2ATestDataBuilder.createMessage({
target,
coordination: { mode: 'broadcast', aggregation: 'all', timeout: 5000, partialSuccess: false }
});
const responses = await testSuite.messageBus.route(message);
expect(responses).toHaveLength(2);
expect(responses.map(r => r.source.agentId)).toContain(testSuite.testAgents[0].id);
expect(responses.map(r => r.source.agentId)).toContain(testSuite.testAgents[1].id);
});
it('should resolve group target by role', async () => {
// Add specialized agents
const coordinatorAgent = A2ATestDataBuilder.createAgent('coord-1', 'coordinator');
testSuite.messageBus.registerAgent(coordinatorAgent);
const target: GroupTarget = {
type: 'group',
role: 'coordinator',
maxAgents: 1,
selectionStrategy: 'random'
};
const message = A2ATestDataBuilder.createMessage({
target,
coordination: { mode: 'broadcast', aggregation: 'all', timeout: 5000, partialSuccess: false }
});
const responses = await testSuite.messageBus.route(message);
expect(responses).toHaveLength(1);
expect(responses[0].source.agentId).toBe('coord-1');
});
it('should resolve broadcast target with filters', async () => {
const target: BroadcastTarget = {
type: 'broadcast',
filter: { role: 'tester' }, // All test agents have 'tester' role
excludeSource: false
};
const message = A2ATestDataBuilder.createMessage({
target,
coordination: { mode: 'broadcast', aggregation: 'all', timeout: 5000, partialSuccess: false }
});
const responses = await testSuite.messageBus.route(message);
expect(responses.length).toBeGreaterThan(0);
expect(responses.every(r => r.source.agentId.includes('test-agent'))).toBe(true);
});
it('should resolve conditional target with fallback', async () => {
const target: ConditionalTarget = {
type: 'conditional',
conditions: [{ capability: 'nonexistent' }], // This will fail
fallback: { type: 'single', agentId: testSuite.testAgents[0].id }
};
const message = A2ATestDataBuilder.createMessage({ target });
const response = await testSuite.messageBus.send(message);
// Should fall back to first agent
expect(response.source.agentId).toBe(testSuite.testAgents[0].id);
});
});
describe('Error Handling Compliance', () => {
it('should handle agent not found errors', async () => {
const message = A2ATestDataBuilder.createMessage({
target: { type: 'single', agentId: 'nonexistent-agent' }
});
try {
await testSuite.messageBus.send(message);
fail('Should have thrown agent not found error');
} catch (error) {
expect(error.message).toContain('not found');
}
});
it('should handle tool not supported errors', async () => {
const message = A2ATestDataBuilder.createMessage({
target: { type: 'single', agentId: testSuite.testAgents[0].id },
toolName: 'nonexistent-tool'
});
const response = await testSuite.messageBus.send(message);
expect(response.success).toBe(false);
expect(response.error?.code).toBe(A2AErrorCode.TOOL_NOT_SUPPORTED);
});
it('should handle resource exhaustion errors', async () => {
const message = A2ATestDataBuilder.createMessage({
target: { type: 'single', agentId: testSuite.testAgents[0].id },
resourceRequirements: [
{ type: 'cpu', amount: 1000, unit: 'cores', priority: 'high', duration: 5000 }
]
});
const response = await testSuite.messageBus.send(message);
expect(response.success).toBe(false);
expect(response.error?.code).toBe(A2AErrorCode.INSUFFICIENT_RESOURCES);
});
it('should handle timeout errors with proper error codes', async () => {
testSuite.testAgents[0].simulateFailure('timeout', 10000);
const message = A2ATestDataBuilder.createMessage({
target: { type: 'single', agentId: testSuite.testAgents[0].id },
execution: { timeout: 1000, priority: 'high', retries: 0, isolation: false }
});
const response = await testSuite.messageBus.send(message);
expect(response.success).toBe(false);
expect(response.error?.code).toBe(A2AErrorCode.TIMEOUT);
expect(response.error?.recoverable).toBe(true);
});
});
describe('State Synchronization Compliance', () => {
it('should handle state requirements correctly', async () => {
const message = A2ATestDataBuilder.createMessage({
target: { type: 'single', agentId: testSuite.testAgents[0].id },
stateRequirements: [
{
type: 'write',
namespace: 'test-state',
keys: ['key1', 'key2'],
consistency: 'strong',
timeout: 5000
}
]
});
const response = await testSuite.messageBus.send(message);
expect(response.success).toBe(true);
// Agent should have handled state requirements
});
it('should detect state conflicts', async () => {
testSuite.testAgents[0].simulateFailure('state', 5000);
const message = A2ATestDataBuilder.createMessage({
target: { type: 'single', agentId: testSuite.testAgents[0].id },
stateRequirements: [
{
type: 'exclusive',
namespace: 'conflicted-state',
keys: ['conflict'],
consistency: 'strong',
timeout: 1000
}
]
});
const response = await testSuite.messageBus.send(message);
expect(response.success).toBe(false);
expect(response.error?.code).toBe(A2AErrorCode.STATE_CONFLICT);
});
});
describe('Performance and Metrics Compliance', () => {
it('should track message processing metrics', async () => {
const message = A2ATestDataBuilder.createMessage();
await testSuite.messageBus.send(message);
const metrics = testSuite.messageBus.getMetrics();
expect(metrics.totalMessages).toBe(1);
expect(metrics.successfulMessages).toBe(1);
expect(metrics.averageLatency).toBeGreaterThan(0);
});
it('should provide response metadata', async () => {
const message = A2ATestDataBuilder.createMessage();
const response = await testSuite.messageBus.send(message);
expect(response.metadata).toBeDefined();
expect(response.metadata.processingTime).toBeGreaterThan(0);
expect(response.metadata.resourceUsage).toBeDefined();
expect(response.metadata.hops).toBeGreaterThanOrEqual(0);
});
it('should handle high-frequency message processing', async () => {
const messageCount = 100;
const startTime = Date.now();
const responses = await A2ATestUtils.generateLoad(
testSuite.messageBus,
messageCount,
10
);
const duration = Date.now() - startTime;
const throughput = messageCount / (duration / 1000);
expect(responses).toHaveLength(messageCount);
expect(throughput).toBeGreaterThan(10); // Messages per second
});
});
});
/**
* Protocol Compliance Test Suite Implementation
*/
class ProtocolComplianceTestSuite extends A2AComplianceTestSuite {
async runTests(): Promise<void> {
// This method can be used for programmatic test execution
console.log('Running A2A Protocol Compliance Tests...');
}
}