contracts: add BrowseChildren RPC for lazy hierarchy browse

This commit is contained in:
Joseph Doherty
2026-05-28 12:47:02 -04:00
parent b3ebf583ad
commit 2c5c5e5c7e
4 changed files with 1274 additions and 14 deletions
@@ -661,6 +661,7 @@ public sealed class ProtobufContractRoundTripTests
Assert.Contains(service.Methods, method => method.Name == "GetLastDeployTime");
Assert.Contains(service.Methods, method => method.Name == "DiscoverHierarchy");
Assert.Contains(service.Methods, method => method.Name == "WatchDeployEvents");
Assert.Contains(service.Methods, method => method.Name == "BrowseChildren");
}
/// <summary>
@@ -769,6 +770,114 @@ public sealed class ProtobufContractRoundTripTests
Assert.True(parsed.Objects[0].Attributes[0].IsAlarm);
}
/// <summary>
/// Verifies that a BrowseChildrenRequest round-trips through every
/// <c>parent</c> oneof arm with the full filter set populated.
/// </summary>
/// <param name="parentArm">The oneof arm selector (0=ParentGobjectId, 1=ParentTagName, 2=ParentContainedPath).</param>
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
public void BrowseChildrenRequest_RoundTripsParentOneofAndFilters(int parentArm)
{
var original = new BrowseChildrenRequest
{
PageSize = 200,
PageToken = "opaque-2",
CategoryIds = { 3, 9 },
TemplateChainContains = { "Analog", "Pump" },
TagNameGlob = "Tank*",
IncludeAttributes = true,
AlarmBearingOnly = true,
HistorizedOnly = false,
};
switch (parentArm)
{
case 0:
original.ParentGobjectId = 4711;
break;
case 1:
original.ParentTagName = "Tank01";
break;
default:
original.ParentContainedPath = "Area1.Tank01";
break;
}
var parsed = BrowseChildrenRequest.Parser.ParseFrom(original.ToByteArray());
Assert.Equal(original, parsed);
Assert.Equal(original.ParentCase, parsed.ParentCase);
Assert.NotEqual(BrowseChildrenRequest.ParentOneofCase.None, parsed.ParentCase);
Assert.True(parsed.HasIncludeAttributes);
Assert.True(parsed.IncludeAttributes);
}
/// <summary>
/// Verifies that a BrowseChildrenReply round-trips its children list,
/// the parallel-indexed <c>child_has_children</c> array, and the
/// cache sequence used to bind page tokens.
/// </summary>
[Fact]
public void BrowseChildrenReply_RoundTripsChildrenAndHasChildrenParallelArrays()
{
var original = new BrowseChildrenReply
{
NextPageToken = "opaque-3",
TotalChildCount = 2,
CacheSequence = 42UL,
Children =
{
new GalaxyObject
{
GobjectId = 4711,
TagName = "Tank01",
ContainedName = "Tank01",
BrowseName = "Tank 01",
ParentGobjectId = 12,
IsArea = false,
CategoryId = 3,
HostedByGobjectId = 8,
TemplateChain = { "$AnalogDevice", "$Tank" },
Attributes =
{
new GalaxyAttribute
{
AttributeName = "Level",
FullTagReference = "Galaxy!Tank01.Level",
MxDataType = 3,
DataTypeName = "Float",
IsArray = false,
ArrayDimension = 0,
ArrayDimensionPresent = false,
MxAttributeCategory = 1,
SecurityClassification = 0,
IsHistorized = true,
IsAlarm = true,
},
},
},
new GalaxyObject
{
GobjectId = 12,
TagName = "Area1",
IsArea = true,
},
},
ChildHasChildren = { true, false },
};
var parsed = BrowseChildrenReply.Parser.ParseFrom(original.ToByteArray());
Assert.Equal(original, parsed);
Assert.Equal(2, parsed.Children.Count);
Assert.Equal(2, parsed.ChildHasChildren.Count);
Assert.True(parsed.ChildHasChildren[0]);
Assert.False(parsed.ChildHasChildren[1]);
Assert.Equal(42UL, parsed.CacheSequence);
}
/// <summary>Verifies that a DeployEvent round-trips with its timestamp and counters.</summary>
[Fact]
public void DeployEvent_RoundTripsTimestampAndCounters()