using System.Text.Json; using Tau.Acuvim.Console.Models; using Tau.Acuvim.Console.Services; namespace Tau.Acuvim.Console.Tests; public class CommandServiceTests { private CommandService CreateService(Data.AppDbContext db) => new(db, TestHelpers.CreateLogger()); [Fact] public async Task CreateAsync_CreatesCommand_WithPendingStatus() { using var db = TestHelpers.CreateDbContext(); var svc = CreateService(db); var cmd = await svc.CreateAsync("DEV-C1", "reboot", null, "admin"); Assert.Equal("DEV-C1", cmd.DeviceId); Assert.Equal("reboot", cmd.CommandName); Assert.Equal("pending", cmd.Status); Assert.Equal("admin", cmd.CreatedBy); Assert.StartsWith("cmd-", cmd.RequestId); Assert.Single(db.Commands); } [Fact] public async Task MarkSentAsync_UpdatesStatus() { using var db = TestHelpers.CreateDbContext(); var svc = CreateService(db); var cmd = await svc.CreateAsync("DEV-C2", "reset", null, "admin"); await svc.MarkSentAsync(cmd.RequestId); var updated = db.Commands.First(c => c.RequestId == cmd.RequestId); Assert.Equal("sent", updated.Status); Assert.NotNull(updated.SentAt); } [Fact] public async Task ProcessResponseAsync_CompletesCommand() { using var db = TestHelpers.CreateDbContext(); var svc = CreateService(db); var cmd = await svc.CreateAsync("DEV-C3", "ping", null, "admin"); await svc.MarkSentAsync(cmd.RequestId); var responsePayload = JsonSerializer.Serialize(new { request_id = cmd.RequestId, status = "success", data = "pong" }); await svc.ProcessResponseAsync("DEV-C3", responsePayload); var updated = db.Commands.First(c => c.RequestId == cmd.RequestId); Assert.Equal("success", updated.Status); Assert.NotNull(updated.Response); Assert.NotNull(updated.CompletedAt); } [Fact] public async Task ProcessResponseAsync_IgnoresUnknownRequestId() { using var db = TestHelpers.CreateDbContext(); var svc = CreateService(db); var responsePayload = JsonSerializer.Serialize(new { request_id = "cmd-unknown", status = "success" }); // Should not throw await svc.ProcessResponseAsync("DEV-C4", responsePayload); Assert.Empty(db.Commands); } [Fact] public async Task BuildCommandPayload_IncludesCommandAndRequestId() { using var db = TestHelpers.CreateDbContext(); var svc = CreateService(db); var cmd = await svc.CreateAsync("DEV-C5", "firmware_update", null, "admin"); var json = svc.BuildCommandPayload(cmd); using var doc = JsonDocument.Parse(json); var root = doc.RootElement; Assert.Equal("firmware_update", root.GetProperty("cmd").GetString()); Assert.Equal(cmd.RequestId, root.GetProperty("request_id").GetString()); } [Fact] public async Task TimeoutStalCommandsAsync_TimesOutOldCommands() { using var db = TestHelpers.CreateDbContext(); var svc = CreateService(db); // Create a command and mark it as sent with an old SentAt time var cmd = new Command { Id = Guid.NewGuid(), DeviceId = "DEV-C6", RequestId = $"cmd-{Guid.NewGuid():N}", CommandName = "check", Status = "sent", SentAt = DateTime.UtcNow.AddSeconds(-120) }; db.Commands.Add(cmd); // Also add a recent sent command that should NOT be timed out var recentCmd = new Command { Id = Guid.NewGuid(), DeviceId = "DEV-C6", RequestId = $"cmd-{Guid.NewGuid():N}", CommandName = "check2", Status = "sent", SentAt = DateTime.UtcNow }; db.Commands.Add(recentCmd); await db.SaveChangesAsync(); var count = await svc.TimeoutStalCommandsAsync(timeoutSeconds: 60); Assert.Equal(1, count); var timedOut = db.Commands.First(c => c.RequestId == cmd.RequestId); Assert.Equal("timeout", timedOut.Status); Assert.NotNull(timedOut.CompletedAt); var stillSent = db.Commands.First(c => c.RequestId == recentCmd.RequestId); Assert.Equal("sent", stillSent.Status); } }