feat(battle-xp): wire /practice/finish through IBattleXpService
- Practice finish now grants win/loss XP via IBattleXpService.GrantAsync with BattleXpMode.Practice. Response fields (get_class_experience, class_experience, class_level) reflect post-grant totals. - Uses req.ClassId from wire; no BattleContext lookup needed. - Test asserts default XpPerWin=100 crosses classexp.csv L1=50 threshold → Level=2 + Exp=50 carry, persists to Viewer.Classes. - Added loss-XP test asserting default XpPerLoss=25 stays under threshold. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -197,7 +197,7 @@ public class PracticeControllerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Finish_accepts_any_recovery_data_returns_zero_xp()
|
||||
public async Task Finish_win_grants_class_xp_and_persists_level_up()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
@@ -218,8 +218,44 @@ public class PracticeControllerTests
|
||||
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), body);
|
||||
|
||||
using var doc = JsonDocument.Parse(body);
|
||||
Assert.That(doc.RootElement.GetProperty("get_class_experience").GetInt32(), Is.EqualTo(0));
|
||||
Assert.That(doc.RootElement.GetProperty("class_experience").GetInt32(), Is.EqualTo(0));
|
||||
// BattleXpConfig.XpPerWin default = 100. classexp.csv seeds L1=50, L2=150, so 100 XP
|
||||
// crosses L1's threshold: land at Level=2 with 50 carry.
|
||||
Assert.That(doc.RootElement.GetProperty("get_class_experience").GetInt32(), Is.EqualTo(100));
|
||||
Assert.That(doc.RootElement.GetProperty("class_experience").GetInt32(), Is.EqualTo(50));
|
||||
Assert.That(doc.RootElement.GetProperty("class_level").GetInt32(), Is.EqualTo(2));
|
||||
Assert.That(doc.RootElement.GetProperty("reward_list").GetArrayLength(), Is.EqualTo(0));
|
||||
|
||||
// Persistence check — reload viewer and confirm the ViewerClassData row moved.
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var v = await db.Viewers.Include(x => x.Classes).ThenInclude(c => c.Class)
|
||||
.FirstAsync(x => x.Id == viewerId);
|
||||
var cls1 = v.Classes.Single(c => c.Class.Id == 1);
|
||||
Assert.That(cls1.Level, Is.EqualTo(2));
|
||||
Assert.That(cls1.Exp, Is.EqualTo(50));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Finish_loss_grants_default_loss_xp()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||
|
||||
var finishJson =
|
||||
"""{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","deck_no":1,"is_win":0,"evolve_count":0,"total_turn":3,"enemy_class_id":3,"difficulty":1,"deck_format":1,"class_id":1,"recovery_data":"{}"}""";
|
||||
|
||||
var response = await client.PostAsync("/practice/finish",
|
||||
new StringContent(finishJson, Encoding.UTF8, "application/json"));
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), body);
|
||||
|
||||
using var doc = JsonDocument.Parse(body);
|
||||
// BattleXpConfig.XpPerLoss default = 25. classexp.csv seeds L1=50, so 25 XP stays
|
||||
// under threshold: Level=1, Exp=25.
|
||||
Assert.That(doc.RootElement.GetProperty("get_class_experience").GetInt32(), Is.EqualTo(25));
|
||||
Assert.That(doc.RootElement.GetProperty("class_experience").GetInt32(), Is.EqualTo(25));
|
||||
Assert.That(doc.RootElement.GetProperty("class_level").GetInt32(), Is.EqualTo(1));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user