using System.Text.Json; using Google.Protobuf; using MxGateway.Contracts; using MxGateway.Contracts.Proto; namespace MxGateway.Tests.Contracts; public sealed class ClientProtoInputTests { [Fact] public void Manifest_DeclaresCurrentProtocolVersionsAndExistingInputs() { DirectoryInfo repositoryRoot = FindRepositoryRoot(); string manifestPath = Path.Combine(repositoryRoot.FullName, "clients", "proto", "proto-inputs.json"); using JsonDocument manifest = JsonDocument.Parse(File.ReadAllText(manifestPath)); JsonElement root = manifest.RootElement; Assert.Equal(1, root.GetProperty("schemaVersion").GetInt32()); Assert.Equal(GatewayContractInfo.GatewayProtocolVersion, root.GetProperty("gatewayProtocolVersion").GetUInt32()); Assert.Equal(GatewayContractInfo.WorkerProtocolVersion, root.GetProperty("workerProtocolVersion").GetUInt32()); string protoRoot = Path.Combine(repositoryRoot.FullName, root.GetProperty("protoRoot").GetString()!); foreach (JsonElement sourceFile in root.GetProperty("sourceFiles").EnumerateArray()) { string sourcePath = Path.Combine(protoRoot, sourceFile.GetProperty("path").GetString()!); Assert.True(File.Exists(sourcePath), $"Expected proto source file '{sourcePath}' to exist."); } foreach (JsonProperty output in root.GetProperty("generatedOutputs").EnumerateObject()) { string outputPath = Path.Combine(repositoryRoot.FullName, output.Value.GetString()!); Assert.True(Directory.Exists(outputPath), $"Expected generated output directory '{outputPath}' to exist."); } } [Fact] public void OpenSessionReplyFixture_ParsesWithCurrentContract() { OpenSessionReply reply = ParseFixture( "open-session-reply.ok.json", OpenSessionReply.Parser); Assert.Equal(GatewayContractInfo.GatewayProtocolVersion, reply.GatewayProtocolVersion); Assert.Equal(GatewayContractInfo.WorkerProtocolVersion, reply.WorkerProtocolVersion); Assert.Equal(ProtocolStatusCode.Ok, reply.ProtocolStatus.Code); } [Fact] public void RegisterCommandRequestFixture_ParsesWithCurrentContract() { MxCommandRequest request = ParseFixture( "register-command-request.json", MxCommandRequest.Parser); Assert.Equal(MxCommandKind.Register, request.Command.Kind); Assert.Equal("fixture-client", request.Command.Register.ClientName); } [Fact] public void OnDataChangeEventFixture_ParsesWithCurrentContract() { MxEvent gatewayEvent = ParseFixture( "on-data-change-event.json", MxEvent.Parser); Assert.Equal(MxEventFamily.OnDataChange, gatewayEvent.Family); Assert.Equal(1ul, gatewayEvent.WorkerSequence); Assert.Equal(MxDataType.Integer, gatewayEvent.Value.DataType); Assert.Equal(MxEvent.BodyOneofCase.OnDataChange, gatewayEvent.BodyCase); } private static T ParseFixture( string fixtureName, MessageParser parser) where T : IMessage { DirectoryInfo repositoryRoot = FindRepositoryRoot(); string fixturePath = Path.Combine(repositoryRoot.FullName, "clients", "proto", "fixtures", "golden", fixtureName); return parser.ParseJson(File.ReadAllText(fixturePath)); } 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."); } }