Initial commit: BizTalk application catalog

This commit is contained in:
2026-07-27 14:46:02 +02:00
commit 2b5b97c869
25 changed files with 3026 additions and 0 deletions
@@ -0,0 +1,190 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using BizTalkApplicationCatalog.Models;
using BizTalkApplicationCatalog.Reporting;
namespace BizTalkApplicationCatalog.Infrastructure
{
/// <summary>
/// Prüft die zentrale Berichtslogik ohne Zugriff auf Windows- oder BizTalk-WMI.
/// </summary>
internal static class SelfTestRunner
{
public static void Run(string outputPath = null)
{
Assert(SensitiveDataSanitizer.Sanitize(
"https://host/path?password=secret&client=100")
== "https://host/path?password=[REDACTED]&client=100",
"Secret-Redaktion ist fehlerhaft.");
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, "test.xlsx");
try
{
var document = SampleDocument();
new XlsxReportWriter().Write(document, 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"
};
foreach (var name in required)
{
Assert(archive.GetEntry(name) != null, "XLSX-Part fehlt: " + name);
}
for (var index = 1; index <= 10; index++)
{
Assert(
archive.GetEntry("xl/worksheets/sheet" + index + ".xml") != null,
"Arbeitsblatt fehlt: " + index);
}
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.Contains("Anwendungen"), "Anwendungsblatt fehlt.");
Assert(names.Contains("Abdeckung"), "Abdeckungsblatt fehlt.");
var contentTypes = LoadXml(archive, "[Content_Types].xml");
XNamespace contentTypeNamespace =
"http://schemas.openxmlformats.org/package/2006/content-types";
Assert(
contentTypes.Root.Elements(contentTypeNamespace + "Override").Count() == 14,
"Content-Type-Overrides sind unvollständig oder im falschen Namespace.");
var relationships = LoadXml(archive, "_rels/.rels");
XNamespace relationshipNamespace =
"http://schemas.openxmlformats.org/package/2006/relationships";
Assert(
relationships.Root.Elements(relationshipNamespace + "Relationship").Count() == 3,
"Paketbeziehungen sind unvollständig oder im falschen Namespace.");
var combinedText = new StringBuilder();
foreach (var entry in archive.Entries.Where(item =>
item.FullName.StartsWith("xl/worksheets/", StringComparison.Ordinal)))
{
using (var reader = new StreamReader(entry.Open(), Encoding.UTF8))
{
combinedText.Append(reader.ReadToEnd());
}
}
Assert(!combinedText.ToString().Contains("supersecret"), "XLSX enthält Testkennwort.");
Assert(combinedText.ToString().Contains("[REDACTED]"), "Redaktionsmarker fehlt.");
}
}
finally
{
try
{
if (!keepOutput && Directory.Exists(directory)) Directory.Delete(directory, true);
}
catch (IOException)
{
}
catch (UnauthorizedAccessException)
{
}
}
}
private static InventoryDocument SampleDocument()
{
var document = new InventoryDocument
{
EnvironmentName = "ACC",
ComputerName = "BIZTALK-ACC",
ToolVersion = "1.0.0.0",
ManagementServer = "SQL-ACC",
ManagementDatabase = "BizTalkMgmtDb",
StartedUtc = DateTime.UtcNow.AddSeconds(-1),
CompletedUtc = DateTime.UtcNow
};
document.SystemProperties.Add(new NameValueRecord(
"Betriebssystem", "Windows Server 2019", "Self-Test"));
document.Applications.Add(new ApplicationRecord
{
Name = "OrderProcessing",
Description = "Aufträge & Sonderzeichen <Test>",
Status = "Gestartet (2)",
IsDefault = "Nein",
Source = "Self-Test"
});
var port = new ArtifactRecord
{
Type = "Send Port",
ApplicationName = "OrderProcessing",
Name = "Send_Orders",
Status = "Gestartet (2)",
HostName = "SendHost",
AdapterName = "WCF-Custom",
Address = SensitiveDataSanitizer.Sanitize(
"https://service/orders?password=supersecret")
};
port.Properties.Add(new NameValueRecord("IsTwoWay", "True", "Self-Test"));
document.Artifacts.Add(port);
document.Coverage.Add(new CoverageRecord
{
DataSource = "MSBTS_Application",
Status = "Vollständig",
RowCount = 1,
Required = "Ja",
Message = "Self-Test"
});
document.SectionStatuses.Add(new SectionStatus
{
Name = "Self-Test",
Status = "Erfolgreich",
Message = "OK",
Required = true,
DurationMilliseconds = 1
});
return document;
}
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);
}
}
}