neon-sprawl/server/NeonSprawl.Server.Tests/Game/Mastery/MasteryFixtureApiTests.cs

179 lines
6.3 KiB
C#

using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using NeonSprawl.Server.Game.Mastery;
using NeonSprawl.Server.Game.Skills;
using Xunit;
namespace NeonSprawl.Server.Tests.Game.Mastery;
public sealed class MasteryFixtureApiTests
{
private const string DevPlayer = "dev-local-1";
private const string FixturePath = $"/game/players/{DevPlayer}/__dev/mastery-fixture";
private static MasteryFixtureRequest ValidFixture(
bool resetPerkState = true,
Dictionary<string, int>? skillXp = null) =>
new MasteryFixtureRequest
{
SchemaVersion = MasteryFixtureRequest.CurrentSchemaVersion,
ResetPerkState = resetPerkState,
SkillXp = skillXp ?? new Dictionary<string, int> { ["salvage"] = 0 },
};
private static async Task ArrangeSalvageProgressedWithTier1PickAsync(HttpClient client)
{
var grant = await client.PostAsJsonAsync(
$"/game/players/{DevPlayer}/skill-progression",
new SkillProgressionGrantRequest
{
SchemaVersion = SkillProgressionGrantRequest.CurrentSchemaVersion,
SkillId = "salvage",
Amount = 100,
SourceKind = "activity",
});
Assert.Equal(HttpStatusCode.OK, grant.StatusCode);
var pick = await client.PostAsJsonAsync(
$"/game/players/{DevPlayer}/perk-state",
new PerkBranchSelectRequest
{
SchemaVersion = PerkBranchSelectRequest.CurrentSchemaVersion,
SkillId = "salvage",
TierIndex = 1,
BranchId = "scrap_efficiency",
});
Assert.Equal(HttpStatusCode.OK, pick.StatusCode);
}
[Fact]
public async Task PostMasteryFixture_ShouldReturnNotFound_WhenRouteNotRegistered()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var client = factory.WithWebHostBuilder(b =>
{
b.UseEnvironment("Production");
b.UseSetting("Game:EnableMasteryFixtureApi", "false");
}).CreateClient();
// Act
var response = await client.PostAsJsonAsync(FixturePath, ValidFixture());
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task PostMasteryFixture_ShouldReturnBadRequest_WhenBodyNull()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var client = factory.CreateClient();
// Act
var response = await client.PostAsync(
FixturePath,
new StringContent(string.Empty, Encoding.UTF8, "application/json"));
// Assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
[Fact]
public async Task PostMasteryFixture_ShouldReturnBadRequest_WhenSchemaVersionMismatch()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var client = factory.CreateClient();
var request = ValidFixture();
request = new MasteryFixtureRequest
{
SchemaVersion = MasteryFixtureRequest.CurrentSchemaVersion + 99,
ResetPerkState = request.ResetPerkState,
SkillXp = request.SkillXp,
};
// Act
var response = await client.PostAsJsonAsync(FixturePath, request);
// Assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
[Fact]
public async Task PostMasteryFixture_ShouldReturnBadRequest_WhenSkillXpNegative()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var client = factory.CreateClient();
// Act
var response = await client.PostAsJsonAsync(
FixturePath,
ValidFixture(skillXp: new Dictionary<string, int> { ["salvage"] = -1 }));
// Assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
[Fact]
public async Task PostMasteryFixture_ShouldReturnNotFound_WhenPlayerUnknown()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var client = factory.CreateClient();
// Act
var response = await client.PostAsJsonAsync(
"/game/players/missing-player/__dev/mastery-fixture",
ValidFixture());
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task PostMasteryFixture_ShouldResetSalvageXpAndPerkState_ThenLevelTooLowDenies()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var client = factory.CreateClient();
await ArrangeSalvageProgressedWithTier1PickAsync(client);
// Act
var reset = await client.PostAsJsonAsync(FixturePath, ValidFixture());
var deny = await client.PostAsJsonAsync(
$"/game/players/{DevPlayer}/perk-state",
new PerkBranchSelectRequest
{
SchemaVersion = PerkBranchSelectRequest.CurrentSchemaVersion,
SkillId = "salvage",
TierIndex = 1,
BranchId = "scrap_efficiency",
});
// Assert
Assert.Equal(HttpStatusCode.OK, reset.StatusCode);
var resetBody = await reset.Content.ReadFromJsonAsync<MasteryFixtureResponse>();
Assert.NotNull(resetBody);
Assert.True(resetBody!.Applied);
var progression = await client.GetFromJsonAsync<SkillProgressionSnapshotResponse>(
$"/game/players/{DevPlayer}/skill-progression");
var salvage = Assert.Single(progression!.Skills!, static s => s.Id == "salvage");
Assert.Equal(0, salvage.Xp);
Assert.Equal(1, salvage.Level);
var perkGet = await client.GetFromJsonAsync<PerkStateSnapshotResponse>(
$"/game/players/{DevPlayer}/perk-state");
Assert.Empty(perkGet!.BranchPicks);
Assert.Equal(HttpStatusCode.OK, deny.StatusCode);
var envelope = await deny.Content.ReadFromJsonAsync<PerkBranchSelectResponse>();
Assert.NotNull(envelope);
Assert.False(envelope!.Selected);
Assert.Equal(PerkUnlockReasonCodes.LevelTooLow, envelope.ReasonCode);
}
}