using System.Text.Json; using AxCopilot.Services.Agent; using FluentAssertions; using Xunit; namespace AxCopilot.Tests.Services; public class SpawnAgentsToolTests { // ═══════════════════════════════════════════ // 도구 메타데이터 // ═══════════════════════════════════════════ [Fact] public void Name_IsSpawnAgents() { var tool = new SpawnAgentsTool(); tool.Name.Should().Be("spawn_agents"); } [Fact] public void Description_IsNonEmpty() { var tool = new SpawnAgentsTool(); tool.Description.Should().NotBeNullOrWhiteSpace(); } [Fact] public void Parameters_HasAgentsArray() { var tool = new SpawnAgentsTool(); tool.Parameters.Properties.Should().ContainKey("agents"); tool.Parameters.Required.Should().Contain("agents"); } // ═══════════════════════════════════════════ // 입력 검증 // ═══════════════════════════════════════════ [Fact] public async Task ExecuteAsync_MissingAgents_ReturnsFail() { var tool = new SpawnAgentsTool(); var json = JsonDocument.Parse("{}").RootElement; var result = await tool.ExecuteAsync(json, CreateMinimalContext()); result.Success.Should().BeFalse(); result.Output.Should().Contain("agents"); } [Fact] public async Task ExecuteAsync_EmptyAgentsArray_ReturnsFail() { var tool = new SpawnAgentsTool(); var json = JsonDocument.Parse("""{"agents": []}""").RootElement; var result = await tool.ExecuteAsync(json, CreateMinimalContext()); result.Success.Should().BeFalse(); result.Output.Should().Contain("empty"); } [Fact] public async Task ExecuteAsync_MissingId_ReturnsFail() { var tool = new SpawnAgentsTool(); var json = JsonDocument.Parse("""{"agents": [{"task": "do something"}]}""").RootElement; var result = await tool.ExecuteAsync(json, CreateMinimalContext()); result.Success.Should().BeFalse(); } [Fact] public async Task ExecuteAsync_MissingTask_ReturnsFail() { var tool = new SpawnAgentsTool(); var json = JsonDocument.Parse("""{"agents": [{"id": "a1"}]}""").RootElement; var result = await tool.ExecuteAsync(json, CreateMinimalContext()); result.Success.Should().BeFalse(); } [Fact] public async Task ExecuteAsync_DuplicateIds_ReturnsFail() { var tool = new SpawnAgentsTool(); var json = JsonDocument.Parse(""" { "agents": [ {"id": "a1", "task": "task1"}, {"id": "a1", "task": "task2"} ] } """).RootElement; var result = await tool.ExecuteAsync(json, CreateMinimalContext()); result.Success.Should().BeFalse(); result.Output.Should().Contain("Duplicate"); } [Fact] public async Task ExecuteAsync_AgentsNotArray_ReturnsFail() { var tool = new SpawnAgentsTool(); var json = JsonDocument.Parse("""{"agents": "not an array"}""").RootElement; var result = await tool.ExecuteAsync(json, CreateMinimalContext()); result.Success.Should().BeFalse(); } // ═══════════════════════════════════════════ // 서브에이전트 재귀 차단 검증 // ═══════════════════════════════════════════ [Fact] public void AllSubAgentProfiles_DisableSpawnAgents() { // spawn_agents는 모든 서브에이전트 프로파일에서 비활성화되어야 함 (재귀 방지) foreach (var name in SubAgentProfileCatalog.AllProfileNames) { var profile = SubAgentProfileCatalog.Get(name); profile.DisabledToolNames.Should().Contain("spawn_agents", $"profile '{name}' should disable spawn_agents to prevent recursion"); } } // ═══════════════════════════════════════════ // 유틸 // ═══════════════════════════════════════════ private static AgentContext CreateMinimalContext() { return new AgentContext { WorkFolder = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), }; } }