Move shared fixtures and parity utilities to TestUtilities project
- git mv JetStreamApiFixture, JetStreamClusterFixture, LeafFixture, Parity utilities, and TestData from NATS.Server.Tests to NATS.Server.TestUtilities - Update namespaces to NATS.Server.TestUtilities (and .Parity sub-ns) - Make fixture classes public for cross-project access - Add PollHelper to replace Task.Delay polling with SemaphoreSlim waits - Refactor all fixture polling loops to use PollHelper - Add 'using NATS.Server.TestUtilities;' to ~75 consuming test files - Rename local fixture duplicates (MetaGroupTestFixture, LeafProtocolTestFixture) to avoid shadowing shared fixtures - Remove TestData entry from NATS.Server.Tests.csproj (moved to TestUtilities)
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
namespace NATS.Server.TestUtilities.Parity;
|
||||
|
||||
public sealed record DriftRow(string Feature, string DifferencesStatus, string EvidenceStatus, string Reason);
|
||||
|
||||
public sealed class JetStreamParityTruthMatrixReport
|
||||
{
|
||||
public JetStreamParityTruthMatrixReport(IReadOnlyList<DriftRow> driftRows, IReadOnlyList<string> contradictions)
|
||||
{
|
||||
DriftRows = driftRows;
|
||||
Contradictions = contradictions;
|
||||
}
|
||||
|
||||
public IReadOnlyList<DriftRow> DriftRows { get; }
|
||||
public IReadOnlyList<string> Contradictions { get; }
|
||||
}
|
||||
|
||||
public static class JetStreamParityTruthMatrix
|
||||
{
|
||||
public static JetStreamParityTruthMatrixReport Load(string differencesRelativePath, string mapRelativePath)
|
||||
{
|
||||
var repositoryRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", ".."));
|
||||
var differencesPath = Path.Combine(repositoryRoot, differencesRelativePath);
|
||||
var mapPath = Path.Combine(repositoryRoot, mapRelativePath);
|
||||
File.Exists(differencesPath).ShouldBeTrue();
|
||||
File.Exists(mapPath).ShouldBeTrue();
|
||||
|
||||
var differences = ParityRowInspector.Load(differencesRelativePath).Rows;
|
||||
var matrixRows = ParseTruthMatrix(mapPath);
|
||||
var drift = new List<DriftRow>();
|
||||
|
||||
if (matrixRows.Count == 0)
|
||||
{
|
||||
drift.Add(new DriftRow(
|
||||
"JetStream Truth Matrix",
|
||||
"missing",
|
||||
"missing",
|
||||
"docs/plans/2026-02-23-jetstream-remaining-parity-map.md must include a populated 'JetStream Truth Matrix' table."));
|
||||
}
|
||||
|
||||
foreach (var row in matrixRows)
|
||||
{
|
||||
var differencesRow = differences.FirstOrDefault(r =>
|
||||
string.Equals(r.Feature, row.DifferencesFeature, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (differencesRow is null)
|
||||
{
|
||||
drift.Add(new DriftRow(
|
||||
row.Feature,
|
||||
"missing",
|
||||
row.EvidenceStatus,
|
||||
$"Differences row '{row.DifferencesFeature}' was not found in differences.md."));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!string.Equals(differencesRow.DotNetStatus, "Y", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
drift.Add(new DriftRow(
|
||||
row.Feature,
|
||||
differencesRow.DotNetStatus,
|
||||
row.EvidenceStatus,
|
||||
"Differences status must be Y for a verified truth-matrix row."));
|
||||
}
|
||||
|
||||
if (!string.Equals(row.EvidenceStatus, "verified", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
drift.Add(new DriftRow(
|
||||
row.Feature,
|
||||
differencesRow.DotNetStatus,
|
||||
row.EvidenceStatus,
|
||||
"Evidence status must be 'verified'."));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(row.TestEvidence) || row.TestEvidence == "-")
|
||||
{
|
||||
drift.Add(new DriftRow(
|
||||
row.Feature,
|
||||
differencesRow.DotNetStatus,
|
||||
row.EvidenceStatus,
|
||||
"Test evidence must be provided for every truth-matrix row."));
|
||||
}
|
||||
}
|
||||
|
||||
var contradictions = ParseRemainingExplicitDeltaContradictions(differencesPath, matrixRows);
|
||||
return new JetStreamParityTruthMatrixReport(drift, contradictions);
|
||||
}
|
||||
|
||||
private static List<TruthMatrixRow> ParseTruthMatrix(string mapPath)
|
||||
{
|
||||
var rows = new List<TruthMatrixRow>();
|
||||
var inTruthMatrix = false;
|
||||
foreach (var rawLine in File.ReadLines(mapPath))
|
||||
{
|
||||
var line = rawLine.Trim();
|
||||
if (line.StartsWith("## ", StringComparison.Ordinal))
|
||||
{
|
||||
inTruthMatrix = string.Equals(
|
||||
line,
|
||||
"## JetStream Truth Matrix",
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inTruthMatrix || !line.StartsWith("|", StringComparison.Ordinal) || line.Contains("---", StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
var cells = line.Trim('|').Split('|').Select(c => c.Trim()).ToArray();
|
||||
if (cells.Length < 4 || string.Equals(cells[0], "Feature", StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
rows.Add(new TruthMatrixRow(
|
||||
cells[0],
|
||||
cells[1],
|
||||
cells[2],
|
||||
cells[3]));
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
private static List<string> ParseRemainingExplicitDeltaContradictions(
|
||||
string differencesPath,
|
||||
IReadOnlyList<TruthMatrixRow> matrixRows)
|
||||
{
|
||||
var contradictions = new List<string>();
|
||||
var inExplicitDeltas = false;
|
||||
var negativeMarkers = new[]
|
||||
{
|
||||
"unimplemented",
|
||||
"still `n`",
|
||||
"still n",
|
||||
"remains",
|
||||
"incomplete",
|
||||
};
|
||||
|
||||
foreach (var rawLine in File.ReadLines(differencesPath))
|
||||
{
|
||||
var line = rawLine.Trim();
|
||||
if (line.StartsWith("### ", StringComparison.Ordinal))
|
||||
{
|
||||
inExplicitDeltas = string.Equals(
|
||||
line,
|
||||
"### Remaining Explicit Deltas",
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inExplicitDeltas && line.StartsWith("## ", StringComparison.Ordinal))
|
||||
{
|
||||
inExplicitDeltas = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inExplicitDeltas || !line.StartsWith("- ", StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
var normalizedLine = line.ToLowerInvariant();
|
||||
if (!negativeMarkers.Any(marker => normalizedLine.Contains(marker, StringComparison.Ordinal)))
|
||||
continue;
|
||||
|
||||
foreach (var row in matrixRows.Where(r =>
|
||||
string.Equals(r.EvidenceStatus, "verified", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
if (MentionsFeature(normalizedLine, row))
|
||||
{
|
||||
contradictions.Add($"{row.Feature}: {line[2..].Trim()}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return contradictions;
|
||||
}
|
||||
|
||||
private static bool MentionsFeature(string normalizedLine, TruthMatrixRow row)
|
||||
{
|
||||
var tokens = Tokenize(row.Feature)
|
||||
.Concat(Tokenize(row.DifferencesFeature))
|
||||
.Where(t => t.Length >= 4)
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
|
||||
if (tokens.Length == 0)
|
||||
return false;
|
||||
|
||||
var matches = tokens.Count(t => normalizedLine.Contains(t, StringComparison.Ordinal));
|
||||
return matches >= 2;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> Tokenize(string value)
|
||||
{
|
||||
var chars = value.ToLowerInvariant()
|
||||
.Select(c => char.IsLetterOrDigit(c) ? c : ' ')
|
||||
.ToArray();
|
||||
return new string(chars)
|
||||
.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
}
|
||||
|
||||
private sealed record TruthMatrixRow(
|
||||
string Feature,
|
||||
string DifferencesFeature,
|
||||
string EvidenceStatus,
|
||||
string TestEvidence);
|
||||
}
|
||||
Reference in New Issue
Block a user