From 89b3d23bde120cf9fb2bae61a2e440ebfc06ceab Mon Sep 17 00:00:00 2001 From: gamer147 Date: Mon, 1 Jun 2026 12:37:44 -0400 Subject: [PATCH] feat(viewer-repo): add LoadForMatchContextAsync for battle-node ctx build Focused AsNoTracking load with Info.SelectedEmblem/SelectedDegree includes for the new MatchContextBuilder. Single test locks the include graph. Co-Authored-By: Claude Opus 4.7 --- .../Repositories/Viewer/IViewerRepository.cs | 7 +++ .../Repositories/Viewer/ViewerRepository.cs | 9 ++++ .../ViewerRepositoryMatchContextTests.cs | 50 +++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 SVSim.UnitTests/Repositories/ViewerRepositoryMatchContextTests.cs diff --git a/SVSim.Database/Repositories/Viewer/IViewerRepository.cs b/SVSim.Database/Repositories/Viewer/IViewerRepository.cs index d032ebf..017e0b1 100644 --- a/SVSim.Database/Repositories/Viewer/IViewerRepository.cs +++ b/SVSim.Database/Repositories/Viewer/IViewerRepository.cs @@ -20,4 +20,11 @@ public interface IViewerRepository /// viewer's UDID to the target, then deletes the anonymous viewer. /// Task MergeAnonymousViewerInto(long anonymousViewerId, long targetViewerId); + + /// + /// Focused load for building a battle-node MatchContext: viewer + Info + Info's + /// equipped Emblem/Degree nav refs. Read-only (AsNoTracking). Returns null if the viewer + /// doesn't exist. + /// + Task LoadForMatchContextAsync(long viewerId); } diff --git a/SVSim.Database/Repositories/Viewer/ViewerRepository.cs b/SVSim.Database/Repositories/Viewer/ViewerRepository.cs index d60ce5e..8d80812 100644 --- a/SVSim.Database/Repositories/Viewer/ViewerRepository.cs +++ b/SVSim.Database/Repositories/Viewer/ViewerRepository.cs @@ -228,6 +228,15 @@ public class ViewerRepository : IViewerRepository await _dbContext.SaveChangesAsync(); } + public async Task LoadForMatchContextAsync(long viewerId) + { + return await _dbContext.Set() + .AsNoTracking() + .Include(v => v.Info.SelectedEmblem) + .Include(v => v.Info.SelectedDegree) + .FirstOrDefaultAsync(v => v.Id == viewerId); + } + private async Task BuildDefaultViewer(string displayName, int initialTutorialState = 1) { Models.Viewer viewer = new Models.Viewer diff --git a/SVSim.UnitTests/Repositories/ViewerRepositoryMatchContextTests.cs b/SVSim.UnitTests/Repositories/ViewerRepositoryMatchContextTests.cs new file mode 100644 index 0000000..833d0ea --- /dev/null +++ b/SVSim.UnitTests/Repositories/ViewerRepositoryMatchContextTests.cs @@ -0,0 +1,50 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using SVSim.Database; +using SVSim.Database.Models; +using SVSim.Database.Repositories.Viewer; +using SVSim.UnitTests.Infrastructure; + +namespace SVSim.UnitTests.Repositories; + +[TestFixture] +public class ViewerRepositoryMatchContextTests +{ + [Test] + public async Task LoadForMatchContext_includes_info_selected_emblem_and_degree() + { + await using var factory = new SVSimTestFactory(); + var vid = await factory.SeedViewerAsync(); + + int emblemId, degreeId; + using (var seedScope = factory.Services.CreateScope()) + { + var db = seedScope.ServiceProvider.GetRequiredService(); + // Use existing rows from the reference-data seed — inserting EmblemEntry/DegreeEntry + // with a hardcoded Id collides with the seeded catalog (UNIQUE constraint). + var emblem = await db.Emblems.FirstAsync(); + var degree = await db.Degrees.FirstAsync(); + emblemId = emblem.Id; + degreeId = degree.Id; + + var viewer = await db.Viewers.FindAsync(vid); + viewer!.Info.CountryCode = "KOR"; + viewer.Info.IsOfficial = true; + viewer.Info.SelectedEmblem = emblem; + viewer.Info.SelectedDegree = degree; + viewer.DisplayName = "DraftedPlayer"; + await db.SaveChangesAsync(); + } + + using var scope = factory.Services.CreateScope(); + var repo = scope.ServiceProvider.GetRequiredService(); + var loaded = await repo.LoadForMatchContextAsync(vid); + + Assert.That(loaded, Is.Not.Null); + Assert.That(loaded!.DisplayName, Is.EqualTo("DraftedPlayer")); + Assert.That(loaded.Info.CountryCode, Is.EqualTo("KOR")); + Assert.That(loaded.Info.IsOfficial, Is.True); + Assert.That(loaded.Info.SelectedEmblem.Id, Is.EqualTo(emblemId)); + Assert.That(loaded.Info.SelectedDegree.Id, Is.EqualTo(degreeId)); + } +}