From ed51e62b292591a37923046a7a85e55735b8b7ae Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Mon, 19 Oct 2026 16:53:58 +0100 Subject: [PATCH] Fix frontmatter parsing for quote workflow Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Models/Billing/FrontmatterOffreResult.cs | 6 +-- .../Services/FrontmatterParser.cs | 24 +++++++---- .../FrontmatterParserTests.cs | 42 +++++++++++++++++++ 3 files changed, 60 insertions(+), 12 deletions(-) create mode 100644 src/test/Yavsc.Server.Tests/FrontmatterParserTests.cs diff --git a/src/Yavsc.Server/Models/Billing/FrontmatterOffreResult.cs b/src/Yavsc.Server/Models/Billing/FrontmatterOffreResult.cs index e7f42698f..e14a153d5 100644 --- a/src/Yavsc.Server/Models/Billing/FrontmatterOffreResult.cs +++ b/src/Yavsc.Server/Models/Billing/FrontmatterOffreResult.cs @@ -2,7 +2,7 @@ namespace Yavsc.Server.Models.Billing; public class FrontmatterOffreResult { - public string Formulaire { get; set; } - public string Devis { get; set; } - public string Corps { get; set; } // le Markdown sans l'en-tĂȘte + public string Formulaire { get; set; } = string.Empty; + public string Devis { get; set; } = string.Empty; + public string Corps { get; set; } = string.Empty; // le Markdown sans l'en-tĂȘte } diff --git a/src/Yavsc.Server/Services/FrontmatterParser.cs b/src/Yavsc.Server/Services/FrontmatterParser.cs index 16b86ad4b..866b1f5ab 100644 --- a/src/Yavsc.Server/Services/FrontmatterParser.cs +++ b/src/Yavsc.Server/Services/FrontmatterParser.cs @@ -6,8 +6,8 @@ namespace Yavsc.Server.Services; public static class FrontmatterParser { - public const string YamlHeader = "---\n"; - public const string YamlSeparator = "\n---"; + public const string YamlHeader = "---"; + public const string YamlSeparator = "---"; private static readonly IDeserializer _deserializer = new DeserializerBuilder() @@ -17,17 +17,23 @@ public static class FrontmatterParser public static FrontmatterOffreResult Parse(string markdown) { - // Le contenu doit commencer par ---- - if (!markdown.TrimStart().StartsWith(YamlHeader, StringComparison.Ordinal)) + ArgumentNullException.ThrowIfNull(markdown); + + var trimmed = markdown.TrimStart(); + + if (!trimmed.StartsWith(YamlHeader, StringComparison.Ordinal)) return new FrontmatterOffreResult { Corps = markdown }; - var parts = markdown.Substring(YamlHeader.Length).Trim().Split(new[] { YamlSeparator }, 2, - StringSplitOptions.None); + var afterOpeningMarker = trimmed[YamlHeader.Length..]; + var parts = afterOpeningMarker + .Split(new[] { "\r\n---", "\n---" }, 2, StringSplitOptions.None); + if (parts.Length < 2) return new FrontmatterOffreResult { Corps = markdown }; - var yamlBlock = parts[0]; - var corps = parts[1]; - var meta = _deserializer.Deserialize(yamlBlock); + + var yamlBlock = parts[0].Trim(); + var corps = parts[1].TrimStart(); + var meta = _deserializer.Deserialize(yamlBlock) ?? new FrontmatterOffreResult(); meta.Corps = corps; return meta; } diff --git a/src/test/Yavsc.Server.Tests/FrontmatterParserTests.cs b/src/test/Yavsc.Server.Tests/FrontmatterParserTests.cs new file mode 100644 index 000000000..95d38bee6 --- /dev/null +++ b/src/test/Yavsc.Server.Tests/FrontmatterParserTests.cs @@ -0,0 +1,42 @@ +using Yavsc.Server.Services; + +namespace Yavsc.Server.Tests; + +public class FrontmatterParserTests +{ + [Fact] + public void Parse_extracts_formulaire_devis_and_body_for_unix_line_endings() + { + const string markdown = "---\nformulaire: Prestation Musicale\ndevis: Arrangement Orchestral\n---\n\nContenu du devis\n"; + + var result = FrontmatterParser.Parse(markdown); + + Assert.Equal("Prestation Musicale", result.Formulaire); + Assert.Equal("Arrangement Orchestral", result.Devis); + Assert.Equal("Contenu du devis", result.Corps.Trim()); + } + + [Fact] + public void Parse_accepts_windows_line_endings() + { + const string markdown = "---\r\nformulaire: Prestation Musicale\r\ndevis: Arrangement Orchestral\r\n---\r\n\r\nContenu du devis\r\n"; + + var result = FrontmatterParser.Parse(markdown); + + Assert.Equal("Prestation Musicale", result.Formulaire); + Assert.Equal("Arrangement Orchestral", result.Devis); + Assert.Equal("Contenu du devis", result.Corps.Trim()); + } + + [Fact] + public void Parse_returns_original_markdown_when_no_frontmatter_is_present() + { + const string markdown = "# Hello\n\nThis is plain markdown."; + + var result = FrontmatterParser.Parse(markdown); + + Assert.Equal(string.Empty, result.Formulaire); + Assert.Equal(string.Empty, result.Devis); + Assert.Equal(markdown, result.Corps); + } +}