using FluentAssertions; using SpaceGame.Api.Shared.Runtime; using SpaceGame.Api.Ships.Runtime; using Xunit; namespace SpaceGame.Api.Tests; public sealed class ShipOrderQueueTests { [Fact] public void Enqueue_Throws_WhenQueueIsFull() { var queue = new ShipOrderQueue(); foreach (var index in Enumerable.Range(0, ShipOrderQueue.MaxOrders)) { queue.Enqueue(CreateOrder($"order-{index}", ShipOrderSourceKind.Player, priority: index)); } var act = () => queue.Enqueue(CreateOrder("overflow", ShipOrderSourceKind.Player)); act.Should().Throw() .WithMessage("Order queue is full."); } [Fact] public void GetCurrentOrder_UsesQueueOrder() { var queue = new ShipOrderQueue(); queue.EnqueuePlayerOrder(CreateOrder("player-first", ShipOrderSourceKind.Player)); queue.EnqueueManagedOrder(CreateOrder("commander-second", ShipOrderSourceKind.Commander, priority: 100)); queue.EnqueueManagedOrder(CreateOrder("behavior-third", ShipOrderSourceKind.Behavior, priority: 999)); var currentOrder = queue.GetCurrentOrder(); currentOrder.Should().NotBeNull(); currentOrder!.Id.Should().Be("player-first"); } [Fact] public void GetCurrentOrder_IgnoresTerminalStatuses() { var queue = new ShipOrderQueue(); queue.EnqueuePlayerOrder(CreateOrder("completed-player", ShipOrderSourceKind.Player, priority: 100, status: OrderStatus.Completed)); queue.EnqueueManagedOrder(CreateOrder("active-commander", ShipOrderSourceKind.Commander, priority: 1, status: OrderStatus.Active)); var currentOrder = queue.GetCurrentOrder(); currentOrder.Should().NotBeNull(); currentOrder!.Id.Should().Be("active-commander"); } [Fact] public void EnqueuePlayerOrder_InsertsBeforeManagedOrders() { var queue = new ShipOrderQueue(); queue.EnqueueManagedOrder(CreateOrder("behavior-1", ShipOrderSourceKind.Behavior)); queue.EnqueuePlayerOrder(CreateOrder("player-1", ShipOrderSourceKind.Player)); queue.EnqueueManagedOrder(CreateOrder("behavior-2", ShipOrderSourceKind.Commander)); queue.EnqueuePlayerOrder(CreateOrder("player-2", ShipOrderSourceKind.Player)); queue.Select(order => order.Id).Should().Equal("player-1", "player-2", "behavior-1", "behavior-2"); queue.FindLeadingOrderForSource(ShipOrderSourceKind.Player)!.Id.Should().Be("player-1"); } [Fact] public void AddOrReplaceManagedOrder_ReplacesMatchingOrderWithoutGrowingQueue() { var queue = new ShipOrderQueue(); queue.EnqueueManagedOrder(CreateOrder("order-1", ShipOrderSourceKind.Behavior, label: "Initial label")); queue.AddOrReplaceManagedOrder(CreateOrder("order-1", ShipOrderSourceKind.Behavior, label: "Updated label", priority: 7)); queue.Count.Should().Be(1); queue.FindById("order-1").Should().NotBeNull(); queue.FindById("order-1")!.Label.Should().Be("Updated label"); queue.FindById("order-1")!.Priority.Should().Be(7); } [Fact] public void RemoveById_RemovesMatchingOrder() { var queue = new ShipOrderQueue(); queue.EnqueuePlayerOrder(CreateOrder("remove-me", ShipOrderSourceKind.Player)); queue.EnqueuePlayerOrder(CreateOrder("keep-me", ShipOrderSourceKind.Player)); var removed = queue.RemoveById("remove-me"); removed.Should().BeTrue(); queue.Select(order => order.Id).Should().ContainSingle().Which.Should().Be("keep-me"); } [Fact] public void TryMovePlayerOrder_ReordersWithinPlayerSegment() { var queue = new ShipOrderQueue(); queue.EnqueuePlayerOrder(CreateOrder("player-1", ShipOrderSourceKind.Player)); queue.EnqueuePlayerOrder(CreateOrder("player-2", ShipOrderSourceKind.Player)); queue.EnqueueManagedOrder(CreateOrder("behavior-1", ShipOrderSourceKind.Behavior)); var moved = queue.TryMovePlayerOrder("player-2", 0); moved.Should().BeTrue(); queue.Select(order => order.Id).Should().Equal("player-2", "player-1", "behavior-1"); } [Fact] public void TryCompleteOrder_RemovesCompletedDirectOrderFromQueue() { var queue = new ShipOrderQueue(); queue.EnqueuePlayerOrder(CreateOrder("direct-order", ShipOrderSourceKind.Player, status: OrderStatus.Active)); queue.EnqueueManagedOrder(CreateOrder("behavior-order", ShipOrderSourceKind.Behavior)); var completed = queue.TryCompleteOrder("direct-order"); completed.Should().BeTrue(); queue.FindById("direct-order").Should().BeNull(); queue.GetCurrentOrder()!.Id.Should().Be("behavior-order"); } private static ShipOrderRuntime CreateOrder( string id, ShipOrderSourceKind sourceKind, int priority = 0, OrderStatus status = OrderStatus.Queued, string? label = null, DateTimeOffset? createdAtUtc = null) { return new ShipOrderRuntime { Id = id, Kind = "test-order", SourceKind = sourceKind, SourceId = $"{sourceKind}-source", Priority = priority, Status = status, Label = label, CreatedAtUtc = createdAtUtc ?? new DateTimeOffset(2026, 4, 8, 12, 0, 0, TimeSpan.Zero), }; } }