diff --git a/SVSim.Bootstrap/Importers/ReferenceDataImporter.cs b/SVSim.Bootstrap/Importers/ReferenceDataImporter.cs index a65fe81..a7a8002 100644 --- a/SVSim.Bootstrap/Importers/ReferenceDataImporter.cs +++ b/SVSim.Bootstrap/Importers/ReferenceDataImporter.cs @@ -15,14 +15,30 @@ namespace SVSim.Bootstrap.Importers; /// public class ReferenceDataImporter { + private readonly TextWriter _out; + private readonly TextWriter _err; + + public ReferenceDataImporter() : this(Console.Out, Console.Error) { } + + /// + /// Pass for both to silence progress banners (tests + /// instantiate this importer ~500 times per run; the captured stdout otherwise OOMs + /// the NUnit trx serializer). + /// + public ReferenceDataImporter(TextWriter output, TextWriter error) + { + _out = output; + _err = error; + } + public async Task ImportAllAsync(SVSimDbContext context, string dataDir) { if (!Directory.Exists(dataDir)) { - Console.Error.WriteLine($"[ReferenceDataImporter] Data dir missing: {dataDir}"); + _err.WriteLine($"[ReferenceDataImporter] Data dir missing: {dataDir}"); return; } - Console.WriteLine($"[ReferenceDataImporter] Reading CSVs from {dataDir}..."); + _out.WriteLine($"[ReferenceDataImporter] Reading CSVs from {dataDir}..."); await ImportClasses(context, dataDir); await ImportLeaderSkins(context, dataDir); @@ -34,10 +50,10 @@ public class ReferenceDataImporter await ImportRankInfo(context, dataDir); await ImportClassExp(context, dataDir); - Console.WriteLine("[ReferenceDataImporter] Done."); + _out.WriteLine("[ReferenceDataImporter] Done."); } - private static async Task ImportClasses(SVSimDbContext ctx, string dir) + private async Task ImportClasses(SVSimDbContext ctx, string dir) { var rows = ReadCsv(dir, "classes.csv"); var existing = await ctx.Classes.ToDictionaryAsync(c => c.Id); @@ -51,10 +67,10 @@ public class ReferenceDataImporter else { ctx.Classes.Add(r); created++; } } await ctx.SaveChangesAsync(); - Console.WriteLine($"[ReferenceDataImporter] Classes: +{created} / ~{updated}"); + _out.WriteLine($"[ReferenceDataImporter] Classes: +{created} / ~{updated}"); } - private static async Task ImportLeaderSkins(SVSimDbContext ctx, string dir) + private async Task ImportLeaderSkins(SVSimDbContext ctx, string dir) { var rows = ReadCsv(dir, "leaderskins.csv"); // CSV writes class_chara_id=0 for neutral/unassigned; the FK column is nullable. @@ -74,10 +90,10 @@ public class ReferenceDataImporter else { ctx.LeaderSkins.Add(r); created++; } } await ctx.SaveChangesAsync(); - Console.WriteLine($"[ReferenceDataImporter] LeaderSkins: +{created} / ~{updated}"); + _out.WriteLine($"[ReferenceDataImporter] LeaderSkins: +{created} / ~{updated}"); } - private static async Task ImportSleeves(SVSimDbContext ctx, string dir) + private async Task ImportSleeves(SVSimDbContext ctx, string dir) { var rows = ReadCsv(dir, "sleeves.csv"); var existing = (await ctx.Sleeves.ToListAsync()).ToHashSet(); @@ -88,10 +104,10 @@ public class ReferenceDataImporter ctx.Sleeves.Add(r); created++; } await ctx.SaveChangesAsync(); - Console.WriteLine($"[ReferenceDataImporter] Sleeves: +{created}"); + _out.WriteLine($"[ReferenceDataImporter] Sleeves: +{created}"); } - private static async Task ImportEmblems(SVSimDbContext ctx, string dir) + private async Task ImportEmblems(SVSimDbContext ctx, string dir) { var rows = ReadCsv(dir, "emblems.csv"); var existing = (await ctx.Emblems.Select(e => e.Id).ToListAsync()).ToHashSet(); @@ -102,10 +118,10 @@ public class ReferenceDataImporter ctx.Emblems.Add(r); created++; } await ctx.SaveChangesAsync(); - Console.WriteLine($"[ReferenceDataImporter] Emblems: +{created}"); + _out.WriteLine($"[ReferenceDataImporter] Emblems: +{created}"); } - private static async Task ImportDegrees(SVSimDbContext ctx, string dir) + private async Task ImportDegrees(SVSimDbContext ctx, string dir) { var rows = ReadCsv(dir, "degrees.csv"); var existing = (await ctx.Degrees.Select(e => e.Id).ToListAsync()).ToHashSet(); @@ -116,10 +132,10 @@ public class ReferenceDataImporter ctx.Degrees.Add(r); created++; } await ctx.SaveChangesAsync(); - Console.WriteLine($"[ReferenceDataImporter] Degrees: +{created}"); + _out.WriteLine($"[ReferenceDataImporter] Degrees: +{created}"); } - private static async Task ImportBattlefields(SVSimDbContext ctx, string dir) + private async Task ImportBattlefields(SVSimDbContext ctx, string dir) { var rows = ReadCsv(dir, "battlefields.csv"); var existing = await ctx.Battlefields.ToDictionaryAsync(b => b.Id); @@ -133,10 +149,10 @@ public class ReferenceDataImporter else { ctx.Battlefields.Add(r); created++; } } await ctx.SaveChangesAsync(); - Console.WriteLine($"[ReferenceDataImporter] Battlefields: +{created} / ~{updated}"); + _out.WriteLine($"[ReferenceDataImporter] Battlefields: +{created} / ~{updated}"); } - private static async Task ImportMyPageBackgrounds(SVSimDbContext ctx, string dir) + private async Task ImportMyPageBackgrounds(SVSimDbContext ctx, string dir) { var rows = ReadCsv(dir, "mypagebackgrounds.csv"); var existing = (await ctx.MyPageBackgrounds.Select(e => e.Id).ToListAsync()).ToHashSet(); @@ -147,10 +163,10 @@ public class ReferenceDataImporter ctx.MyPageBackgrounds.Add(r); created++; } await ctx.SaveChangesAsync(); - Console.WriteLine($"[ReferenceDataImporter] MyPageBackgrounds: +{created}"); + _out.WriteLine($"[ReferenceDataImporter] MyPageBackgrounds: +{created}"); } - private static async Task ImportRankInfo(SVSimDbContext ctx, string dir) + private async Task ImportRankInfo(SVSimDbContext ctx, string dir) { var rows = ReadCsv(dir, "ranks.csv"); var existing = await ctx.RankInfo.ToDictionaryAsync(r => r.Id); @@ -164,7 +180,7 @@ public class ReferenceDataImporter else { ctx.RankInfo.Add(r); created++; } } await ctx.SaveChangesAsync(); - Console.WriteLine($"[ReferenceDataImporter] RankInfo: +{created} / ~{updated}"); + _out.WriteLine($"[ReferenceDataImporter] RankInfo: +{created} / ~{updated}"); } private static bool ApplyRankUpdates(RankInfoEntry e, RankInfoEntry r) @@ -189,7 +205,7 @@ public class ReferenceDataImporter return changed; } - private static async Task ImportClassExp(SVSimDbContext ctx, string dir) + private async Task ImportClassExp(SVSimDbContext ctx, string dir) { var rows = ReadCsv(dir, "classexp.csv"); var existing = await ctx.ClassExpCurve.ToDictionaryAsync(c => c.Id); @@ -203,15 +219,15 @@ public class ReferenceDataImporter else { ctx.ClassExpCurve.Add(r); created++; } } await ctx.SaveChangesAsync(); - Console.WriteLine($"[ReferenceDataImporter] ClassExp: +{created} / ~{updated}"); + _out.WriteLine($"[ReferenceDataImporter] ClassExp: +{created} / ~{updated}"); } - private static List ReadCsv(string dir, string fileName) where TMap : ClassMap, new() + private List ReadCsv(string dir, string fileName) where TMap : ClassMap, new() { string path = Path.Combine(dir, fileName); if (!File.Exists(path)) { - Console.Error.WriteLine($"[ReferenceDataImporter] Missing CSV: {path}"); + _err.WriteLine($"[ReferenceDataImporter] Missing CSV: {path}"); return new List(); } using var reader = new StreamReader(path); diff --git a/SVSim.EmulatedEntrypoint/Program.cs b/SVSim.EmulatedEntrypoint/Program.cs index 2a41a49..0ef2d65 100644 --- a/SVSim.EmulatedEntrypoint/Program.cs +++ b/SVSim.EmulatedEntrypoint/Program.cs @@ -151,7 +151,13 @@ public class Program } } - app.UseHttpLogging(); + // HttpLogging captures full request/response per call. In Testing it pipes ~3 GB of + // stdout into NUnit's per-test result capture across the suite, which OOMs the trx + // serializer. Production keeps it on. + if (!app.Environment.IsEnvironment("Testing")) + { + app.UseHttpLogging(); + } // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) diff --git a/SVSim.EmulatedEntrypoint/appsettings.Testing.json b/SVSim.EmulatedEntrypoint/appsettings.Testing.json new file mode 100644 index 0000000..0a42f07 --- /dev/null +++ b/SVSim.EmulatedEntrypoint/appsettings.Testing.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning", + "Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Warning", + "Microsoft.EntityFrameworkCore": "Warning" + } + } +} diff --git a/SVSim.UnitTests/Infrastructure/SVSimTestFactory.cs b/SVSim.UnitTests/Infrastructure/SVSimTestFactory.cs index 83b2fea..731a65f 100644 --- a/SVSim.UnitTests/Infrastructure/SVSimTestFactory.cs +++ b/SVSim.UnitTests/Infrastructure/SVSimTestFactory.cs @@ -78,7 +78,8 @@ internal sealed class SVSimTestFactory : WebApplicationFactory // production uses so tests exercise the same code path. CardCosmeticRewards skipped — // FK to Cards would reject every row against the minimal 3-card test seed below. var dataDir = Path.Combine(AppContext.BaseDirectory, "Data"); - new ReferenceDataImporter().ImportAllAsync(db, dataDir).GetAwaiter().GetResult(); + new ReferenceDataImporter(TextWriter.Null, TextWriter.Null) + .ImportAllAsync(db, dataDir).GetAwaiter().GetResult(); // Seed a minimal card set so card-pool tests can resolve a non-empty pool without // requiring the full CardImporter tool or a cards.json file. The set is marked