From 2f765d0515ba3ef338fd7ffeac9f8d84c6fe3e97 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Sep 2026 02:24:30 +0000 Subject: [PATCH] Keep a lone list property's key when merging properties [patch] MergeArrayValues always wrote the list under its canonical key, even when the group held a single key and nothing was merged, so with FrontmatterNaming.AsIs a lone 'section' list still became 'categories' (and 'keywords' became 'tags'). A lone list now passes through under its own key, the same as a scalar; lists that really are merged still land under the canonical key. Fixes #147 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AFCuHRLN6NWMQ4DJNZApHa --- Frontmatter.Test/ListKeyNamingTests.cs | 50 ++++++++++++++++++++++++++ Frontmatter/PropertyMerger.cs | 5 +-- 2 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 Frontmatter.Test/ListKeyNamingTests.cs diff --git a/Frontmatter.Test/ListKeyNamingTests.cs b/Frontmatter.Test/ListKeyNamingTests.cs new file mode 100644 index 0000000..d0e84a4 --- /dev/null +++ b/Frontmatter.Test/ListKeyNamingTests.cs @@ -0,0 +1,50 @@ +// Copyright (c) 2023-2026 ktsu-dev contributors + +namespace ktsu.Frontmatter.Test; + +using System.Collections.Generic; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +/// +/// Regression tests for #147: merging must not rename a list property, since renaming is the job of +/// , and turns it off. +/// +[TestClass] +public class ListKeyNamingTests +{ + private static readonly string Nl = Environment.NewLine; + + [TestMethod] + [DataRow(FrontmatterMergeStrategy.None)] + [DataRow(FrontmatterMergeStrategy.Conservative)] + [DataRow(FrontmatterMergeStrategy.Aggressive)] + [DataRow(FrontmatterMergeStrategy.Maximum)] + public void CombineFrontmatter_AsIsNaming_LoneListPropertyKeepsItsKey(FrontmatterMergeStrategy strategy) + { + string input = $"---{Nl}section:{Nl} - news{Nl}summary: hi{Nl}---{Nl}Body{Nl}"; + + string result = Frontmatter.CombineFrontmatter(input, FrontmatterNaming.AsIs, FrontmatterOrder.AsIs, strategy); + Dictionary? frontmatter = Frontmatter.ExtractFrontmatter(result); + + Assert.IsNotNull(frontmatter); + Assert.IsTrue(frontmatter.ContainsKey("section"), $"section was renamed: {result}"); + Assert.IsFalse(frontmatter.ContainsKey("categories"), $"section was renamed: {result}"); + } + + [TestMethod] + [DataRow("keywords")] + [DataRow("category")] + public void MergeSimilarProperties_LoneListProperty_KeepsItsKey(string key) + { + Dictionary source = new() + { + [key] = new List { "a", "b" }, + }; + + Dictionary result = PropertyMerger.MergeSimilarProperties(source, FrontmatterMergeStrategy.Conservative); + + Assert.IsTrue(result.ContainsKey(key), $"{key} was renamed to {string.Join(", ", result.Keys)}"); + Assert.AreEqual(1, result.Count); + } +} diff --git a/Frontmatter/PropertyMerger.cs b/Frontmatter/PropertyMerger.cs index baa50d2..0667664 100644 --- a/Frontmatter/PropertyMerger.cs +++ b/Frontmatter/PropertyMerger.cs @@ -144,8 +144,9 @@ private static void MergePropertyGroup( return; } - // Handle array/list types specially - if (firstValue is IList || firstValue is object[]) + // Handle array/list types specially. A lone list has nothing to merge with, so it keeps its own + // key like a scalar does: renaming is NameStandardizer's job, which FrontmatterNaming.AsIs turns off. + if (originalKeys.Count > 1 && (firstValue is IList || firstValue is object[])) { MergeArrayValues(source, target, canonicalKey, originalKeys); }