using System.Text; using System.Text.Json; namespace ClaudeDo.Localization.Tests; /// /// JsonDocument/JsonElement silently keep the last value on a duplicate key instead of throwing, /// so a repeated top-level block (e.g. two "checks" objects) is invisible to JSON-validity and /// parity tests alike. This walks the raw token stream instead. /// public class DuplicateLocaleKeysTests { private static string LocalesDir() { var dir = AppContext.BaseDirectory; while (dir is not null) { var candidate = Path.Combine(dir, "src", "ClaudeDo.Localization", "locales"); if (Directory.Exists(candidate)) return candidate; dir = Path.GetDirectoryName(dir); } throw new DirectoryNotFoundException("Could not locate src/ClaudeDo.Localization/locales"); } private static List FindDuplicateTopLevelKeys(string json) { var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json)); var seen = new HashSet(StringComparer.Ordinal); var duplicates = new List(); var depth = 0; while (reader.Read()) { switch (reader.TokenType) { case JsonTokenType.StartObject: case JsonTokenType.StartArray: depth++; break; case JsonTokenType.EndObject: case JsonTokenType.EndArray: depth--; break; case JsonTokenType.PropertyName: if (depth == 1) { var name = reader.GetString()!; if (!seen.Add(name)) duplicates.Add(name); } break; } } return duplicates; } [Fact] public void Detects_duplicate_top_level_key_in_fixture() { const string json = """ { "checks": { "a": "1" }, "other": { "b": "2" }, "checks": { "c": "3" } } """; var duplicates = FindDuplicateTopLevelKeys(json); Assert.Contains("checks", duplicates); } [Fact] public void Locale_files_have_no_duplicate_top_level_keys() { var localesDir = LocalesDir(); foreach (var path in Directory.EnumerateFiles(localesDir, "*.json")) { var duplicates = FindDuplicateTopLevelKeys(File.ReadAllText(path)); Assert.True(duplicates.Count == 0, $"{Path.GetFileName(path)} has duplicate top-level keys: {string.Join(", ", duplicates)}"); } } }