Issue #35: add parity fixture matrix

This commit is contained in:
Joseph Doherty
2026-04-26 20:47:05 -04:00
parent 14afb325c3
commit 0a670eb381
4 changed files with 872 additions and 0 deletions
@@ -0,0 +1,293 @@
using System.Text.Json;
using MxGateway.Contracts;
namespace MxGateway.Tests.Contracts;
public sealed class ParityFixtureMatrixTests
{
[Fact]
public void Matrix_DeclaresCurrentProtocolVersionsAndComparisonFields()
{
using JsonDocument matrix = LoadParityMatrix();
JsonElement root = matrix.RootElement;
Assert.Equal(1, root.GetProperty("schemaVersion").GetInt32());
Assert.Equal("mxaccess-gateway-parity-fixture-matrix", root.GetProperty("fixtureSet").GetString());
Assert.Equal(GatewayContractInfo.GatewayProtocolVersion, root.GetProperty("gatewayProtocolVersion").GetUInt32());
Assert.Equal(GatewayContractInfo.WorkerProtocolVersion, root.GetProperty("workerProtocolVersion").GetUInt32());
JsonElement comparisonFormat = root.GetProperty("comparisonFormat");
AssertRequiredFields(
comparisonFormat.GetProperty("directMxAccess").GetProperty("requiredFields"),
"method",
"arguments",
"returnedValue",
"hresult",
"statuses",
"events");
AssertRequiredFields(
comparisonFormat.GetProperty("gatewayResult").GetProperty("requiredFields"),
"kind",
"protocolStatus",
"returnValue",
"hresult",
"statuses",
"events");
AssertRequiredFields(
comparisonFormat.GetProperty("eventFields"),
"family",
"value",
"quality",
"sourceTimestamp",
"statuses",
"workerSequence");
AssertRequiredFields(
comparisonFormat.GetProperty("comparisonKeys"),
"hresult",
"statusArrayShape",
"statusRawFields",
"eventFamilyOrder",
"eventPayloadShape",
"valueProjection",
"rawFallbackMetadata");
}
[Fact]
public void Matrix_CoversEveryPublicMxAccessMethod()
{
using JsonDocument matrix = LoadParityMatrix();
JsonElement methodFixtures = matrix.RootElement.GetProperty("methodFixtures");
Dictionary<string, JsonElement> fixturesByMethod = [];
HashSet<string> ids = new(StringComparer.Ordinal);
foreach (JsonElement fixture in methodFixtures.EnumerateArray())
{
string id = fixture.GetProperty("id").GetString()!;
string method = fixture.GetProperty("method").GetString()!;
string commandKind = fixture.GetProperty("commandKind").GetString()!;
string status = fixture.GetProperty("status").GetString()!;
Assert.True(ids.Add(id), $"Duplicate parity fixture id '{id}'.");
Assert.True(fixturesByMethod.TryAdd(method, fixture), $"Duplicate parity method '{method}'.");
Assert.StartsWith("MX_COMMAND_KIND_", commandKind, StringComparison.Ordinal);
Assert.Contains(status, KnownFixtureStatuses);
Assert.NotEmpty(fixture.GetProperty("assertions").EnumerateArray());
AssertCaptureReferencesAreRelative(fixture.GetProperty("captureReferences"));
}
Assert.Equal(ExpectedPublicMethods.Order(StringComparer.Ordinal), fixturesByMethod.Keys.Order(StringComparer.Ordinal));
foreach (string method in ExpectedPublicMethods)
{
JsonElement fixture = fixturesByMethod[method];
string status = fixture.GetProperty("status").GetString()!;
Assert.True(
status == "planned_fixture" || status == "documented_gap",
$"Method '{method}' must have a planned parity fixture or documented gap.");
}
}
[Fact]
public void Matrix_CoversRequiredParityScenarioGroups()
{
using JsonDocument matrix = LoadParityMatrix();
HashSet<string> knownFixtureIds = GetFixtureIds(matrix.RootElement);
Dictionary<string, JsonElement> groupsById = [];
foreach (JsonElement group in matrix.RootElement.GetProperty("scenarioGroups").EnumerateArray())
{
string id = group.GetProperty("id").GetString()!;
Assert.True(groupsById.TryAdd(id, group), $"Duplicate parity scenario group '{id}'.");
Assert.NotEmpty(group.GetProperty("description").GetString()!);
Assert.NotEmpty(group.GetProperty("fixtureIds").EnumerateArray());
AssertCaptureReferencesAreRelative(group.GetProperty("captureReferences"));
foreach (JsonElement fixtureIdElement in group.GetProperty("fixtureIds").EnumerateArray())
{
string fixtureId = fixtureIdElement.GetString()!;
Assert.Contains(fixtureId, knownFixtureIds);
}
}
foreach (string requiredGroup in RequiredScenarioGroups)
{
Assert.True(groupsById.ContainsKey(requiredGroup), $"Missing required parity scenario group '{requiredGroup}'.");
}
AssertScenarioCovers(groupsById["invalid_handles"], "method.remove-item.basic", "method.write.value-status-matrix");
AssertScenarioCovers(groupsById["write_statuses"], "method.write.value-status-matrix", "event.on-write-complete.status");
AssertScenarioCovers(groupsById["secured_writes"], "method.write-secured.rejection-gap", "method.write-secured2.authenticated");
AssertScenarioCovers(groupsById["add_item_context"], "method.add-item2.context", "method.add-buffered-item.context");
AssertScenarioCovers(groupsById["buffered_registration"], "method.add-buffered-item.context", "event.on-buffered-data-change.batch-gap");
}
[Fact]
public void Matrix_CoversEveryPublicMxAccessEventFamily()
{
using JsonDocument matrix = LoadParityMatrix();
Dictionary<string, JsonElement> fixturesByFamily = [];
foreach (JsonElement fixture in matrix.RootElement.GetProperty("eventFixtures").EnumerateArray())
{
string family = fixture.GetProperty("family").GetString()!;
string status = fixture.GetProperty("status").GetString()!;
Assert.True(fixturesByFamily.TryAdd(family, fixture), $"Duplicate parity event family '{family}'.");
Assert.Contains(status, KnownFixtureStatuses);
Assert.NotEmpty(fixture.GetProperty("assertions").EnumerateArray());
AssertCaptureReferencesAreRelative(fixture.GetProperty("captureReferences"));
}
foreach (string eventFamily in ExpectedEventFamilies)
{
Assert.True(fixturesByFamily.ContainsKey(eventFamily), $"Missing parity fixture for event family '{eventFamily}'.");
}
Assert.Equal("documented_gap", fixturesByFamily["MX_EVENT_FAMILY_OPERATION_COMPLETE"].GetProperty("status").GetString());
Assert.Equal("documented_gap", fixturesByFamily["MX_EVENT_FAMILY_ON_BUFFERED_DATA_CHANGE"].GetProperty("status").GetString());
}
private static readonly string[] ExpectedPublicMethods =
[
"Register",
"Unregister",
"AddItem",
"AddItem2",
"RemoveItem",
"Advise",
"UnAdvise",
"AdviseSupervisory",
"AddBufferedItem",
"SetBufferedUpdateInterval",
"Suspend",
"Activate",
"Write",
"Write2",
"WriteSecured",
"WriteSecured2",
"AuthenticateUser",
"ArchestrAUserToId",
];
private static readonly string[] ExpectedEventFamilies =
[
"MX_EVENT_FAMILY_ON_DATA_CHANGE",
"MX_EVENT_FAMILY_ON_WRITE_COMPLETE",
"MX_EVENT_FAMILY_OPERATION_COMPLETE",
"MX_EVENT_FAMILY_ON_BUFFERED_DATA_CHANGE",
];
private static readonly string[] RequiredScenarioGroups =
[
"invalid_handles",
"write_statuses",
"secured_writes",
"add_item_context",
"buffered_registration",
];
private static readonly string[] KnownFixtureStatuses =
[
"planned_fixture",
"documented_gap",
];
private static void AssertRequiredFields(
JsonElement fields,
params string[] expectedFields)
{
HashSet<string> declared = fields
.EnumerateArray()
.Select(field => field.GetString()!)
.ToHashSet(StringComparer.Ordinal);
foreach (string expectedField in expectedFields)
{
Assert.Contains(expectedField, declared);
}
}
private static void AssertCaptureReferencesAreRelative(JsonElement captureReferences)
{
int count = 0;
foreach (JsonElement captureReference in captureReferences.EnumerateArray())
{
string path = captureReference.GetString()!;
Assert.StartsWith("captures/", path, StringComparison.Ordinal);
Assert.DoesNotContain("\\", path, StringComparison.Ordinal);
Assert.False(Path.IsPathRooted(path), $"Capture reference '{path}' must be relative.");
count++;
}
Assert.True(count > 0, "Each parity fixture must reference at least one MXAccess capture.");
}
private static void AssertScenarioCovers(
JsonElement group,
params string[] fixtureIds)
{
HashSet<string> declared = group
.GetProperty("fixtureIds")
.EnumerateArray()
.Select(fixtureId => fixtureId.GetString()!)
.ToHashSet(StringComparer.Ordinal);
foreach (string fixtureId in fixtureIds)
{
Assert.Contains(fixtureId, declared);
}
}
private static HashSet<string> GetFixtureIds(JsonElement root)
{
HashSet<string> ids = new(StringComparer.Ordinal);
foreach (JsonElement fixture in root.GetProperty("methodFixtures").EnumerateArray())
{
ids.Add(fixture.GetProperty("id").GetString()!);
}
foreach (JsonElement fixture in root.GetProperty("eventFixtures").EnumerateArray())
{
ids.Add(fixture.GetProperty("id").GetString()!);
}
return ids;
}
private static JsonDocument LoadParityMatrix()
{
return JsonDocument.Parse(File.ReadAllText(Path.Combine(GetParityFixtureRoot().FullName, "parity-fixture-matrix.json")));
}
private static DirectoryInfo GetParityFixtureRoot()
{
DirectoryInfo repositoryRoot = FindRepositoryRoot();
return new DirectoryInfo(Path.Combine(repositoryRoot.FullName, "clients", "proto", "fixtures", "parity"));
}
private static DirectoryInfo FindRepositoryRoot()
{
DirectoryInfo? current = new(AppContext.BaseDirectory);
while (current is not null)
{
if (File.Exists(Path.Combine(current.FullName, "AGENTS.md"))
&& Directory.Exists(Path.Combine(current.FullName, "src"))
&& Directory.Exists(Path.Combine(current.FullName, "clients")))
{
return current;
}
current = current.Parent;
}
throw new DirectoryNotFoundException("Could not locate the repository root from the test output directory.");
}
}