using System.Text; namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.TestSupport; /// /// Aggregated result of an run. Test fixtures /// typically call to produce the argument for xUnit's /// Assert.Skip when any hard dependency failed. /// public sealed class PrerequisiteReport { public IReadOnlyList Checks { get; } public PrerequisiteReport(IEnumerable checks) { Checks = [.. checks]; } /// True when every probe is Pass / Warn / Skip — no Fail entries. public bool IsLivetestReady => !Checks.Any(c => c.Status == PrerequisiteStatus.Fail); /// /// True when only the AVEVA-side probes pass — ignores failures in the /// category. Lets a live-test gate /// say "AVEVA is ready even if the v2 services aren't installed yet" without /// conflating the two. Useful for tests that exercise Galaxy directly (e.g. /// ) rather than through our stack. /// public bool IsAvevaSideReady => !Checks.Any(c => c.Status == PrerequisiteStatus.Fail && c.Category != PrerequisiteCategory.OtOpcUaService); /// /// Multi-line message for Assert.Skip when a hard dependency isn't met. Returns /// null when is true. /// public string? SkipReason { get { var fails = Checks.Where(c => c.Status == PrerequisiteStatus.Fail).ToList(); if (fails.Count == 0) return null; var sb = new StringBuilder(); sb.AppendLine($"Live-AVEVA prerequisites not met ({fails.Count} failed):"); foreach (var f in fails) sb.AppendLine($" • [{f.Category}] {f.Name} — {f.Detail}"); sb.Append("Run `Get-Service aa*` / `sqlcmd -S localhost -d ZB -E -Q \"SELECT 1\"` to triage."); return sb.ToString(); } } /// /// Human-readable summary of warnings — caller decides whether to log or ignore. Useful /// when a live test does pass but an operator should know their environment is degraded. /// public string? Warnings { get { var warns = Checks.Where(c => c.Status == PrerequisiteStatus.Warn).ToList(); if (warns.Count == 0) return null; var sb = new StringBuilder(); sb.AppendLine($"AVEVA prerequisites with warnings ({warns.Count}):"); foreach (var w in warns) sb.AppendLine($" • [{w.Category}] {w.Name} — {w.Detail}"); return sb.ToString(); } } /// /// Throw if any /// contain a Fail — useful when a specific test needs, say, Galaxy Repository but doesn't /// care about Historian. Call before Assert.Skip if you want to be strict. /// public void RequireCategories(params PrerequisiteCategory[] categories) { var set = categories.ToHashSet(); var fails = Checks.Where(c => c.Status == PrerequisiteStatus.Fail && set.Contains(c.Category)).ToList(); if (fails.Count == 0) return; var detail = string.Join("; ", fails.Select(f => $"{f.Name}: {f.Detail}")); throw new InvalidOperationException($"Required prerequisite categories failed: {detail}"); } public override string ToString() { var sb = new StringBuilder(); sb.AppendLine($"PrerequisiteReport: {Checks.Count} checks"); foreach (var c in Checks) sb.AppendLine($" [{c.Status,-4}] {c.Category}/{c.Name}: {c.Detail}"); return sb.ToString(); } }