neon-sprawl/server/NeonSprawl.Server.Tests/Game/AbilityInput/CooldownSnapshotApiTests.cs

106 lines
4.2 KiB
C#

using System.Net;
using System.Net.Http.Json;
using NeonSprawl.Server.Game.AbilityInput;
using NeonSprawl.Server.Game.Targeting;
using NeonSprawl.Server.Tests;
using Xunit;
namespace NeonSprawl.Server.Tests.Game.AbilityInput;
public sealed class CooldownSnapshotApiTests
{
private static async Task BindSlot0PulseAsync(HttpClient client)
{
var post = await client.PostAsJsonAsync(
"/game/players/dev-local-1/hotbar-loadout",
new HotbarLoadoutUpdateRequest
{
SchemaVersion = HotbarLoadoutUpdateRequest.CurrentSchemaVersion,
Slots = [new HotbarSlotBindingJson { SlotIndex = 0, AbilityId = PrototypeAbilityRegistry.PrototypePulse }],
});
post.EnsureSuccessStatusCode();
}
private static async Task LockPrototypeTargetAlphaAsync(HttpClient client)
{
var response = await client.PostAsJsonAsync(
"/game/players/dev-local-1/target/select",
new TargetSelectRequest
{
SchemaVersion = TargetSelectRequest.CurrentSchemaVersion,
TargetId = PrototypeTargetRegistry.PrototypeTargetAlphaId,
});
response.EnsureSuccessStatusCode();
}
[Fact]
public async Task GetCooldownSnapshot_ShouldReturnNotFound_WhenPlayerUnknown()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var client = factory.CreateClient();
// Act
var response = await client.GetAsync("/game/players/missing-player/cooldown-snapshot");
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task GetCooldownSnapshot_ShouldReturnV1Schema_WithEightSlots_WhenPlayerKnown()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var client = factory.CreateClient();
// Act
var response = await client.GetAsync("/game/players/dev-local-1/cooldown-snapshot");
// Assert
var body = await response.Content.ReadFromJsonAsync<CooldownSnapshotResponse>();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(body);
Assert.Equal(CooldownSnapshotResponse.CurrentSchemaVersion, body!.SchemaVersion);
Assert.Equal("dev-local-1", body.PlayerId);
Assert.Equal(HotbarLoadoutResponse.SlotCountV1, body.Slots.Count);
Assert.All(body.Slots, s => Assert.Null(s.CooldownEndsAtUtc));
}
[Fact]
public async Task GetCooldownSnapshot_ShouldExposeSlotEnd_WhenCastAccepted_ThenClear_WhenAdvancedPastDuration()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var client = factory.CreateClient();
Assert.NotNull(factory.FakeClock);
await BindSlot0PulseAsync(client);
await LockPrototypeTargetAlphaAsync(client);
var cast = new AbilityCastRequest
{
SchemaVersion = AbilityCastRequest.CurrentSchemaVersion,
SlotIndex = 0,
AbilityId = PrototypeAbilityRegistry.PrototypePulse,
TargetId = PrototypeTargetRegistry.PrototypeTargetAlphaId,
};
// Act
var castResponse = await client.PostAsJsonAsync("/game/players/dev-local-1/ability-cast", cast);
var snapDuring = await client.GetAsync("/game/players/dev-local-1/cooldown-snapshot");
factory.FakeClock!.Advance(AbilityPrototypeCooldown.GlobalDuration + TimeSpan.FromMilliseconds(50));
var snapAfter = await client.GetAsync("/game/players/dev-local-1/cooldown-snapshot");
// Assert
castResponse.EnsureSuccessStatusCode();
var during = await snapDuring.Content.ReadFromJsonAsync<CooldownSnapshotResponse>();
var after = await snapAfter.Content.ReadFromJsonAsync<CooldownSnapshotResponse>();
Assert.NotNull(during);
Assert.NotNull(after);
var slot0During = during!.Slots.Single(s => s.SlotIndex == 0);
Assert.NotNull(slot0During.CooldownEndsAtUtc);
Assert.True(slot0During.CooldownEndsAtUtc > during.ServerTimeUtc);
var slot0After = after!.Slots.Single(s => s.SlotIndex == 0);
Assert.Null(slot0After.CooldownEndsAtUtc);
}
}