101 lines
3.9 KiB
C#
101 lines
3.9 KiB
C#
using Google.Protobuf;
|
|
using Google.Protobuf.Reflection;
|
|
using RetainedGalaxyReflection = ZB.MOM.WW.MxGateway.Contracts.Proto.Galaxy.GalaxyRepositoryReflection;
|
|
using ServedGalaxyReflection = ZB.MOM.WW.GalaxyRepository.Grpc.GalaxyRepositoryReflection;
|
|
|
|
namespace ZB.MOM.WW.MxGateway.Tests.Contracts;
|
|
|
|
public sealed class GalaxyContractParityTests
|
|
{
|
|
/// <summary>Guards the retained client proto against drift from the descriptor served by the gateway.</summary>
|
|
[Fact]
|
|
public void RetainedClientDescriptor_MatchesServedGatewayDescriptor()
|
|
{
|
|
FileDescriptorProto retained = Normalize(RetainedGalaxyReflection.Descriptor);
|
|
FileDescriptorProto served = Normalize(ServedGalaxyReflection.Descriptor);
|
|
|
|
AssertEquivalentDescriptors(retained, served);
|
|
}
|
|
|
|
/// <summary>Proves descriptor drift diagnostics name the first differing protobuf message.</summary>
|
|
[Fact]
|
|
public void DescriptorComparison_WhenMessageDiffers_ReportsDifferingMessage()
|
|
{
|
|
FileDescriptorProto retained = Normalize(RetainedGalaxyReflection.Descriptor);
|
|
FileDescriptorProto changed = retained.Clone();
|
|
DescriptorProto changedMessage = changed.MessageType.First(message => message.Field.Count > 0);
|
|
changedMessage.Field[0].Name += "_changed";
|
|
|
|
Xunit.Sdk.XunitException exception = Assert.Throws<Xunit.Sdk.XunitException>(
|
|
() => AssertEquivalentDescriptors(retained, changed));
|
|
|
|
Assert.Contains(changedMessage.Name, exception.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
private static FileDescriptorProto Normalize(FileDescriptor descriptor)
|
|
{
|
|
FileDescriptorProto proto = descriptor.ToProto();
|
|
proto.SourceCodeInfo = null;
|
|
proto.Name = string.Empty;
|
|
if (proto.Options is not null)
|
|
{
|
|
proto.Options.CsharpNamespace = string.Empty;
|
|
}
|
|
|
|
return proto;
|
|
}
|
|
|
|
private static void AssertEquivalentDescriptors(FileDescriptorProto expected, FileDescriptorProto actual)
|
|
{
|
|
byte[] expectedBytes = SerializeDeterministically(expected);
|
|
byte[] actualBytes = SerializeDeterministically(actual);
|
|
if (expectedBytes.AsSpan().SequenceEqual(actualBytes))
|
|
{
|
|
return;
|
|
}
|
|
|
|
throw new Xunit.Sdk.XunitException(
|
|
$"Galaxy Repository descriptors differ at {DescribeFirstDifference(expected, actual)}.");
|
|
}
|
|
|
|
private static string DescribeFirstDifference(FileDescriptorProto expected, FileDescriptorProto actual)
|
|
{
|
|
int comparableMessageCount = Math.Min(expected.MessageType.Count, actual.MessageType.Count);
|
|
for (int index = 0; index < comparableMessageCount; index++)
|
|
{
|
|
DescriptorProto expectedMessage = expected.MessageType[index];
|
|
DescriptorProto actualMessage = actual.MessageType[index];
|
|
if (!SerializeDeterministically(expectedMessage).AsSpan()
|
|
.SequenceEqual(SerializeDeterministically(actualMessage)))
|
|
{
|
|
string messageName = string.IsNullOrEmpty(expectedMessage.Name)
|
|
? actualMessage.Name
|
|
: expectedMessage.Name;
|
|
return $"message '{Qualify(expected.Package, messageName)}'";
|
|
}
|
|
}
|
|
|
|
if (expected.MessageType.Count != actual.MessageType.Count)
|
|
{
|
|
return "the top-level message list";
|
|
}
|
|
|
|
return "the normalized file descriptor";
|
|
}
|
|
|
|
private static string Qualify(string package, string symbol) =>
|
|
string.IsNullOrEmpty(package) ? symbol : package + "." + symbol;
|
|
|
|
private static byte[] SerializeDeterministically(IMessage message)
|
|
{
|
|
using MemoryStream stream = new();
|
|
using CodedOutputStream output = new(stream, leaveOpen: true)
|
|
{
|
|
Deterministic = true,
|
|
};
|
|
message.WriteTo(output);
|
|
output.Flush();
|
|
return stream.ToArray();
|
|
}
|
|
}
|