diff --git a/SVSim.EmulatedEntrypoint/Controllers/UserMyPageController.cs b/SVSim.EmulatedEntrypoint/Controllers/UserMyPageController.cs index 4e942c0..33eff81 100644 --- a/SVSim.EmulatedEntrypoint/Controllers/UserMyPageController.cs +++ b/SVSim.EmulatedEntrypoint/Controllers/UserMyPageController.cs @@ -32,6 +32,8 @@ public sealed class UserMyPageController : SVSimController viewer.MyPageBgSelectType = request.SelectType; viewer.MyPageBgId = ParseIdOrZero(request.MyPageId); + // Clear() on a loaded OwnsMany marks every tracked entry as Deleted; SaveChangesAsync + // issues DELETEs for all old slots before inserting the new ones. viewer.MyPageBgRotation.Clear(); for (int slot = 0; slot < request.MyPageIdList.Count; slot++) { diff --git a/SVSim.UnitTests/Controllers/UserMyPageControllerTests.cs b/SVSim.UnitTests/Controllers/UserMyPageControllerTests.cs index fb20901..bb26b2c 100644 --- a/SVSim.UnitTests/Controllers/UserMyPageControllerTests.cs +++ b/SVSim.UnitTests/Controllers/UserMyPageControllerTests.cs @@ -146,4 +146,23 @@ public class UserMyPageControllerTests // controller's literal return value is what comes back. An empty class serializes to "{}". Assert.That(raw, Is.EqualTo("{}")); } + + [Test] + public async Task Update_with_unparseable_mypage_id_list_entries_falls_back_to_zero() + { + using var factory = new SVSimTestFactory(); + long viewerId = await factory.SeedViewerAsync(); + using var client = factory.CreateAuthenticatedClient(viewerId); + + var body = JsonBody(""" + {"select_type":2,"mypage_id":"0","mypage_id_list":["1001","garbage","","1002"]} + """); + var response = await client.PostAsync("/user_mypage/update", body); + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); + + var viewer = await LoadViewerWithRotation(factory, viewerId); + var pool = viewer.MyPageBgRotation.OrderBy(r => r.Slot).Select(r => r.BgId).ToList(); + Assert.That(pool, Is.EqualTo(new[] { 1001, 0, 0, 1002 }), + "garbage and empty entries are stored as 0; valid entries unaffected"); + } }