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)}");
}
}
}