From ea328e65bdb8c65a989f00b6c4879613a7604edc Mon Sep 17 00:00:00 2001 From: Frank Tovar Date: Sun, 15 Mar 2026 13:08:00 +0100 Subject: [PATCH] Fix false editor compare warnings --- .../Shared/CriticalCellEditorDialog.razor | 80 ++++------------- .../CriticalCellComparisonEvaluator.cs | 80 +++++++++++++++++ .../CriticalCellComparisonEvaluatorTests.cs | 90 +++++++++++++++++++ .../RolemasterDb.ImportTool.Tests.csproj | 1 + 4 files changed, 186 insertions(+), 65 deletions(-) create mode 100644 src/RolemasterDb.App/Features/CriticalCellComparisonEvaluator.cs create mode 100644 src/RolemasterDb.ImportTool.Tests/CriticalCellComparisonEvaluatorTests.cs diff --git a/src/RolemasterDb.App/Components/Shared/CriticalCellEditorDialog.razor b/src/RolemasterDb.App/Components/Shared/CriticalCellEditorDialog.razor index ec210f2..d4c09a1 100644 --- a/src/RolemasterDb.App/Components/Shared/CriticalCellEditorDialog.razor +++ b/src/RolemasterDb.App/Components/Shared/CriticalCellEditorDialog.razor @@ -667,26 +667,29 @@ return ["Generated comparison is unavailable."]; } + var comparisonSourceModel = GetComparisonSourceModel(model, comparisonBaseline); + var comparisonSourceEffects = BuildPreviewEffects(comparisonSourceModel); + var comparisonSourceBranches = BuildPreviewBranches(comparisonSourceModel); var items = new List(); - if (DescriptionDiffers(model, comparisonBaseline)) + if (CriticalCellComparisonEvaluator.DescriptionDiffers(comparisonSourceModel.DescriptionText, model.GeneratedState.DescriptionText)) { items.Add("Result text differs"); } - if (EffectsDiffer(model, comparisonBaseline)) + if (CriticalCellComparisonEvaluator.EffectsDiffer(comparisonSourceEffects, model.GeneratedState.Effects)) { items.Add("Base effects differ"); } - if (BranchesDiffer(model, comparisonBaseline)) + if (CriticalCellComparisonEvaluator.BranchesDiffer(comparisonSourceBranches, model.GeneratedState.Branches)) { items.Add("Conditions differ"); } if (items.Count == 0) { - items.Add("Current card matches the fresh parse"); + items.Add("Current card matches the fresh generation"); } if (model.GeneratedState.ValidationMessages.Count > 0) @@ -699,75 +702,22 @@ private static int GetComparisonDifferenceCount(CriticalCellEditorModel model, CriticalCellEditorModel? comparisonBaseline) { - var count = 0; - - if (DescriptionDiffers(model, comparisonBaseline)) + if (model.GeneratedState is null) { - count++; + return 0; } - if (EffectsDiffer(model, comparisonBaseline)) - { - count++; - } - - if (BranchesDiffer(model, comparisonBaseline)) - { - count++; - } - - return count; + var comparisonSourceModel = GetComparisonSourceModel(model, comparisonBaseline); + return CriticalCellComparisonEvaluator.GetDifferenceCount( + comparisonSourceModel.DescriptionText, + BuildPreviewEffects(comparisonSourceModel), + BuildPreviewBranches(comparisonSourceModel), + model.GeneratedState); } private static bool HasComparisonDifferences(CriticalCellEditorModel? model, CriticalCellEditorModel? comparisonBaseline) => model is not null && GetComparisonDifferenceCount(model, comparisonBaseline) > 0; - private static bool DescriptionDiffers(CriticalCellEditorModel model, CriticalCellEditorModel? comparisonBaseline) => - model.GeneratedState is not null && - !string.Equals( - NormalizeDisplayText(GetComparisonSourceModel(model, comparisonBaseline).DescriptionText), - NormalizeDisplayText(model.GeneratedState.DescriptionText), - StringComparison.Ordinal); - - private static bool EffectsDiffer(CriticalCellEditorModel model, CriticalCellEditorModel? comparisonBaseline) => - model.GeneratedState is not null && - SerializeComparisonValue(BuildPreviewEffects(GetComparisonSourceModel(model, comparisonBaseline)).Select(ProjectEffectForComparison).ToList()) != - SerializeComparisonValue(model.GeneratedState.Effects.Select(ProjectEffectForComparison).ToList()); - - private static bool BranchesDiffer(CriticalCellEditorModel model, CriticalCellEditorModel? comparisonBaseline) => - model.GeneratedState is not null && - SerializeComparisonValue(BuildPreviewBranches(GetComparisonSourceModel(model, comparisonBaseline)).Select(ProjectBranchForComparison).ToList()) != - SerializeComparisonValue(model.GeneratedState.Branches.Select(ProjectBranchForComparison).ToList()); - - private static string NormalizeDisplayText(string? value) => - value?.Trim() ?? string.Empty; - - private static string SerializeComparisonValue(TValue value) => - JsonSerializer.Serialize(value, DiagnosticJsonOptions); - - private static object ProjectEffectForComparison(CriticalEffectLookupResponse effect) => new - { - effect.EffectCode, - effect.Target, - effect.ValueInteger, - effect.ValueExpression, - effect.DurationRounds, - effect.PerRound, - effect.Modifier, - effect.BodyPart, - effect.IsPermanent, - SourceText = NormalizeDisplayText(effect.SourceText) - }; - - private static object ProjectBranchForComparison(CriticalBranchLookupResponse branch) => new - { - Condition = NormalizeDisplayText(branch.ConditionText), - Description = NormalizeDisplayText(branch.Description), - Effects = branch.Effects - .Select(ProjectEffectForComparison) - .ToList() - }; - private static CriticalCellEditorModel GetComparisonSourceModel( CriticalCellEditorModel model, CriticalCellEditorModel? comparisonBaseline) => diff --git a/src/RolemasterDb.App/Features/CriticalCellComparisonEvaluator.cs b/src/RolemasterDb.App/Features/CriticalCellComparisonEvaluator.cs new file mode 100644 index 0000000..d782ca4 --- /dev/null +++ b/src/RolemasterDb.App/Features/CriticalCellComparisonEvaluator.cs @@ -0,0 +1,80 @@ +using System.Text.Json; + +namespace RolemasterDb.App.Features; + +public static class CriticalCellComparisonEvaluator +{ + private static readonly JsonSerializerOptions ComparisonJsonOptions = new(JsonSerializerDefaults.Web); + + public static int GetDifferenceCount( + string? currentDescription, + IReadOnlyList currentEffects, + IReadOnlyList currentBranches, + CriticalCellComparisonState generatedState) + { + var count = 0; + + if (DescriptionDiffers(currentDescription, generatedState.DescriptionText)) + { + count++; + } + + if (EffectsDiffer(currentEffects, generatedState.Effects)) + { + count++; + } + + if (BranchesDiffer(currentBranches, generatedState.Branches)) + { + count++; + } + + return count; + } + + public static bool DescriptionDiffers(string? currentDescription, string? generatedDescription) => + !string.Equals( + NormalizeText(currentDescription), + NormalizeText(generatedDescription), + StringComparison.Ordinal); + + public static bool EffectsDiffer( + IReadOnlyList currentEffects, + IReadOnlyList generatedEffects) => + SerializeComparisonValue(currentEffects.Select(ProjectEffectForComparison).ToList()) != + SerializeComparisonValue(generatedEffects.Select(ProjectEffectForComparison).ToList()); + + public static bool BranchesDiffer( + IReadOnlyList currentBranches, + IReadOnlyList generatedBranches) => + SerializeComparisonValue(currentBranches.Select(ProjectBranchForComparison).ToList()) != + SerializeComparisonValue(generatedBranches.Select(ProjectBranchForComparison).ToList()); + + private static string NormalizeText(string? value) => + value?.Trim() ?? string.Empty; + + private static string SerializeComparisonValue(TValue value) => + JsonSerializer.Serialize(value, ComparisonJsonOptions); + + private static object ProjectEffectForComparison(CriticalEffectLookupResponse effect) => new + { + effect.EffectCode, + Target = NormalizeText(effect.Target), + effect.ValueInteger, + ValueExpression = NormalizeText(effect.ValueExpression), + effect.DurationRounds, + effect.PerRound, + effect.Modifier, + BodyPart = NormalizeText(effect.BodyPart), + effect.IsPermanent + }; + + private static object ProjectBranchForComparison(CriticalBranchLookupResponse branch) => new + { + Condition = NormalizeText(branch.ConditionText), + Description = NormalizeText(branch.Description), + Effects = branch.Effects + .Select(ProjectEffectForComparison) + .ToList() + }; +} diff --git a/src/RolemasterDb.ImportTool.Tests/CriticalCellComparisonEvaluatorTests.cs b/src/RolemasterDb.ImportTool.Tests/CriticalCellComparisonEvaluatorTests.cs new file mode 100644 index 0000000..b30df91 --- /dev/null +++ b/src/RolemasterDb.ImportTool.Tests/CriticalCellComparisonEvaluatorTests.cs @@ -0,0 +1,90 @@ +using RolemasterDb.App.Domain; +using RolemasterDb.App.Features; + +namespace RolemasterDb.ImportTool.Tests; + +public sealed class CriticalCellComparisonEvaluatorTests +{ + [Fact] + public void Effects_do_not_differ_when_only_source_metadata_changes() + { + var currentEffects = new[] + { + new CriticalEffectLookupResponse( + CriticalEffectCodes.MustParryRounds, + "foe", + null, + null, + 1, + null, + null, + null, + false, + "symbol", + "π") + }; + var generatedEffects = new[] + { + new CriticalEffectLookupResponse( + CriticalEffectCodes.MustParryRounds, + "foe", + null, + null, + 1, + null, + null, + null, + false, + "quick", + "1mp") + }; + + Assert.False(CriticalCellComparisonEvaluator.EffectsDiffer(currentEffects, generatedEffects)); + } + + [Fact] + public void Difference_count_detects_actual_visible_effect_changes() + { + var currentEffects = new[] + { + new CriticalEffectLookupResponse( + CriticalEffectCodes.DirectHits, + "foe", + 5, + null, + null, + null, + null, + null, + false, + "import", + "+5") + }; + var generatedState = new CriticalCellComparisonState( + "Arcane force staggers the foe.", + [ + new CriticalEffectLookupResponse( + CriticalEffectCodes.DirectHits, + "foe", + 10, + null, + null, + null, + null, + null, + false, + "quick", + "+10") + ], + [], + []); + + var differenceCount = CriticalCellComparisonEvaluator.GetDifferenceCount( + "Arcane force staggers the foe.", + currentEffects, + [], + generatedState); + + Assert.Equal(1, differenceCount); + } +} diff --git a/src/RolemasterDb.ImportTool.Tests/RolemasterDb.ImportTool.Tests.csproj b/src/RolemasterDb.ImportTool.Tests/RolemasterDb.ImportTool.Tests.csproj index 975741f..116bb5e 100644 --- a/src/RolemasterDb.ImportTool.Tests/RolemasterDb.ImportTool.Tests.csproj +++ b/src/RolemasterDb.ImportTool.Tests/RolemasterDb.ImportTool.Tests.csproj @@ -19,6 +19,7 @@ +