using System; using System.IO; using System.IO.Compression; using System.Linq; using System.Text; using System.Xml.Linq; using BizTalkApplicationCatalog.Models; using BizTalkApplicationCatalog.Persistence; using BizTalkApplicationCatalog.Reporting; namespace BizTalkApplicationCatalog.Infrastructure { /// /// Prüft Snapshot, Merge und Excel-Ausgabe ohne BizTalk-Zugriff. /// internal static class SelfTestRunner { public static void Run(string outputPath = null) { var keepOutput = !string.IsNullOrWhiteSpace(outputPath); var directory = keepOutput ? Path.GetDirectoryName(Path.GetFullPath(outputPath)) : Path.Combine( Path.GetTempPath(), "BizTalkApplicationCatalogTests-" + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(directory); var path = keepOutput ? Path.GetFullPath(outputPath) : Path.Combine(directory, "phase1-test.xlsx"); var jsonPath = Path.Combine( directory, "phase1-test-" + Guid.NewGuid().ToString("N") + ".json"); try { var acc = AccSnapshot(); SnapshotStore.Save(acc, jsonPath); var loadedAcc = SnapshotStore.Load(jsonPath); SnapshotStore.ValidateForMerge(loadedAcc, "ACC"); Assert( loadedAcc.Applications.Count == 2, "JSON-Roundtrip hat Anwendungen verloren."); var prod = ProdSnapshot(); SnapshotStore.ValidateForMerge(prod, "PROD"); new XlsxReportWriter().Write(new[] { loadedAcc, prod }, path); Assert(File.Exists(path), "XLSX-Datei wurde nicht erzeugt."); using (var archive = ZipFile.OpenRead(path)) { var required = new[] { "[Content_Types].xml", "_rels/.rels", "docProps/core.xml", "docProps/app.xml", "xl/workbook.xml", "xl/_rels/workbook.xml.rels", "xl/styles.xml", "xl/worksheets/sheet1.xml" }; foreach (var name in required) { Assert( archive.GetEntry(name) != null, "XLSX-Part fehlt: " + name); } Assert( archive.GetEntry("xl/worksheets/sheet2.xml") == null, "Die Arbeitsmappe enthält mehr als ein Datenblatt."); foreach (var entry in archive.Entries.Where(item => item.FullName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase) || item.FullName.EndsWith(".rels", StringComparison.OrdinalIgnoreCase))) { using (var stream = entry.Open()) { XDocument.Load(stream); } } var workbook = LoadXml(archive, "xl/workbook.xml"); XNamespace spreadsheet = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"; var names = workbook.Descendants(spreadsheet + "sheet") .Select(item => (string)item.Attribute("name")) .ToList(); Assert( names.SequenceEqual(new[] { "Phase 1" }), "Es wird nicht exakt das Blatt 'Phase 1' erzeugt."); var contentTypes = LoadXml(archive, "[Content_Types].xml"); XNamespace contentTypeNamespace = "http://schemas.openxmlformats.org/package/2006/content-types"; Assert( contentTypes.Root .Elements(contentTypeNamespace + "Override") .Count() == 5, "Content-Type-Overrides sind unvollständig."); var worksheet = LoadXml( archive, "xl/worksheets/sheet1.xml"); Assert( worksheet.Descendants(spreadsheet + "mergeCell") .Select(item => (string)item.Attribute("ref")) .Contains("A1:G1"), "Titelzeile ist nicht zusammengeführt."); Assert( (string)worksheet .Descendants(spreadsheet + "autoFilter") .Single() .Attribute("ref") == "A2:G5", "Filterbereich ist falsch."); string text; using (var reader = new StreamReader( archive.GetEntry("xl/worksheets/sheet1.xml").Open(), Encoding.UTF8)) { text = reader.ReadToEnd(); } Assert(text.Contains("BizTalk-Anwendung in ACC"), "ACC-Spalte fehlt."); Assert(text.Contains("BizTalk-Anwendung in PRD"), "PRD-Spalte fehlt."); Assert(text.Contains("FILE (1x) / SFTP (1x)"), "Adapteraggregation ist falsch."); Assert(text.Contains("Nur in ACC vorhanden"), "ACC-Hinweis fehlt."); Assert(text.Contains("Nur in PRD vorhanden"), "PRD-Hinweis fehlt."); Assert(text.Contains("Gesamt: 3 BizTalk-Anwendungen"), "Summenzeile ist falsch."); } } finally { try { if (File.Exists(jsonPath)) File.Delete(jsonPath); if (!keepOutput && Directory.Exists(directory)) { Directory.Delete(directory, true); } } catch (IOException) { } catch (UnauthorizedAccessException) { } } } private static CatalogSnapshot AccSnapshot() { var snapshot = Snapshot("ACC", "BIZTALK-ACC"); snapshot.Applications.Add(Application( "OrderProcessing", new AdapterCount { AdapterType = "FILE", Count = 1 }, new AdapterCount { AdapterType = "SFTP", Count = 1 })); snapshot.Applications.Add(Application( "AccOnly", new AdapterCount { AdapterType = "FILE", Count = 1 })); return snapshot; } private static CatalogSnapshot ProdSnapshot() { var snapshot = Snapshot("PROD", "BIZTALK-PROD"); snapshot.Applications.Add(Application( "OrderProcessing", new AdapterCount { AdapterType = "FILE", Count = 1 }, new AdapterCount { AdapterType = "SFTP", Count = 1 })); snapshot.Applications.Add(Application( "ProdOnly", new AdapterCount { AdapterType = "SFTP", Count = 1 })); return snapshot; } private static CatalogSnapshot Snapshot( string environment, string computer) { return new CatalogSnapshot { EnvironmentName = environment, ComputerName = computer, CreatedUtc = DateTime.UtcNow.ToString("o"), IsComplete = true }; } private static CatalogApplication Application( string name, params AdapterCount[] adapters) { var result = new CatalogApplication { Name = name, EndpointCount = adapters.Sum(item => item.Count) }; result.AdapterCounts.AddRange(adapters); return result; } private static XDocument LoadXml(ZipArchive archive, string name) { using (var stream = archive.GetEntry(name).Open()) { return XDocument.Load(stream); } } private static void Assert(bool condition, string message) { if (!condition) throw new InvalidOperationException(message); } } }