130 lines
5.8 KiB
C#
130 lines
5.8 KiB
C#
using System.Net;
|
|
using System.Net.Http.Json;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using NeonSprawl.Server.Game.AbilityInput;
|
|
using NeonSprawl.Server.Game.Combat;
|
|
using NeonSprawl.Server.Game.Encounters;
|
|
using NeonSprawl.Server.Game.Npc;
|
|
using NeonSprawl.Server.Tests;
|
|
using Xunit;
|
|
|
|
namespace NeonSprawl.Server.Tests.Game.Combat;
|
|
|
|
public sealed class CombatTargetFixtureApiTests
|
|
{
|
|
private const string FixturePath = "/game/__dev/combat-targets-fixture";
|
|
|
|
[Fact]
|
|
public async Task PostCombatTargetFixture_ShouldResetPrototypeTargets_WhenEnabled()
|
|
{
|
|
// Arrange
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.CreateClient();
|
|
var store = factory.Services.GetRequiredService<ICombatEntityHealthStore>();
|
|
var threatStore = factory.Services.GetRequiredService<IThreatStateStore>();
|
|
var runtimeStore = factory.Services.GetRequiredService<INpcRuntimeStateStore>();
|
|
var playerHealthStore = factory.Services.GetRequiredService<IPlayerCombatHealthStore>();
|
|
var progressStore = factory.Services.GetRequiredService<IEncounterProgressStore>();
|
|
var completionStore = factory.Services.GetRequiredService<IEncounterCompletionStore>();
|
|
var eventStore = factory.Services.GetRequiredService<IEncounterCompleteEventStore>();
|
|
_ = progressStore.TryActivate("dev-local-1", "prototype_combat_pocket");
|
|
_ = progressStore.TryAddDefeatedTarget("dev-local-1", "prototype_combat_pocket", "prototype_npc_melee");
|
|
_ = completionStore.TryMarkCompleted("dev-local-1", "prototype_combat_pocket", DateTimeOffset.UtcNow);
|
|
_ = eventStore.TryRecord(
|
|
new EncounterCompleteEvent(
|
|
"dev-local-1",
|
|
"prototype_combat_pocket",
|
|
"prototype_combat_pocket_clear",
|
|
[new EncounterGrantApplied("scrap_metal_bulk", 10)],
|
|
DateTimeOffset.UtcNow,
|
|
"dev-local-1:prototype_combat_pocket"));
|
|
_ = store.TryApplyDamage(PrototypeNpcRegistry.PrototypeNpcMeleeId, 100, out _);
|
|
_ = playerHealthStore.TryApplyDamage("dev-local-1", 40, out _);
|
|
_ = threatStore.TrySetHolder(PrototypeNpcRegistry.PrototypeNpcMeleeId, "dev-local-1");
|
|
var windupStarted = new DateTimeOffset(2026, 5, 27, 12, 0, 0, TimeSpan.Zero);
|
|
_ = runtimeStore.TryWrite(
|
|
PrototypeNpcRegistry.PrototypeNpcMeleeId,
|
|
new NpcRuntimeStateSnapshot(
|
|
PrototypeNpcRegistry.PrototypeNpcMeleeId,
|
|
NpcBehaviorState.TelegraphWindup,
|
|
windupStarted,
|
|
new ActiveTelegraphSnapshot("prototype_npc_melee:test", windupStarted)));
|
|
|
|
// Act
|
|
var response = await client.PostAsJsonAsync(
|
|
FixturePath,
|
|
new CombatTargetFixtureRequest
|
|
{
|
|
SchemaVersion = CombatTargetFixtureRequest.CurrentSchemaVersion,
|
|
});
|
|
|
|
// Assert
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
var body = await response.Content.ReadFromJsonAsync<CombatTargetFixtureResponse>();
|
|
Assert.NotNull(body);
|
|
Assert.True(body!.Applied);
|
|
store.TryGet(PrototypeNpcRegistry.PrototypeNpcMeleeId, out var melee);
|
|
Assert.Equal(100, melee.CurrentHp);
|
|
Assert.False(melee.Defeated);
|
|
threatStore.TryGet(PrototypeNpcRegistry.PrototypeNpcMeleeId, out var threat);
|
|
Assert.Null(threat.AggroHolderPlayerId);
|
|
runtimeStore.TryGet(PrototypeNpcRegistry.PrototypeNpcMeleeId, out var runtime);
|
|
Assert.Equal(NpcBehaviorState.Idle, runtime.BehaviorState);
|
|
Assert.Null(runtime.ActiveTelegraph);
|
|
playerHealthStore.TryGet("dev-local-1", out var playerHealth);
|
|
Assert.Equal(PlayerCombatHealthDefaults.MaxHp, playerHealth.CurrentHp);
|
|
Assert.False(playerHealth.Defeated);
|
|
Assert.False(progressStore.TryGetProgress("dev-local-1", "prototype_combat_pocket", out _));
|
|
Assert.False(completionStore.IsCompleted("dev-local-1", "prototype_combat_pocket"));
|
|
Assert.False(eventStore.TryGet("dev-local-1", "prototype_combat_pocket", out _));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PostCombatTargetFixture_ShouldClearAbilityCooldowns_WhenEnabled()
|
|
{
|
|
// Arrange
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.CreateClient();
|
|
var cooldownStore = factory.Services.GetRequiredService<IPlayerAbilityCooldownStore>();
|
|
var now = factory.FakeClock!.GetUtcNow();
|
|
cooldownStore.StartCooldown("dev-local-1", slotIndex: 0, now, TimeSpan.FromSeconds(30));
|
|
Assert.True(cooldownStore.IsOnCooldown("dev-local-1", slotIndex: 0, now));
|
|
|
|
// Act
|
|
var response = await client.PostAsJsonAsync(
|
|
FixturePath,
|
|
new CombatTargetFixtureRequest
|
|
{
|
|
SchemaVersion = CombatTargetFixtureRequest.CurrentSchemaVersion,
|
|
});
|
|
|
|
// Assert
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
Assert.False(cooldownStore.IsOnCooldown("dev-local-1", slotIndex: 0, now));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PostCombatTargetFixture_ShouldReturnNotFound_WhenRouteNotRegistered()
|
|
{
|
|
// Arrange
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.WithWebHostBuilder(b =>
|
|
{
|
|
b.UseEnvironment("Production");
|
|
b.UseSetting("Game:EnableCombatTargetFixtureApi", "false");
|
|
}).CreateClient();
|
|
|
|
// Act
|
|
var response = await client.PostAsJsonAsync(
|
|
FixturePath,
|
|
new CombatTargetFixtureRequest
|
|
{
|
|
SchemaVersion = CombatTargetFixtureRequest.CurrentSchemaVersion,
|
|
});
|
|
|
|
// Assert
|
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
|
}
|
|
}
|