fix(arena): match prod get_challenge_info wire shape; stub ranking_history
Prod /arena/get_challenge_info capture (Season 26):
- reward_step_info.reward_step_list is a Dict<string,string>
({"5":"5","10":"10","15":"15"}), not the List<int> I'd assumed
- max_reward_step is stringified
The previous stub would have parsed at the client (LitJson tolerates the
shape via indexed iteration), but cleaning to match prod exactly.
Also stubs /arena/get_challenge_ranking_history (new endpoint observed
in the same capture). Prod ships {two_pick: [], sealed: []} with no
history populated — empty lists match. Routing smoke added.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -43,6 +43,14 @@ public class ArenaController : SVSimController
|
|||||||
catch { /* fall back to defaults */ }
|
catch { /* fall back to defaults */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default Challenge Master reward steps from prod capture: 3 milestones at 5/10/15 wins.
|
||||||
|
var rewardSteps = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["5"] = "5",
|
||||||
|
["10"] = "10",
|
||||||
|
["15"] = "15",
|
||||||
|
};
|
||||||
|
|
||||||
return Ok(new GetChallengeInfoResponseDto
|
return Ok(new GetChallengeInfoResponseDto
|
||||||
{
|
{
|
||||||
ChallengeName = name,
|
ChallengeName = name,
|
||||||
@@ -51,9 +59,17 @@ public class ArenaController : SVSimController
|
|||||||
TwoPickAllWinCount = 0,
|
TwoPickAllWinCount = 0,
|
||||||
RewardStepInfo = new RewardStepInfoDto
|
RewardStepInfo = new RewardStepInfoDto
|
||||||
{
|
{
|
||||||
MaxRewardStep = 0,
|
MaxRewardStep = 15,
|
||||||
RewardStepList = new List<int>(),
|
RewardStepList = rewardSteps,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("get_challenge_ranking_history")]
|
||||||
|
public IActionResult GetChallengeRankingHistory([FromBody] GetChallengeInfoRequest req)
|
||||||
|
{
|
||||||
|
if (!TryGetViewerId(out _)) return Unauthorized();
|
||||||
|
// Prod returns {two_pick: [], sealed: []}. Stub matches.
|
||||||
|
return Ok(new GetChallengeRankingHistoryResponseDto());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using MessagePack;
|
using MessagePack;
|
||||||
|
using SVSim.EmulatedEntrypoint.Models.Dtos.Common;
|
||||||
|
|
||||||
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Arena;
|
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Arena;
|
||||||
|
|
||||||
@@ -32,9 +33,15 @@ public class GetChallengeInfoResponseDto
|
|||||||
[MessagePackObject]
|
[MessagePackObject]
|
||||||
public class RewardStepInfoDto
|
public class RewardStepInfoDto
|
||||||
{
|
{
|
||||||
[JsonPropertyName("max_reward_step")] [Key("max_reward_step")]
|
[JsonPropertyName("max_reward_step")] [JsonConverter(typeof(StringifiedIntConverter))] [Key("max_reward_step")]
|
||||||
public int MaxRewardStep { get; set; }
|
public int MaxRewardStep { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wire shape: dict keyed by stringified-int win count → stringified-int reward step
|
||||||
|
/// (prod capture: <c>{"5":"5","10":"10","15":"15"}</c>). Client parser at
|
||||||
|
/// ChallangeHistoryInfoTask.cs:43 iterates by Count + indexed value access, which works
|
||||||
|
/// for both arrays and LitJson object-iteration order — but prod always ships the dict.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("reward_step_list")] [Key("reward_step_list")]
|
[JsonPropertyName("reward_step_list")] [Key("reward_step_list")]
|
||||||
public List<int> RewardStepList { get; set; } = new();
|
public Dictionary<string, string> RewardStepList { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using MessagePack;
|
||||||
|
|
||||||
|
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Arena;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wire shape for /arena/get_challenge_ranking_history. Prod returns two empty lists
|
||||||
|
/// (two_pick + sealed) per the season-26 capture. Populated history is per-viewer + per-season
|
||||||
|
/// ranking snapshots; not tracked locally yet.
|
||||||
|
/// </summary>
|
||||||
|
[MessagePackObject]
|
||||||
|
public class GetChallengeRankingHistoryResponseDto
|
||||||
|
{
|
||||||
|
[JsonPropertyName("two_pick")] [Key("two_pick")]
|
||||||
|
public List<object> TwoPick { get; set; } = new();
|
||||||
|
|
||||||
|
[JsonPropertyName("sealed")] [Key("sealed")]
|
||||||
|
public List<object> Sealed { get; set; } = new();
|
||||||
|
}
|
||||||
@@ -110,6 +110,7 @@ public class RoutingSmokeTests
|
|||||||
[TestCase("/arena_two_pick_battle/finish")]
|
[TestCase("/arena_two_pick_battle/finish")]
|
||||||
[TestCase("/arena_colosseum/get_fee_info")]
|
[TestCase("/arena_colosseum/get_fee_info")]
|
||||||
[TestCase("/arena/get_challenge_info")]
|
[TestCase("/arena/get_challenge_info")]
|
||||||
|
[TestCase("/arena/get_challenge_ranking_history")]
|
||||||
public async Task Authenticated_route_resolves(string path)
|
public async Task Authenticated_route_resolves(string path)
|
||||||
{
|
{
|
||||||
using var factory = new TestFactory();
|
using var factory = new TestFactory();
|
||||||
|
|||||||
Reference in New Issue
Block a user