using System.Text.Json.Nodes; using Xunit; using Recordingtest.Normalizer; namespace Recordingtest.Normalizer.Tests; public class RuleTests { [Fact] public void StripTimestamps_ReplacesIso8601() { var input = "saved at 2026-04-07T12:34:56.789Z and 2025-01-02 03:04:05"; var (o, c) = Rules.StripTimestamps(input); Assert.Equal(2, c); Assert.Equal("saved at and ", o); } [Fact] public void MaskGuids_ReplacesUuids() { var input = "id=550e8400-e29b-41d4-a716-446655440000 done"; var (o, c) = Rules.MaskGuids(input); Assert.Equal(1, c); Assert.Contains("", o); } [Fact] public void NormalizePaths_ReplacesRepoAndUser() { Environment.SetEnvironmentVariable("RECORDINGTEST_REPO", @"D:\proj\recordingtest"); var input = @"file: D:\proj\recordingtest\foo\bar.txt"; var (o, c) = Rules.NormalizePaths(input); Assert.True(c >= 1); Assert.Contains("", o); Environment.SetEnvironmentVariable("RECORDINGTEST_REPO", null); } [Fact] public void RoundFloats_RoundsToSixDecimals() { var node = JsonNode.Parse("{\"x\": 3.1415926535897932, \"n\": 1}"); var (n, c) = Rules.RoundFloatsInNode(node); Assert.Equal(1, c); Assert.Equal(3.141593, n!["x"]!.GetValue()); } [Fact] public void SortJsonKeys_RecursivelySortsObjects() { var node = JsonNode.Parse("{\"b\":1,\"a\":{\"y\":2,\"x\":1}}"); var (sorted, _) = Rules.SortJsonKeys(node); var s = sorted!.ToJsonString(); Assert.Equal("{\"a\":{\"x\":1,\"y\":2},\"b\":1}", s); } [Fact] public void Normalize_IsIdempotent() { var input = "{\"b\":2.0000001,\"a\":\"2026-04-07T00:00:00Z\",\"id\":\"550e8400-e29b-41d4-a716-446655440000\"}"; var first = Normalizer.Normalize(input, "default"); var second = Normalizer.Normalize(first.Output, "default"); Assert.Equal(first.Output, second.Output); } [Fact] public void Normalize_AppliesAllDefaultRules() { var input = "{\"ts\":\"2026-04-07T00:00:00Z\",\"x\":1.23456789}"; var r = Normalizer.Normalize(input, "default"); Assert.Equal(6, r.Log.Count); var ids = r.Log.Select(l => l.RuleId).ToList(); Assert.Contains("strip_timestamps", ids); Assert.Contains("mask_guids", ids); Assert.Contains("normalize_paths", ids); Assert.Contains("round_floats", ids); Assert.Contains("sort_json_keys", ids); Assert.Contains("mask_volatile_settings", ids); } [Fact] public void Normalize_WritesSidecarLogFile() { var input = "{\"ts\":\"2026-04-07T00:00:00Z\",\"id\":\"550e8400-e29b-41d4-a716-446655440000\",\"GridSnap\":true,\"x\":1.234567}"; var tmp = Path.Combine(Path.GetTempPath(), "norm-sidecar-" + Guid.NewGuid().ToString("N") + ".log"); try { var r = Normalizer.Normalize(input, "default", tmp); Assert.True(File.Exists(tmp), "sidecar file should exist at " + tmp); var text = File.ReadAllText(tmp); // every rule from result.Log should appear with matching count int total = 0; foreach (var entry in r.Log) { Assert.Contains($"{entry.RuleId}\tcount={entry.Count}", text); total += entry.Count; } Assert.Contains($"total={total}", text); // sorted by RuleId var lines = text.Split('\n', StringSplitOptions.RemoveEmptyEntries); var ruleLines = lines.Where(l => !l.StartsWith("total=")).ToList(); var sorted = ruleLines.OrderBy(l => l, StringComparer.Ordinal).ToList(); Assert.Equal(sorted, ruleLines); } finally { if (File.Exists(tmp)) File.Delete(tmp); } } [Fact] public void Normalize_SidecarPath_AcceptsDirectory() { var dir = Path.Combine(Path.GetTempPath(), "norm-sidecar-dir-" + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(dir); try { Normalizer.Normalize("{\"a\":1}", "default", dir); var expected = Path.Combine(dir, "normalization.log"); Assert.True(File.Exists(expected)); } finally { if (Directory.Exists(dir)) Directory.Delete(dir, true); } } }