feat(replay): wire finish hook for rank-battle family

Finish now consumes the stashed BattleContext, records a
ViewerBattleHistory row (idempotent + retention-capped), and
calls IPlayedTogetherWriter for human PvP (skipped for AI).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-06-10 08:15:12 -04:00
parent a81289311f
commit 81aac701f4
2 changed files with 100 additions and 2 deletions

View File

@@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using SVSim.Database;
using SVSim.Database.Models;
using SVSim.Database.Services.Replay;
using SVSim.EmulatedEntrypoint.Models.Dtos.Replay;
using SVSim.UnitTests.Infrastructure;
@@ -102,6 +103,76 @@ public class ReplayControllerTests
Assert.That(resp.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task AiRankFinish_writes_history_row_visible_from_ReplayInfo()
{
using var factory = new SVSimTestFactory();
long viewerId = await factory.SeedViewerAsync(steamId: 76_561_198_000_000_001UL);
// Inject a pre-stashed BattleContext as if /ai_rotation_rank_battle/start had run.
var store = factory.Services.GetRequiredService<IBattleContextStore>();
store.Set(viewerId, new BattleContext(
BattleId: 234_471_983_876L,
BattleType: 2, DeckFormat: 0, TwoPickType: 0,
SelfClassId: 8, SelfSubClassId: 0, SelfCharaId: 8, SelfRotationId: "0",
OpponentViewerId: 0, OpponentName: "BotName", OpponentClassId: 5,
OpponentSubClassId: 0, OpponentCharaId: 805, OpponentCountryCode: "",
OpponentEmblemId: 721_341_010L, OpponentDegreeId: 120_023L,
OpponentRotationId: "0",
BattleStartTime: new DateTime(2026, 6, 4, 17, 13, 13, DateTimeKind.Utc)));
var client = factory.CreateAuthenticatedClient(viewerId);
var finishResp = await client.PostAsJsonAsync("/ai_rotation_rank_battle/finish", new
{
viewer_id = "0",
steam_id = 0,
steam_session_ticket = "",
battle_result = 1, // win
class_id = 8,
total_turn = 7,
evolve_count = 1,
enemy_evolve_count = 0,
sdtrb = 0,
});
Assert.That(finishResp.StatusCode, Is.EqualTo(HttpStatusCode.OK));
var infoBody = await client.PostAsJsonAsync("/replay/info", EmptyBody())
.ContinueWith(t => t.Result.Content.ReadFromJsonAsync<ReplayInfoResponseDto>())
.Unwrap();
Assert.That(infoBody!.ReplayList, Has.Count.EqualTo(1));
var row = infoBody.ReplayList[0];
Assert.That(row.BattleId, Is.EqualTo("234471983876"));
Assert.That(row.OpponentName, Is.EqualTo("BotName"));
Assert.That(row.IsWin, Is.EqualTo("1"));
Assert.That(row.OpponentEmblemId, Is.EqualTo("721341010"));
}
[Test]
public async Task AiRankFinish_with_no_stashed_context_does_not_crash()
{
using var factory = new SVSimTestFactory();
long viewerId = await factory.SeedViewerAsync(steamId: 76_561_198_000_000_001UL);
var client = factory.CreateAuthenticatedClient(viewerId);
var finishResp = await client.PostAsJsonAsync("/ai_rotation_rank_battle/finish", new
{
viewer_id = "0",
steam_id = 0,
steam_session_ticket = "",
battle_result = 0,
class_id = 1, total_turn = 1, evolve_count = 0, enemy_evolve_count = 0, sdtrb = 0,
});
Assert.That(finishResp.StatusCode, Is.EqualTo(HttpStatusCode.OK));
var infoBody = await client.PostAsJsonAsync("/replay/info", EmptyBody())
.ContinueWith(t => t.Result.Content.ReadFromJsonAsync<ReplayInfoResponseDto>())
.Unwrap();
Assert.That(infoBody!.ReplayList, Is.Empty);
}
private static ViewerBattleHistory NewRow(long viewerId, long battleId, DateTime createTime) => new()
{
ViewerId = viewerId,