feat(load): emit daily_login_bonus via ILoginBonusService

Grant runs inside the existing inventory tx so streak-bump and reward
credit commit atomically with cosmetic backfill.
This commit is contained in:
gamer147
2026-06-13 16:40:44 -04:00
parent 5083d43e51
commit 7d83560fc9
2 changed files with 51 additions and 7 deletions

View File

@@ -307,9 +307,11 @@ public class LoadControllerTests
Assert.That(root.GetProperty("rotation_card_set_id_list").GetArrayLength(),
Is.GreaterThanOrEqualTo(2));
// Optional/absent fields stay absent when nothing meaningful to surface
Assert.That(root.TryGetProperty("daily_login_bonus", out _), Is.False,
"daily_login_bonus optional per spec; emit null when no active campaign");
// daily_login_bonus IS emitted for a fresh viewer (LastLoginBonusClaimedAt is null →
// IsDue returns true). The seeded globals test uses a fresh viewer, so it gets Day 1.
Assert.That(root.TryGetProperty("daily_login_bonus", out var dlbGlobals), Is.True,
"daily_login_bonus should be present for a fresh viewer (IsDue=true)");
Assert.That(dlbGlobals.ValueKind, Is.EqualTo(JsonValueKind.Object));
// battle_pass_level_info is present when levels are seeded — 100-entry dict keyed by level string.
Assert.That(root.TryGetProperty("battle_pass_level_info", out var bpli), Is.True,
@@ -380,6 +382,46 @@ public class LoadControllerTests
}
}
[Test]
public async Task Index_emits_daily_login_bonus_for_fresh_viewer()
{
using var factory = new SVSimTestFactory();
long viewerId = await factory.SeedViewerAsync();
var root = await PostIndexAndReadBody(factory, viewerId);
Assert.That(root.TryGetProperty("daily_login_bonus", out var dlb), Is.True);
Assert.That(dlb.ValueKind, Is.EqualTo(JsonValueKind.Object));
var normal = dlb.GetProperty("normal");
Assert.That(normal.GetProperty("now_count").GetInt32(), Is.EqualTo(1));
Assert.That(normal.GetProperty("name").GetString(), Is.EqualTo("Daily Bonus"));
Assert.That(normal.GetProperty("campaign_id").ValueKind, Is.EqualTo(JsonValueKind.String));
Assert.That(normal.GetProperty("img").ValueKind, Is.EqualTo(JsonValueKind.String));
Assert.That(normal.GetProperty("reward").GetArrayLength(), Is.EqualTo(15));
var firstReward = normal.GetProperty("reward")[0];
Assert.That(firstReward.GetProperty("reward_type").GetString(), Is.EqualTo("9"));
Assert.That(firstReward.GetProperty("reward_number").GetString(), Is.EqualTo("20"));
Assert.That(dlb.GetProperty("campaign").ValueKind, Is.EqualTo(JsonValueKind.Array));
Assert.That(dlb.GetProperty("campaign").GetArrayLength(), Is.EqualTo(0));
}
[Test]
public async Task Index_omits_daily_login_bonus_on_second_call_same_day()
{
using var factory = new SVSimTestFactory();
long viewerId = await factory.SeedViewerAsync();
await PostIndexAndReadBody(factory, viewerId);
var root = await PostIndexAndReadBody(factory, viewerId);
var present = root.TryGetProperty("daily_login_bonus", out var dlb)
&& dlb.ValueKind != JsonValueKind.Null;
Assert.That(present, Is.False, "Second-same-day /load/index must not re-emit the bonus");
}
[Test]
public async Task LoadIndex_emits_battle_pass_level_info_with_100_entries_when_period_active()
{