Files
Radicade/ServerTests/AvatarCatalogParsingTests.cs
2026-10-16 23:33:13 -07:00

101 lines
4.2 KiB
C#

using RadicadeServer.RecNet.Avatar;
using Xunit;
namespace RadicadeServer.Tests;
public class AvatarCatalogParsingTests
{
[Fact]
public void V4UsesExactClientKeysAndNonNullStrings()
{
var item = new AvatarItem
{
AvatarItemDesc = "item,,,", FriendlyName = "Test", Rarity = 20,
ThumbnailImage = "abc123.png"
};
var json = System.Text.Json.JsonSerializer.SerializeToElement(new AvatarItemV4(item));
// PascalCase, matching a real capture of this route. The client has no case-insensitive
// fallback, so a lowercased name leaves AvatarItemDesc null and the item unresolvable.
Assert.Equal(
new[]
{
"AvatarItemDesc", "AvatarItemId", "AvatarItemType", "CreatedAt", "FriendlyName",
"IsBaseAvatarItem", "PlatformMask", "Rarity", "TagList", "ThumbnailImage", "Tooltip"
},
json.EnumerateObject().Select(property => property.Name).OrderBy(name => name));
Assert.Equal("item,,,", json.GetProperty("AvatarItemDesc").GetString());
Assert.Equal("", json.GetProperty("TagList").GetString());
Assert.Equal(20, json.GetProperty("Rarity").GetInt32());
Assert.Equal("abc123.png", json.GetProperty("ThumbnailImage").GetString());
// 0 here would read as "usable on no platform" and the wardrobe would hide the item.
Assert.Equal(-1, json.GetProperty("PlatformMask").GetInt32());
var legacy = System.Text.Json.JsonSerializer.SerializeToElement(item);
Assert.True(legacy.TryGetProperty("AvatarItemDesc", out _));
}
[Fact]
public void RepositoryCatalogLoadsSupportedItemsWithoutLosingMetadata()
{
var json = File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Fixtures", "avatar.json"));
var expected = AvatarUnlockCatalog.KeepSupportedItems(
System.Text.Json.JsonSerializer.Deserialize<List<AvatarItem>>(json));
var actual = AvatarUnlockCatalog.ParseCatalog(json);
Assert.NotEmpty(actual);
Assert.Equal(expected.Count, actual.Count);
Assert.Equal(expected.Select(x => x.AvatarItemDesc), actual.Select(x => x.AvatarItemDesc));
Assert.Equal(expected.Select(x => x.FriendlyName), actual.Select(x => x.FriendlyName));
Assert.Equal(expected.Select(x => x.Rarity), actual.Select(x => x.Rarity));
}
[Fact]
public void CatalogCanBeEnrichedWithItemIdsAndThumbnails()
{
var items = AvatarUnlockCatalog.ParseCatalog(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Fixtures", "avatar.json")));
var metadata = File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Fixtures", "merged-avatar.json"));
var enriched = AvatarUnlockCatalog.EnrichWithMetadata(items, metadata);
var shirt = Assert.Single(enriched.Where(x => x.AvatarItemDesc == "8aa79563-ace1-4ba7-ad0c-f3210a78142f,"));
Assert.Equal(1018, shirt.AvatarItemId);
Assert.False(string.IsNullOrWhiteSpace(shirt.ThumbnailImage));
}
[Fact]
public void BadEntriesDoNotDiscardValidItems()
{
var items = AvatarUnlockCatalog.ParseCatalog("""
[
null, 42,
{"AvatarItemDesc":"bad,,,","Rarity":"not-a-number"},
{"AvatarItemDesc":"overflow,,,","AvatarItemId":9223372036854775807},
{"FriendlyName":"Missing description"},
{"AvatarItemDesc":"valid,,,","FriendlyName":"Valid item","Rarity":20}
]
""");
var item = Assert.Single(items);
Assert.Equal("valid,", item.AvatarItemDesc);
Assert.Equal(-1, item.PlatformMask);
Assert.Equal("Valid item", item.FriendlyName);
}
[Fact]
public void AcceptsCommentsTrailingCommasAndQuotedNumbers()
{
var items = AvatarUnlockCatalog.ParseCatalog("""
[ // Hand-edited catalog
{"avatarItemDesc":"valid,,,","rarity":"20","platformMask":"-1",},
]
""");
Assert.Equal(20, Assert.Single(items).Rarity);
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData("null")]
[InlineData("{}")]
[InlineData("[broken")]
public void InvalidCatalogReturnsEmptyForFallback(string? json)
{
Assert.Empty(AvatarUnlockCatalog.ParseCatalog(json));
}
}