using System.IO.Compression; using System.Xml.Linq; using LehrerApp.Core.Services; using Xunit; namespace LehrerApp.Tests; public sealed class LetterTemplateServiceTests : IDisposable { private static readonly XNamespace W = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; private readonly string _directory = Path.Combine(Path.GetTempPath(), $"lehrerapp-letter-tests-{Guid.NewGuid():N}"); public LetterTemplateServiceTests() => Directory.CreateDirectory(_directory); [Fact] public void Validate_OhneInhaltssteuerelement_Warnt() { var path = CreateDocx("Brief"); var result = new LetterTemplateService(_directory).Validate(path); Assert.Contains(result.Issues, i => i.Severity == TemplateIssueSeverity.Warning && i.Message.Contains("keine Inhaltssteuerelemente")); } [Fact] public void Validate_UnbekannterTag_Warnt() { var path = CreateDocx(Control("Something.EntirelyUnknown")); var result = new LetterTemplateService(_directory).Validate(path); Assert.Contains(result.Issues, i => i.Tag == "Something.EntirelyUnknown" && i.Severity == TemplateIssueSeverity.Warning); } [Fact] public void Validate_WahrscheinlicherTippfehler_WarntDeutlichMitVorschlag() { var path = CreateDocx(Control("Contact.Adress")); var result = new LetterTemplateService(_directory).Validate(path); var issue = Assert.Single(result.Issues); Assert.Equal(TemplateIssueSeverity.StrongWarning, issue.Severity); Assert.Equal("Contact.Address", issue.SuggestedTag); Assert.Contains("Meinten Sie", issue.Message); } [Fact] public void Validate_FindetTagsInKopfzeile() { var path = CreateDocx(Control("Student.FirstName"), Control("CurrentDate")); var result = new LetterTemplateService(_directory).Validate(path); Assert.Contains("Student.FirstName", result.Tags); Assert.Contains("CurrentDate", result.Tags); Assert.Empty(result.Issues); } [Fact] public void Generate_BefuelltTextUndMehrzeiligeAdresse_OhneVorlageZuVeraendern() { var path = CreateDocx(Control("Student.FirstName") + Control("Contact.Address")); var original = File.ReadAllBytes(path); var output = Path.Combine(_directory, "filled.docx"); var service = new LetterTemplateService(_directory); service.Generate(path, output, new Dictionary { ["Student.FirstName"] = "Mara", ["Contact.Address"] = "Hauptstraße 1\n12345 Musterstadt", }); Assert.Equal(original, File.ReadAllBytes(path)); using var archive = ZipFile.OpenRead(output); var entry = archive.GetEntry("word/document.xml")!; using var reader = new StreamReader(entry.Open()); var xml = XDocument.Parse(reader.ReadToEnd()); Assert.Contains("Mara", xml.Descendants(W + "t").Select(t => t.Value)); Assert.Single(xml.Descendants(W + "br")); Assert.Contains("12345 Musterstadt", xml.Descendants(W + "t").Select(t => t.Value)); } [Fact] public void Import_KopiertVorlageUndLoeschenEntferntSie() { var path = CreateDocx(Control("CurrentDate")); var service = new LetterTemplateService(Path.Combine(_directory, "appdata")); var imported = service.Import(path, "Elternbrief"); Assert.Equal("Elternbrief", Assert.Single(service.GetTemplates()).Name); Assert.True(File.Exists(service.GetTemplatePath(imported))); service.Delete(imported.Id); Assert.Empty(service.GetTemplates()); Assert.False(File.Exists(service.GetTemplatePath(imported))); } private string CreateDocx(string body, string? header = null) { var path = Path.Combine(_directory, $"{Guid.NewGuid():N}.docx"); using var archive = ZipFile.Open(path, ZipArchiveMode.Create); AddXml(archive, "word/document.xml", body); if (header is not null) AddXml(archive, "word/header1.xml", header); return path; } private static void AddXml(ZipArchive archive, string name, string content) { var entry = archive.CreateEntry(name); using var writer = new StreamWriter(entry.Open()); writer.Write($"{content}"); } private static string Control(string tag) => $"" + "Platzhalter" + ""; public void Dispose() { if (Directory.Exists(_directory)) Directory.Delete(_directory, recursive: true); } }