feat(bootstrap): validate seed event_type against MissionEventKeys registry
Achievement, mission, and BP monthly mission importers now throw at bootstrap if any seed row references an event_type that doesn't start with a registered top-level prefix. Prevents drift: a typo in the seed JSON (or a prefix removed from code without updating the seed) fails loudly at startup instead of silently producing counters no emitter writes to. Verified all 12 top-level prefixes currently in the seed JSON pass validation. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ using SVSim.Bootstrap.Models.Seed;
|
||||
using SVSim.Database;
|
||||
using SVSim.Database.Enums;
|
||||
using SVSim.Database.Models;
|
||||
using SVSim.Database.Services;
|
||||
|
||||
namespace SVSim.Bootstrap.Importers;
|
||||
|
||||
@@ -24,6 +25,22 @@ public class AchievementCatalogImporter
|
||||
|
||||
var existing = await context.AchievementCatalog
|
||||
.ToDictionaryAsync(e => (e.AchievementType, e.Level));
|
||||
// Fail fast on drift between seed event_type values and the code-side registry.
|
||||
// Missing prefixes silently produce counters no emitter ever writes to; typos likewise.
|
||||
var unregistered = seed
|
||||
.Where(s => s.EventType is not null && !MissionEventKeys.IsRegistered(s.EventType))
|
||||
.Select(s => s.EventType!)
|
||||
.Distinct()
|
||||
.OrderBy(x => x)
|
||||
.ToList();
|
||||
if (unregistered.Count > 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"[AchievementCatalogImporter] seed rows reference unregistered event_type(s): "
|
||||
+ string.Join(", ", unregistered)
|
||||
+ ". Add the top-level prefix to MissionEventKeys.RegisteredPrefixes, or fix the seed.");
|
||||
}
|
||||
|
||||
int created = 0, updated = 0;
|
||||
var unmappedTypes = new HashSet<int>();
|
||||
foreach (var s in seed)
|
||||
|
||||
@@ -3,6 +3,7 @@ using SVSim.Bootstrap.Models.Seed;
|
||||
using SVSim.Database;
|
||||
using SVSim.Database.Enums;
|
||||
using SVSim.Database.Models;
|
||||
using SVSim.Database.Services;
|
||||
|
||||
namespace SVSim.Bootstrap.Importers;
|
||||
|
||||
@@ -22,6 +23,21 @@ public class BattlePassMonthlyMissionImporter
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Fail fast on drift between seed event_type values and the code-side registry.
|
||||
var unregistered = seed
|
||||
.Where(s => s.EventType is not null && !MissionEventKeys.IsRegistered(s.EventType))
|
||||
.Select(s => s.EventType!)
|
||||
.Distinct()
|
||||
.OrderBy(x => x)
|
||||
.ToList();
|
||||
if (unregistered.Count > 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"[BattlePassMonthlyMissionImporter] seed rows reference unregistered event_type(s): "
|
||||
+ string.Join(", ", unregistered)
|
||||
+ ". Add the top-level prefix to MissionEventKeys.RegisteredPrefixes, or fix the seed.");
|
||||
}
|
||||
|
||||
var existing = await context.BattlePassMonthlyMissions
|
||||
.ToDictionaryAsync(e => (e.Year, e.Month, e.OrderNum));
|
||||
int created = 0, updated = 0;
|
||||
|
||||
@@ -3,6 +3,7 @@ using SVSim.Bootstrap.Models.Seed;
|
||||
using SVSim.Database;
|
||||
using SVSim.Database.Enums;
|
||||
using SVSim.Database.Models;
|
||||
using SVSim.Database.Services;
|
||||
|
||||
namespace SVSim.Bootstrap.Importers;
|
||||
|
||||
@@ -21,6 +22,21 @@ public class MissionCatalogImporter
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Fail fast on drift between seed event_type values and the code-side registry.
|
||||
var unregistered = seed
|
||||
.Where(s => s.EventType is not null && !MissionEventKeys.IsRegistered(s.EventType))
|
||||
.Select(s => s.EventType!)
|
||||
.Distinct()
|
||||
.OrderBy(x => x)
|
||||
.ToList();
|
||||
if (unregistered.Count > 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"[MissionCatalogImporter] seed rows reference unregistered event_type(s): "
|
||||
+ string.Join(", ", unregistered)
|
||||
+ ". Add the top-level prefix to MissionEventKeys.RegisteredPrefixes, or fix the seed.");
|
||||
}
|
||||
|
||||
var existing = await context.MissionCatalog.ToDictionaryAsync(e => e.Id);
|
||||
int created = 0, updated = 0;
|
||||
var unmapped = new List<int>();
|
||||
|
||||
Reference in New Issue
Block a user