From aa837907036504d6edbc47304ff9bf43610d7ae0 Mon Sep 17 00:00:00 2001 From: VinPropane Date: Sun, 17 May 2026 20:46:28 -0400 Subject: [PATCH] NEO-48: omit null reasonCode from success JSON responses WhenWritingNull on PerkBranchSelectResponse and SkillProgressionGrantResponse matches hotbar/cast/interaction; fixes Bruno tier1 undefined check. --- .../perk-state/Post branch select tier1.bru | 1 + .../Game/Mastery/PerkStateApiTests.cs | 9 ++++++++- server/NeonSprawl.Server/Game/Mastery/PerkStateDtos.cs | 1 + .../Game/Skills/SkillProgressionGrantDtos.cs | 1 + 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/bruno/neon-sprawl-server/perk-state/Post branch select tier1.bru b/bruno/neon-sprawl-server/perk-state/Post branch select tier1.bru index 1c7d8c7..4c8ffc2 100644 --- a/bruno/neon-sprawl-server/perk-state/Post branch select tier1.bru +++ b/bruno/neon-sprawl-server/perk-state/Post branch select tier1.bru @@ -6,6 +6,7 @@ meta { docs { NEO-48: tier-1 salvage branch pick. Run skill-progression grant (100+ XP for level 2) first if this denies level_too_low. + On success, reasonCode is omitted from JSON (not null); test expects undefined. } post { diff --git a/server/NeonSprawl.Server.Tests/Game/Mastery/PerkStateApiTests.cs b/server/NeonSprawl.Server.Tests/Game/Mastery/PerkStateApiTests.cs index d83c843..d6372a4 100644 --- a/server/NeonSprawl.Server.Tests/Game/Mastery/PerkStateApiTests.cs +++ b/server/NeonSprawl.Server.Tests/Game/Mastery/PerkStateApiTests.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Json; +using System.Text.Json; using System.Text; using NeonSprawl.Server.Game.Mastery; using NeonSprawl.Server.Game.Skills; @@ -156,7 +157,13 @@ public sealed class PerkStateApiTests // Assert Assert.Equal(HttpStatusCode.OK, post.StatusCode); - var envelope = await post.Content.ReadFromJsonAsync(); + var json = await post.Content.ReadAsStringAsync(); + using (var doc = JsonDocument.Parse(json)) + { + Assert.False(doc.RootElement.TryGetProperty("reasonCode", out _)); + } + + var envelope = JsonSerializer.Deserialize(json); Assert.NotNull(envelope); Assert.True(envelope!.Selected); Assert.Null(envelope.ReasonCode); diff --git a/server/NeonSprawl.Server/Game/Mastery/PerkStateDtos.cs b/server/NeonSprawl.Server/Game/Mastery/PerkStateDtos.cs index f2f6a78..e45a1a5 100644 --- a/server/NeonSprawl.Server/Game/Mastery/PerkStateDtos.cs +++ b/server/NeonSprawl.Server/Game/Mastery/PerkStateDtos.cs @@ -69,6 +69,7 @@ public sealed class PerkBranchSelectResponse public bool Selected { get; init; } [JsonPropertyName("reasonCode")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ReasonCode { get; init; } [JsonPropertyName("perkState")] diff --git a/server/NeonSprawl.Server/Game/Skills/SkillProgressionGrantDtos.cs b/server/NeonSprawl.Server/Game/Skills/SkillProgressionGrantDtos.cs index b853314..d3ba3e9 100644 --- a/server/NeonSprawl.Server/Game/Skills/SkillProgressionGrantDtos.cs +++ b/server/NeonSprawl.Server/Game/Skills/SkillProgressionGrantDtos.cs @@ -34,6 +34,7 @@ public sealed class SkillProgressionGrantResponse /// Set when is false; stable snake_case values (see README). [JsonPropertyName("reasonCode")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ReasonCode { get; init; } [JsonPropertyName("progression")]