NEO-48: revert WhenWritingNull on skill-progression grant API
Keep omit-null reasonCode only on new perk-state responses; restoring the existing grant endpoint wire format (reasonCode: null on success).pull/83/head
parent
12e52bccc9
commit
00c5937872
|
|
@ -8,6 +8,7 @@ docs {
|
||||||
NEO-38: single skill XP grant + allowlist + level-up metadata; see server README.
|
NEO-38: single skill XP grant + allowlist + level-up metadata; see server README.
|
||||||
NEO-40: same contract; server adds comment-only telemetry hook markers (no request/response change).
|
NEO-40: same contract; server adds comment-only telemetry hook markers (no request/response change).
|
||||||
NEO-47: on level-up, server re-evaluates mastery perk unlocks internally (no JSON change until NEO-48 perk-state HTTP).
|
NEO-47: on level-up, server re-evaluates mastery perk unlocks internally (no JSON change until NEO-48 perk-state HTTP).
|
||||||
|
Success responses include reasonCode: null (stable since NEO-38); perk-state POST omits reasonCode on success instead.
|
||||||
E2.M2 tracking row in docs/decomposition/modules/documentation_and_implementation_alignment.md lists NEO-40 landed with plan + manual QA links.
|
E2.M2 tracking row in docs/decomposition/modules/documentation_and_implementation_alignment.md lists NEO-40 landed with plan + manual QA links.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,6 +33,7 @@ tests {
|
||||||
const body = res.getBody();
|
const body = res.getBody();
|
||||||
expect(body.schemaVersion).to.equal(1);
|
expect(body.schemaVersion).to.equal(1);
|
||||||
expect(body.granted).to.equal(true);
|
expect(body.granted).to.equal(true);
|
||||||
|
expect(body.reasonCode).to.equal(null);
|
||||||
expect(body.progression).to.be.an("object");
|
expect(body.progression).to.be.an("object");
|
||||||
expect(body.progression.schemaVersion).to.equal(1);
|
expect(body.progression.schemaVersion).to.equal(1);
|
||||||
expect(body.progression.playerId).to.equal("dev-local-1");
|
expect(body.progression.playerId).to.equal("dev-local-1");
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
|
using System.Text.Json;
|
||||||
using NeonSprawl.Server.Game.Skills;
|
using NeonSprawl.Server.Game.Skills;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -120,6 +121,26 @@ public sealed class SkillProgressionGrantApiTests
|
||||||
Assert.Equal(SkillProgressionSnapshotApi.ReasonInvalidAmount, envelope.ReasonCode);
|
Assert.Equal(SkillProgressionSnapshotApi.ReasonInvalidAmount, envelope.ReasonCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task PostSkillProgression_ShouldSerializeReasonCodeNull_OnSuccess()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
await using var factory = new InMemoryWebApplicationFactory();
|
||||||
|
var client = factory.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.PostAsJsonAsync(
|
||||||
|
"/game/players/dev-local-1/skill-progression",
|
||||||
|
ValidGrant("salvage", amount: 10));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var json = await response.Content.ReadAsStringAsync();
|
||||||
|
using var doc = JsonDocument.Parse(json);
|
||||||
|
Assert.True(doc.RootElement.TryGetProperty("reasonCode", out var reasonCode));
|
||||||
|
Assert.Equal(JsonValueKind.Null, reasonCode.ValueKind);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task PostSkillProgression_ShouldApplyGrant_AndGetMatches_WhenTrainerAllowedForRefine()
|
public async Task PostSkillProgression_ShouldApplyGrant_AndGetMatches_WhenTrainerAllowedForRefine()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,6 @@ public sealed class SkillProgressionGrantResponse
|
||||||
|
|
||||||
/// <remarks>Set when <see cref="Granted"/> is <c>false</c>; stable snake_case values (see README).</remarks>
|
/// <remarks>Set when <see cref="Granted"/> is <c>false</c>; stable snake_case values (see README).</remarks>
|
||||||
[JsonPropertyName("reasonCode")]
|
[JsonPropertyName("reasonCode")]
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
||||||
public string? ReasonCode { get; init; }
|
public string? ReasonCode { get; init; }
|
||||||
|
|
||||||
[JsonPropertyName("progression")]
|
[JsonPropertyName("progression")]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue