Files
AdamuSw/tests/MUnique.OpenMU.Tests/CastleSiege/CastleSiegeContextTest.cs

127 lines
5.2 KiB
C#

// <copyright file="CastleSiegeContextTest.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.Tests.CastleSiege;
using MUnique.OpenMU.GameLogic.CastleSiege;
/// <summary>
/// Tests for the Castle Siege phase state machine (time-driven, injected clock).
/// </summary>
[TestFixture]
public class CastleSiegeContextTest
{
private static readonly DateTime T0 = new(2026, 1, 1, 12, 0, 0, DateTimeKind.Utc);
/// <summary>Tests that a fresh context starts in the ownership (resting) phase.</summary>
[Test]
public void StartsInOwnership()
{
var ctx = new CastleSiegeContext(Config());
Assert.That(ctx.Phase, Is.EqualTo(CastleSiegePhase.Ownership));
}
/// <summary>Tests that force-starting moves the state machine into registration.</summary>
[Test]
public async Task ForceStartMovesToRegistrationAsync()
{
var ctx = new CastleSiegeContext(Config());
await ctx.ForceStartRegistrationAsync(T0);
Assert.That(ctx.Phase, Is.EqualTo(CastleSiegePhase.Registration));
}
/// <summary>Tests that registration advances to preparation once its duration elapses.</summary>
[Test]
public async Task RegistrationAdvancesToPreparationAfterDurationAsync()
{
var ctx = new CastleSiegeContext(Config());
await ctx.ForceStartRegistrationAsync(T0);
await ctx.TickAsync(T0.AddMinutes(4)); // still within registration
Assert.That(ctx.Phase, Is.EqualTo(CastleSiegePhase.Registration));
await ctx.TickAsync(T0.AddMinutes(5)); // registration duration elapsed
Assert.That(ctx.Phase, Is.EqualTo(CastleSiegePhase.Preparation));
}
/// <summary>Tests a full cycle: registration -> preparation -> siege -> settlement -> ownership.</summary>
[Test]
public async Task FullCycleReturnsToOwnershipAsync()
{
var ctx = new CastleSiegeContext(Config());
await ctx.ForceStartRegistrationAsync(T0);
await ctx.TickAsync(T0.AddMinutes(5)); // -> Preparation
await ctx.TickAsync(T0.AddMinutes(7)); // +2 prep -> Siege
Assert.That(ctx.Phase, Is.EqualTo(CastleSiegePhase.Siege));
await ctx.TickAsync(T0.AddMinutes(17)); // +10 siege -> Settlement
await ctx.TickAsync(T0.AddMinutes(17)); // Settlement -> Ownership
Assert.That(ctx.Phase, Is.EqualTo(CastleSiegePhase.Ownership));
}
/// <summary>Tests that guilds can be registered (by name) during the registration phase.</summary>
[Test]
public async Task RegisterGuildCollectsNamesDuringRegistrationAsync()
{
var ctx = new CastleSiegeContext(Config());
await ctx.ForceStartRegistrationAsync(T0);
ctx.RegisterGuild("Attackers");
Assert.That(ctx.RegisteredGuilds, Does.Contain("Attackers"));
}
/// <summary>Tests the full siege objective chain: destroy statues, hold both switches, then capture the throne.</summary>
[Test]
public async Task FullSiegeObjectiveChainToOwnershipAsync()
{
var ctx = new CastleSiegeContext(Config());
await ctx.ForceStartRegistrationAsync(T0);
await ctx.ForcePhaseAsync(CastleSiegePhase.Siege, T0);
ctx.SetStatueCount(2);
// Throne blocked until statues destroyed + both switches held.
Assert.That(ctx.TryCaptureThrone("Attackers").Success, Is.False);
ctx.NotifyStatueDestroyed();
ctx.NotifyStatueDestroyed();
ctx.HoldSwitch(217, "Attackers");
Assert.That(ctx.TryCaptureThrone("Attackers").Success, Is.False); // only one switch held
ctx.HoldSwitch(218, "Attackers");
Assert.That(ctx.TryCaptureThrone("Attackers").Success, Is.True); // both switches + statues down
Assert.That(ctx.OccupierGuildName, Is.EqualTo("Attackers"));
await ctx.TickAsync(T0.AddMinutes(20)); // Siege -> Settlement
await ctx.TickAsync(T0.AddMinutes(20)); // Settlement -> Ownership
Assert.That(ctx.OwnerGuildName, Is.EqualTo("Attackers"));
}
/// <summary>Tests that a rival guild holding only one switch cannot steal the throne.</summary>
[Test]
public async Task ThroneRequiresBothSwitchesBySameGuildAsync()
{
var ctx = new CastleSiegeContext(Config());
await ctx.ForceStartRegistrationAsync(T0);
await ctx.ForcePhaseAsync(CastleSiegePhase.Siege, T0);
ctx.SetStatueCount(0);
ctx.HoldSwitch(217, "A");
ctx.HoldSwitch(218, "B");
Assert.That(ctx.TryCaptureThrone("A").Success, Is.False);
Assert.That(ctx.TryCaptureThrone("B").Success, Is.False);
}
/// <summary>Tests that objective actions outside the siege phase are no-ops.</summary>
[Test]
public void ObjectiveActionsOutsideSiegeAreNoOp()
{
var ctx = new CastleSiegeContext(Config());
ctx.HoldSwitch(217, "A");
Assert.That(ctx.TryCaptureThrone("A").Success, Is.False);
Assert.That(ctx.OccupierGuildName, Is.Null);
}
private static CastleSiegeConfiguration Config() => new()
{
RegistrationDuration = TimeSpan.FromMinutes(5),
PreparationDuration = TimeSpan.FromMinutes(2),
SiegeDuration = TimeSpan.FromMinutes(10),
};
}