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();
}
}