diff --git a/src/LogExpert.Core/Config/Preferences.cs b/src/LogExpert.Core/Config/Preferences.cs index 8673acfa..a8aaa13d 100644 --- a/src/LogExpert.Core/Config/Preferences.cs +++ b/src/LogExpert.Core/Config/Preferences.cs @@ -26,11 +26,11 @@ public class Preferences /// Will be removed in a future version once migration period is complete. /// [Obsolete("This property exists only for backward compatibility with old settings files. Use HighlightGroupList instead.")] - [Newtonsoft.Json.JsonProperty("hilightGroupList")] - [System.Text.Json.Serialization.JsonPropertyName("hilightGroupList")] + [Newtonsoft.Json.JsonProperty("hilightGroupList", DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.Ignore, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.Text.Json.Serialization.JsonIgnore] public List HilightGroupList { - get => HighlightGroupList; + get => null; // Always return null so Newtonsoft.Json won't serialize this property set => HighlightGroupList = value ?? []; } diff --git a/src/LogExpert.Core/Config/Settings.cs b/src/LogExpert.Core/Config/Settings.cs index 5921663e..990f5389 100644 --- a/src/LogExpert.Core/Config/Settings.cs +++ b/src/LogExpert.Core/Config/Settings.cs @@ -42,9 +42,8 @@ public class Settings /// Will be removed in a future version once migration period is complete. /// [Obsolete("This property exists only for backward compatibility with old settings files. Data is stored in Preferences.HighlightGroupList.")] - [Newtonsoft.Json.JsonProperty("hilightEntryList")] - [System.Text.Json.Serialization.JsonPropertyName("hilightEntryList")] - [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)] + [Newtonsoft.Json.JsonProperty("hilightEntryList", DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.Ignore, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.Text.Json.Serialization.JsonIgnore] public List HilightEntryList { get => null; // Never serialize this @@ -61,9 +60,8 @@ public List HilightEntryList /// Will be removed in a future version once migration period is complete. /// [Obsolete("This property exists only for backward compatibility with old settings files. Data is stored in Preferences.HighlightGroupList.")] - [Newtonsoft.Json.JsonProperty("hilightGroupList")] - [System.Text.Json.Serialization.JsonPropertyName("hilightGroupList")] - [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)] + [Newtonsoft.Json.JsonProperty("hilightGroupList", DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.Ignore, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.Text.Json.Serialization.JsonIgnore] public List HilightGroupList { get => null; // Never serialize this diff --git a/src/LogExpert.Core/Entities/HighlightGroup.cs b/src/LogExpert.Core/Entities/HighlightGroup.cs index 3b244792..62c41fac 100644 --- a/src/LogExpert.Core/Entities/HighlightGroup.cs +++ b/src/LogExpert.Core/Entities/HighlightGroup.cs @@ -26,11 +26,11 @@ public class HighlightGroup : ICloneable /// Will be removed in a future version once migration period is complete. /// [Obsolete("This property exists only for backward compatibility with old settings files. Use HighlightEntryList instead.")] - [Newtonsoft.Json.JsonProperty("hilightEntryList")] - [System.Text.Json.Serialization.JsonPropertyName("hilightEntryList")] + [Newtonsoft.Json.JsonProperty("hilightEntryList", DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.Ignore, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.Text.Json.Serialization.JsonIgnore] public List HilightEntryList { - get => HighlightEntryList; + get => null; // Always return null so Newtonsoft.Json won't serialize this property set => HighlightEntryList = value ?? []; } diff --git a/src/LogExpert.Tests/ConfigManagerTest.cs b/src/LogExpert.Tests/ConfigManagerTest.cs index 0c835a0f..845b5489 100644 --- a/src/LogExpert.Tests/ConfigManagerTest.cs +++ b/src/LogExpert.Tests/ConfigManagerTest.cs @@ -975,4 +975,105 @@ public void BackupFile_AlwaysValid_WhenExists () } #endregion + + #region Backward Compatibility Tests + + [Test] + [Category("BackwardCompatibility")] + [Description("LoadOrCreateNew should successfully load old JSON with 'hilightGroupList' typo")] + public void LoadOrCreateNew_LegacyHilightGroupList_LoadsSuccessfully () + { + // Arrange - Create JSON with old "hilightGroupList" property name (with typo) + string legacyJson = @"{ + ""Preferences"": { + ""hilightGroupList"": [ + { + ""GroupName"": ""LegacyGroup"", + ""hilightEntryList"": [] + } + ], + ""FontName"": ""Courier New"", + ""FontSize"": 9 + }, + ""FilterList"": [], + ""SearchHistoryList"": [] +}"; + File.WriteAllText(_testSettingsFile.FullName, legacyJson); + + // Act + LoadResult loadResult = InvokePrivateInstanceMethod("LoadOrCreateNew", _testSettingsFile); + + // Assert + Assert.That(loadResult, Is.Not.Null); + Assert.That(loadResult.Settings, Is.Not.Null); + Assert.That(loadResult.Settings.Preferences.HighlightGroupList.Count, Is.EqualTo(1), "Should load legacy 'hilightGroupList' into HighlightGroupList"); + Assert.That(loadResult.Settings.Preferences.HighlightGroupList[0].GroupName, Is.EqualTo("LegacyGroup")); + } + + [Test] + [Category("BackwardCompatibility")] + [Description("SaveAsJSON should not write the obsolete 'hilightGroupList' property")] + public void SaveAsJSON_DoesNotWriteLegacyProperty () + { + // Arrange + Settings settings = CreateTestSettings(); + settings.Preferences.HighlightGroupList.Add(new HighlightGroup { GroupName = "TestGroup" }); + + // Act + InvokePrivateInstanceMethod("SaveAsJSON", _testSettingsFile, settings); + + // Assert + string json = File.ReadAllText(_testSettingsFile.FullName); + + // Should contain the new property name + Assert.That(json, Does.Contain("HighlightGroupList"), + "Should write 'HighlightGroupList' property"); + + // Should NOT contain the legacy property name + Assert.That(json, Does.Not.Contain("hilightGroupList"), + "Should NOT write obsolete 'hilightGroupList' property"); + + // Verify the content is correct + Assert.That(json, Does.Contain("TestGroup")); + } + + [Test] + [Category("BackwardCompatibility")] + [Description("Old JSON with both properties should not create duplicates")] + public void LoadOrCreateNew_BothPropertiesInJSON_NoDuplicates () + { + // Arrange - Create JSON with BOTH property names (simulating a corrupted file) + string jsonWithBoth = @"{ + ""Preferences"": { + ""HighlightGroupList"": [ + { + ""GroupName"": ""Group1"", + ""HighlightEntryList"": [] + } + ], + ""hilightGroupList"": [ + { + ""GroupName"": ""Group1"", + ""hilightEntryList"": [] + } + ], + ""FontName"": ""Courier New"", + ""FontSize"": 9 + }, + ""FilterList"": [], + ""SearchHistoryList"": [] +}"; + File.WriteAllText(_testSettingsFile.FullName, jsonWithBoth); + + // Act + LoadResult loadResult = InvokePrivateInstanceMethod("LoadOrCreateNew", _testSettingsFile); + + // Assert + Assert.That(loadResult, Is.Not.Null); + Assert.That(loadResult.Settings, Is.Not.Null); + Assert.That(loadResult.Settings.Preferences.HighlightGroupList.Count, Is.EqualTo(1), "Should have exactly 1 group, not duplicates"); + Assert.That(loadResult.Settings.Preferences.HighlightGroupList[0].GroupName, Is.EqualTo("Group1")); + } + + #endregion } diff --git a/src/PluginRegistry/PluginHashGenerator.Generated.cs b/src/PluginRegistry/PluginHashGenerator.Generated.cs index b1e7657f..c748f387 100644 --- a/src/PluginRegistry/PluginHashGenerator.Generated.cs +++ b/src/PluginRegistry/PluginHashGenerator.Generated.cs @@ -10,7 +10,7 @@ public static partial class PluginValidator { /// /// Gets pre-calculated SHA256 hashes for built-in plugins. - /// Generated: 2026-01-22 15:36:59 UTC + /// Generated: 2026-01-23 15:56:33 UTC /// Configuration: Release /// Plugin count: 22 /// @@ -18,28 +18,28 @@ public static Dictionary GetBuiltInPluginHashes() { return new Dictionary(StringComparer.OrdinalIgnoreCase) { - ["AutoColumnizer.dll"] = "7BC68D6CAED29AA844E0C3D0EEF354DDD4CBFC4468343F060D6E09D80DC90B95", + ["AutoColumnizer.dll"] = "1D07B11AE3270F10155D1D6AA54FE70ED0080415E43A1F2B2A0EF72875DA84AF", ["BouncyCastle.Cryptography.dll"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", ["BouncyCastle.Cryptography.dll (x86)"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", - ["CsvColumnizer.dll"] = "D67A6A7B0EAB65B5C352EAED26B89104BC057612279ECD7ADD34BBEE3830019E", - ["CsvColumnizer.dll (x86)"] = "D67A6A7B0EAB65B5C352EAED26B89104BC057612279ECD7ADD34BBEE3830019E", - ["DefaultPlugins.dll"] = "E0503BC7A1CE12E8F18F058C62AB84C2402733E76F9E871C163A3A7900841B5A", - ["FlashIconHighlighter.dll"] = "32510C6566AA4321EFD39190471F9622EED1934D4E415E73D176A150CD4963B5", - ["GlassfishColumnizer.dll"] = "D504BFF6EC14F54C073BF23778C8BB0513F6385B21A10363D6C4415E18ED724A", - ["JsonColumnizer.dll"] = "2C786E6C1E9917EDEB224720C8B4AC7F322A0F9489C07E44F5F62AB887801E79", - ["JsonCompactColumnizer.dll"] = "63E5AE492888DF52C0B5F4584F6CDD35FA8B2DDF8192C75A9DD662C2C4FEDD96", - ["Log4jXmlColumnizer.dll"] = "3E85454E4CFD2563F77B26A3BD7E1A6F85D6B7C0E96338B3FFED1B8BC472C8D7", - ["LogExpert.Core.dll"] = "B8977B248A9A11632B9632D2905A364F98F8625391DDCA40F76CF8CDA185EAE6", - ["LogExpert.Resources.dll"] = "43507497CCBECB8E9A3285A7E837A4B8C88679FC030CF4AA7B2611B203A3AB9C", + ["CsvColumnizer.dll"] = "4DDCDD5E767C265AE31FF09DE7C6A093DC66979CB8EC2D9A8CBCB9A79096ED51", + ["CsvColumnizer.dll (x86)"] = "4DDCDD5E767C265AE31FF09DE7C6A093DC66979CB8EC2D9A8CBCB9A79096ED51", + ["DefaultPlugins.dll"] = "9F2745A5376897CA4436C6A90B9B2BB0A29F4225F81BF95C0B868E66DFBB8D3C", + ["FlashIconHighlighter.dll"] = "D7D008CF7945D67C790BCC17649F01810B1B2F243483DBBAE08B653A9B62FBA4", + ["GlassfishColumnizer.dll"] = "E25C288D6C13B1B80A58EC25906D9BEBAC5C5B46AB98F28B6C4C9E90401F9E40", + ["JsonColumnizer.dll"] = "A74907FDB01478D84F89C8AEB1DD83B4A6F2FDF884760296EDCBD2BA3D46FF1B", + ["JsonCompactColumnizer.dll"] = "AEF65E2C43EE39EE7DC80FBD8D8F499E6E215D6839FF6BDA7CED98C6312DB5BA", + ["Log4jXmlColumnizer.dll"] = "F85079FDA7BB9F8837AA6F5A6449B5F222552B77886F78F3A16A125E4EDD288C", + ["LogExpert.Core.dll"] = "CACF8255B06CB4AF43595196419192CF8BC2CB630456F15E6BDBC7292AC8A86E", + ["LogExpert.Resources.dll"] = "ABA39DD2544E6F8D357008649500B8AEDC88BD233EC3ADEE9304035EB9FD1853", ["Microsoft.Extensions.DependencyInjection.Abstractions.dll"] = "67FA4325000DB017DC0C35829B416F024F042D24EFB868BCF17A895EE6500A93", ["Microsoft.Extensions.DependencyInjection.Abstractions.dll (x86)"] = "67FA4325000DB017DC0C35829B416F024F042D24EFB868BCF17A895EE6500A93", ["Microsoft.Extensions.Logging.Abstractions.dll"] = "BB853130F5AFAF335BE7858D661F8212EC653835100F5A4E3AA2C66A4D4F685D", ["Microsoft.Extensions.Logging.Abstractions.dll (x86)"] = "BB853130F5AFAF335BE7858D661F8212EC653835100F5A4E3AA2C66A4D4F685D", - ["RegexColumnizer.dll"] = "0E6C500586C79D0280973B5D2A69AC0DB16FCF1AB963C6016965AE6CE3DB1103", - ["SftpFileSystem.dll"] = "A2C0E76211A0DE50F0C9B2B9BD1B97519ED390382179ACB73AED232245F42D2B", - ["SftpFileSystem.dll (x86)"] = "453EA3A17F9F6CAE24C76C6368264C0FA6745DC0FC8A2E6DAD86B127227A02E5", - ["SftpFileSystem.Resources.dll"] = "A1185922C0FF4ED0E8CE734FDA6B238ADE7C4DD4C8DC3811C41F618FCD4D4C5E", - ["SftpFileSystem.Resources.dll (x86)"] = "A1185922C0FF4ED0E8CE734FDA6B238ADE7C4DD4C8DC3811C41F618FCD4D4C5E", + ["RegexColumnizer.dll"] = "F44BF1FFF92E4D96C66E0B518D3E276BFE92E7DDB9EB0F93479ABC8EBE072CE2", + ["SftpFileSystem.dll"] = "1971EE85E9CEDDD0A38E846A3EC78F42D823B1DD1CA1A02B0A132B436FC1B32D", + ["SftpFileSystem.dll (x86)"] = "46D0701DB009188D5FADF47FDB54A5ADC85DB39D73CCE40C7823059CF9F19872", + ["SftpFileSystem.Resources.dll"] = "92C1963B793F646215C33E7B3E459564E0EDB6193D305739AEB871092572F318", + ["SftpFileSystem.Resources.dll (x86)"] = "92C1963B793F646215C33E7B3E459564E0EDB6193D305739AEB871092572F318", }; }