neon-sprawl/server/NeonSprawl.Server.Tests/Game/Combat/PlayerCombatHealthApiTests.cs

133 lines
5.5 KiB
C#

using System.Net;
using System.Net.Http.Json;
using Microsoft.Extensions.DependencyInjection;
using NeonSprawl.Server.Game.AbilityInput;
using NeonSprawl.Server.Game.Combat;
using NeonSprawl.Server.Game.Npc;
using NeonSprawl.Server.Game.PositionState;
using NeonSprawl.Server.Game.Targeting;
using NeonSprawl.Server.Tests;
using Xunit;
namespace NeonSprawl.Server.Tests.Game.Combat;
public sealed class PlayerCombatHealthApiTests
{
private static async Task MoveDevPlayerNearEliteAsync(HttpClient client)
{
var response = await client.PostAsJsonAsync(
"/game/players/dev-local-1/move",
new MoveCommandRequest
{
SchemaVersion = MoveCommandRequest.CurrentSchemaVersion,
Target = new PositionVector { X = -2, Y = 0.9, Z = -2 },
});
response.EnsureSuccessStatusCode();
}
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 LockPrototypeNpcEliteAsync(HttpClient client)
{
var response = await client.PostAsJsonAsync(
"/game/players/dev-local-1/target/select",
new TargetSelectRequest
{
SchemaVersion = TargetSelectRequest.CurrentSchemaVersion,
TargetId = PrototypeNpcRegistry.PrototypeNpcEliteId,
});
response.EnsureSuccessStatusCode();
}
private static AbilityCastRequest PulseCastRequest() =>
new()
{
SchemaVersion = AbilityCastRequest.CurrentSchemaVersion,
SlotIndex = 0,
AbilityId = PrototypeAbilityRegistry.PrototypePulse,
TargetId = PrototypeNpcRegistry.PrototypeNpcEliteId,
};
[Fact]
public async Task GetPlayerCombatHealth_ShouldReturnSchemaV1_WithFullHpBaseline()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var client = factory.CreateClient();
// Act
var response = await client.GetAsync("/game/players/dev-local-1/combat-health");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadFromJsonAsync<PlayerCombatHealthResponse>();
Assert.NotNull(body);
Assert.Equal(PlayerCombatHealthResponse.CurrentSchemaVersion, body!.SchemaVersion);
Assert.Equal("dev-local-1", body.PlayerId);
Assert.Equal(PlayerCombatHealthDefaults.MaxHp, body.MaxHp);
Assert.Equal(PlayerCombatHealthDefaults.MaxHp, body.CurrentHp);
Assert.False(body.Defeated);
}
[Fact]
public async Task GetPlayerCombatHealth_ShouldReturnNotFound_WhenPlayerUnknown()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var client = factory.CreateClient();
// Act
var response = await client.GetAsync("/game/players/missing-player/combat-health");
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task GetPlayerCombatHealth_ShouldDecreaseAfterEliteTelegraphComplete()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var client = factory.CreateClient();
Assert.NotNull(factory.FakeClock);
await BindSlot0PulseAsync(client);
await MoveDevPlayerNearEliteAsync(client);
await LockPrototypeNpcEliteAsync(client);
var castResponse = await client.PostAsJsonAsync(
"/game/players/dev-local-1/ability-cast",
PulseCastRequest());
castResponse.EnsureSuccessStatusCode();
_ = await client.GetAsync("/game/world/npc-runtime-snapshot");
factory.FakeClock!.Advance(TimeSpan.FromSeconds(5));
_ = await client.GetAsync("/game/world/npc-runtime-snapshot");
factory.FakeClock.Advance(TimeSpan.FromSeconds(2.5));
// Act
var snapshotResponse = await client.GetAsync("/game/world/npc-runtime-snapshot");
var healthResponse = await client.GetAsync("/game/players/dev-local-1/combat-health");
// Assert
snapshotResponse.EnsureSuccessStatusCode();
healthResponse.EnsureSuccessStatusCode();
var snapshotBody = await snapshotResponse.Content.ReadFromJsonAsync<NpcRuntimeSnapshotResponse>();
var healthBody = await healthResponse.Content.ReadFromJsonAsync<PlayerCombatHealthResponse>();
Assert.NotNull(snapshotBody);
Assert.NotNull(healthBody);
var elite = snapshotBody!.NpcInstances.Single(row => row.NpcInstanceId == PrototypeNpcRegistry.PrototypeNpcEliteId);
Assert.Equal("recover", elite.State);
Assert.Equal(75, healthBody!.CurrentHp);
Assert.False(healthBody.Defeated);
Assert.Equal("dev-local-1", healthBody.PlayerId);
var store = factory.Services.GetRequiredService<IPlayerCombatHealthStore>();
store.TryGet("dev-local-1", out var storeSnapshot);
Assert.Equal(healthBody.CurrentHp, storeSnapshot.CurrentHp);
Assert.Equal(healthBody.MaxHp, storeSnapshot.MaxHp);
Assert.Equal(healthBody.Defeated, storeSnapshot.Defeated);
}
}