using System.IO; using System.Text.Json; using AxCopilot.Services.Agent; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Wordprocessing; using FluentAssertions; using Xunit; namespace AxCopilot.Tests.Services; public class DocxSkillStyleMapTests { [Fact] public async Task ExecuteAsync_WithStyleMap_ShouldApplyTemplateParagraphStyles() { var workDir = Path.Combine(Path.GetTempPath(), "ax-docx-stylemap-" + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(workDir); try { var templatePath = Path.Combine(workDir, "style-template.docx"); using (var template = WordprocessingDocument.Create(templatePath, DocumentFormat.OpenXml.WordprocessingDocumentType.Document)) { var mainPart = template.AddMainDocumentPart(); mainPart.Document = new Document(new Body(new Paragraph(new Run(new Text("Template Root"))))); var stylePart = mainPart.AddNewPart(); stylePart.Styles = new Styles( CreateParagraphStyle("CustomTitle"), CreateParagraphStyle("CustomHeading1"), CreateParagraphStyle("CustomHeading2"), CreateParagraphStyle("CustomBody")); stylePart.Styles.Save(); mainPart.Document.Save(); } var tool = new DocxSkill(); var context = new AgentContext { WorkFolder = workDir, Permission = "Auto", OperationMode = "external", }; var args = JsonDocument.Parse( """ { "path": "styled-brief.docx", "title": "Styled Brief", "template_path": "style-template.docx", "style_map": { "title": "CustomTitle", "heading1": "CustomHeading1", "heading2": "CustomHeading2", "body": "CustomBody" }, "sections": [ { "heading": "Executive Summary", "body": "Body line one.\nBody line two.", "level": 1 }, { "heading": "Detail", "body": "Detail text.", "level": 2 } ] } """).RootElement; var result = await tool.ExecuteAsync(args, context, CancellationToken.None); result.Success.Should().BeTrue(); var outputPath = Path.Combine(workDir, "styled-brief.docx"); File.Exists(outputPath).Should().BeTrue(); using var doc = WordprocessingDocument.Open(outputPath, false); var paragraphs = doc.MainDocumentPart!.Document.Body!.Descendants().ToList(); paragraphs.First(paragraph => paragraph.InnerText == "Styled Brief") .ParagraphProperties!.ParagraphStyleId!.Val!.Value.Should().Be("CustomTitle"); paragraphs.First(paragraph => paragraph.InnerText == "Executive Summary") .ParagraphProperties!.ParagraphStyleId!.Val!.Value.Should().Be("CustomHeading1"); paragraphs.First(paragraph => paragraph.InnerText == "Detail") .ParagraphProperties!.ParagraphStyleId!.Val!.Value.Should().Be("CustomHeading2"); paragraphs.First(paragraph => paragraph.InnerText == "Body line one.") .ParagraphProperties!.ParagraphStyleId!.Val!.Value.Should().Be("CustomBody"); } finally { try { if (Directory.Exists(workDir)) Directory.Delete(workDir, true); } catch { } } } private static Style CreateParagraphStyle(string styleId) { return new Style { Type = StyleValues.Paragraph, StyleId = styleId, CustomStyle = true, StyleName = new StyleName { Val = styleId }, }; } }