Refine ship orders and viewer controls

This commit is contained in:
2026-04-09 12:42:52 -04:00
parent 6c92ab50c8
commit 8503855a4c
64 changed files with 2939 additions and 2037 deletions

View File

@@ -0,0 +1,140 @@
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<InvalidOperationException>()
.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),
};
}
}