using Google.Protobuf;
using Org.Eclipse.Tahu.Protobuf;
using Shouldly;
using Xunit;
namespace ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Tests;
///
/// Pins the vendored Eclipse Tahu sparkplug_b protobuf codegen: the generated types exist,
/// land in the namespace the rest of P2 is written against, and carry the Sparkplug-B field numbers
/// on the wire. Nothing here decodes a real payload — that is Task 16 (SparkplugCodec +
/// golden vectors); this suite exists so a broken/renamed/mangled vendored proto fails at the
/// codegen seam rather than as a mysterious decode failure three tasks later.
///
public sealed class SparkplugProtoCodegenTests
{
[Fact]
public void GeneratedPayload_RoundTrips()
{
var p = new Payload { Seq = 3 };
p.Metrics.Add(new Payload.Types.Metric { Name = "Temperature", Alias = 5 });
var back = Payload.Parser.ParseFrom(p.ToByteArray());
back.Seq.ShouldBe(3ul);
back.Metrics[0].Name.ShouldBe("Temperature");
back.Metrics[0].Alias.ShouldBe(5ul);
}
///
/// Tasks 16 / 20 / 25 name Org.Eclipse.Tahu.Protobuf explicitly, and the namespace is
/// derived (not declared) — protoc PascalCases the proto package because the vendored file
/// carries no option csharp_namespace. Editing the package line to "tidy" it would silently
/// rehome every generated type.
///
[Fact]
public void GeneratedTypes_LiveInTheTahuPackageNamespace()
{
typeof(Payload).Namespace.ShouldBe("Org.Eclipse.Tahu.Protobuf");
Payload.Descriptor.File.Package.ShouldBe("org.eclipse.tahu.protobuf");
}
///
/// Field numbers are the wire contract with every Sparkplug edge node in the plant; a vendored
/// proto that was hand-edited or reconstructed rather than copied from upstream would most likely
/// drift here first.
///
[Fact]
public void PayloadAndMetric_CarrySparkplugBFieldNumbers()
{
Payload.TimestampFieldNumber.ShouldBe(1);
Payload.MetricsFieldNumber.ShouldBe(2);
Payload.SeqFieldNumber.ShouldBe(3);
Payload.UuidFieldNumber.ShouldBe(4);
Payload.BodyFieldNumber.ShouldBe(5);
Payload.Types.Metric.NameFieldNumber.ShouldBe(1);
Payload.Types.Metric.AliasFieldNumber.ShouldBe(2);
Payload.Types.Metric.TimestampFieldNumber.ShouldBe(3);
Payload.Types.Metric.DatatypeFieldNumber.ShouldBe(4);
Payload.Types.Metric.IsNullFieldNumber.ShouldBe(7);
}
///
/// Task 17's datatype map keys off these indices. They are Sparkplug-spec constants, not protobuf
/// tags, so they are worth pinning independently of the field numbers above.
///
[Fact]
public void DataTypeEnum_MatchesSparkplugSpecIndices()
{
((int)DataType.Int8).ShouldBe(1);
((int)DataType.Int32).ShouldBe(3);
((int)DataType.Uint64).ShouldBe(8);
((int)DataType.Float).ShouldBe(9);
((int)DataType.Double).ShouldBe(10);
((int)DataType.Boolean).ShouldBe(11);
((int)DataType.String).ShouldBe(12);
((int)DataType.DateTime).ShouldBe(13);
((int)DataType.DataSet).ShouldBe(16);
((int)DataType.Template).ShouldBe(19);
((int)DataType.Int32Array).ShouldBe(24);
}
///
/// protoc's C# generator RE-CASES enum member names: the proto's UInt64 becomes
/// Uint64 and UUID becomes Uuid (only the leading character of each
/// underscore-separated word survives as a capital). Task 17's datatype map has to spell them
/// the mangled way, so pin the mangling rather than letting it be rediscovered as a build error.
/// The wire-facing spelling is preserved on OriginalName, which is what
/// DataType.Parser-style JSON round-trips use.
///
[Fact]
public void DataTypeEnum_CSharpNamesAreProtocMangled_NotTheProtoSpelling()
{
Enum.GetNames().ShouldContain("Uint64");
Enum.GetNames().ShouldNotContain("UInt64");
Enum.GetNames().ShouldContain("Uuid");
Enum.GetNames().ShouldNotContain("UUID");
}
}