using Nexus.Api.Data; using Nexus.Api.Models; using Nexus.Api.Services; using Xunit; namespace Nexus.Api.Tests; public sealed class AgentServiceTests { [Fact] public async Task GetAgentsAsync_ProjectsOnlyLiveOpenClawInventory() { var service = new AgentService(new StubOpenClawControlService()); var agents = await service.GetAgentsAsync(CancellationToken.None); Assert.Equal(6, agents.Count); Assert.Contains(agents, agent => agent.Id == "product-owner" && agent.Workspace == "/workspace-po" && agent.Role == "Product Owner"); } [Fact] public async Task GetAgentAsync_Iris_ReturnsOrchestrator() { var service = new AgentService(new StubOpenClawControlService()); var agent = await service.GetAgentAsync("iris", CancellationToken.None); Assert.NotNull(agent); Assert.Equal("Orchestrator", agent.Role); Assert.Equal("/workspace/iris", agent.Workspace); Assert.Null(agent.AgentDir); } [Fact] public async Task GetAgentAsync_Unknown_ReturnsNull() { var service = new AgentService(new StubOpenClawControlService()); var agent = await service.GetAgentAsync("nonexistent", CancellationToken.None); Assert.Null(agent); } [Fact] public async Task GetAllowedAgentIdsAsync_UsesLiveIds() { var control = new StubOpenClawControlService( agents: [ Agent("custom-live", "Custom"), Agent("product-owner", "Product Owner") ]); var service = new AgentService(control); var ids = await service.GetAllowedAgentIdsAsync(CancellationToken.None); Assert.Equal(2, ids.Count); Assert.Contains("custom-live", ids); Assert.Contains("product-owner", ids); Assert.DoesNotContain("iris", ids); } [Fact] public async Task GetAgentAsync_LatestSessionModelOverridesInventoryModel() { var older = DateTimeOffset.UtcNow.AddMinutes(-10); var newer = DateTimeOffset.UtcNow; var control = new StubOpenClawControlService( agents: [Agent("programmer-fast", "Programmer Fast", "openai/default")], sessions: [ Session("old", "programmer-fast", "openai/gpt-5.3", older), Session("new", "programmer-fast", "openai/gpt-5.5", newer) ]); var service = new AgentService(control); var agent = await service.GetAgentAsync("programmer-fast", CancellationToken.None); Assert.NotNull(agent); Assert.Equal("Developer", agent.Role); Assert.Equal("openai/gpt-5.5", agent.Model); Assert.Equal(newer, agent.LastSeen); } [Fact] public async Task GetAgentsAsync_DisconnectedGatewayMarksAgentsOffline() { var service = new AgentService(new StubOpenClawControlService(connected: false)); var agents = await service.GetAgentsAsync(CancellationToken.None); Assert.All(agents, agent => Assert.Equal(OperationalStatus.Offline, agent.Status)); } private static OpenClawAgentDto Agent( string id, string name, string model = "openai/gpt-5.5") => new( Id: id, Name: name, Description: null, Model: model, Provider: "openai", Workspace: $"/live/{id}", Status: "ready"); private static OpenClawSessionDto Session( string key, string agentId, string model, DateTimeOffset updatedAt) => new( Key: key, SessionId: key, AgentId: agentId, Title: key, Status: "active", Kind: "agent", Channel: null, Model: model, Provider: "openai", RunId: null, UpdatedAt: updatedAt, InputTokens: null, OutputTokens: null, TotalTokens: null, CanAbort: true); }