feat(objects): CategorizationSync.Reconcile — append Uncategorized + report orphans
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ee0c8c256d
commit
8f20106aec
2 changed files with 121 additions and 0 deletions
|
|
@ -0,0 +1,44 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Server.Commands;
|
||||
using Server.Items;
|
||||
using Xunit;
|
||||
|
||||
namespace UOContent.Tests.Commands.Objects;
|
||||
|
||||
public class CategorizationSyncTests
|
||||
{
|
||||
private static CAGJson Cat(string category, params System.Type[] types) =>
|
||||
new()
|
||||
{
|
||||
Category = category,
|
||||
Objects = types.Select(t => new CAGObject { Type = t }).ToArray()
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void Reconcile_appends_missing_types_to_uncategorized()
|
||||
{
|
||||
var categorization = new List<CAGJson> { Cat("Items.Weapons.Swords", typeof(Katana)) };
|
||||
var discovered = new List<System.Type> { typeof(Katana), typeof(Runebook) };
|
||||
|
||||
var (updated, report) = CategorizationSync.Reconcile(categorization, discovered);
|
||||
|
||||
Assert.Contains("Runebook", report.Appended);
|
||||
Assert.Empty(report.Orphaned);
|
||||
|
||||
var uncategorized = Assert.Single(updated, c => c.Category == "Items.Uncategorized");
|
||||
Assert.Contains(uncategorized.Objects, o => o.Type == typeof(Runebook));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Reconcile_reports_orphans_not_in_discovered()
|
||||
{
|
||||
var categorization = new List<CAGJson> { Cat("Items.Weapons.Swords", typeof(Katana)) };
|
||||
var discovered = new List<System.Type> { typeof(Runebook) };
|
||||
|
||||
var (_, report) = CategorizationSync.Reconcile(categorization, discovered);
|
||||
|
||||
Assert.Contains("Katana", report.Orphaned);
|
||||
Assert.Contains("Runebook", report.Appended);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Commands;
|
||||
|
||||
public sealed record SyncReport(List<string> Appended, List<string> Orphaned);
|
||||
|
||||
public static class CategorizationSync
|
||||
{
|
||||
public static (List<CAGJson> updated, SyncReport report) Reconcile(
|
||||
List<CAGJson> categorization, IReadOnlyList<Type> discovered
|
||||
)
|
||||
{
|
||||
var categorizedTypes = new HashSet<Type>();
|
||||
foreach (var cag in categorization)
|
||||
{
|
||||
foreach (var obj in cag.Objects ?? [])
|
||||
{
|
||||
if (obj.Type != null)
|
||||
{
|
||||
categorizedTypes.Add(obj.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var discoveredSet = new HashSet<Type>(discovered);
|
||||
|
||||
var orphaned = categorizedTypes
|
||||
.Where(t => !discoveredSet.Contains(t))
|
||||
.Select(t => t.Name)
|
||||
.OrderBy(n => n, StringComparer.Ordinal)
|
||||
.ToList();
|
||||
|
||||
var updated = new List<CAGJson>(categorization);
|
||||
var appended = new List<string>();
|
||||
var itemAppend = new List<CAGObject>();
|
||||
var mobileAppend = new List<CAGObject>();
|
||||
|
||||
foreach (var type in discovered)
|
||||
{
|
||||
if (categorizedTypes.Contains(type))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
appended.Add(type.Name);
|
||||
var target = typeof(Mobile).IsAssignableFrom(type) ? mobileAppend : itemAppend;
|
||||
target.Add(new CAGObject { Type = type });
|
||||
}
|
||||
|
||||
AppendUncategorized(updated, "Items.Uncategorized", itemAppend);
|
||||
AppendUncategorized(updated, "Mobiles.Uncategorized", mobileAppend);
|
||||
|
||||
return (updated, new SyncReport(appended, orphaned));
|
||||
}
|
||||
|
||||
private static void AppendUncategorized(List<CAGJson> updated, string category, List<CAGObject> toAdd)
|
||||
{
|
||||
if (toAdd.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var existing = updated.FirstOrDefault(c => c.Category == category);
|
||||
if (existing == null)
|
||||
{
|
||||
updated.Add(new CAGJson { Category = category, Objects = toAdd.ToArray() });
|
||||
return;
|
||||
}
|
||||
|
||||
var merged = new List<CAGObject>(existing.Objects ?? []);
|
||||
merged.AddRange(toAdd);
|
||||
updated[updated.IndexOf(existing)] = existing with { Objects = merged.ToArray() };
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue