docs(lmxproxy): update test tags to TestChildObject namespace for v2 type coverage

Replace JoeAppEngine tags with TestChildObject tags (TestBool, TestInt, TestFloat,
TestDouble, TestString, TestDateTime, and array variants) in Phase 4 and Phase 7
plans. These tags cover all TypedValue oneof cases for comprehensive v2 testing.
This commit is contained in:
Joseph Doherty
2026-03-21 23:35:15 -04:00
parent 4303f06fc3
commit 08d2a07d8b
2 changed files with 73 additions and 29 deletions

View File

@@ -165,7 +165,7 @@ public class DetailedHealthCheckService : IHealthCheck
private readonly IScadaClient _scadaClient; private readonly IScadaClient _scadaClient;
private readonly string _testTagAddress; private readonly string _testTagAddress;
public DetailedHealthCheckService(IScadaClient scadaClient, string testTagAddress = "System.Heartbeat") { ... } public DetailedHealthCheckService(IScadaClient scadaClient, string testTagAddress = "TestChildObject.TestBool") { ... }
public async Task<HealthCheckResult> CheckHealthAsync( public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context, HealthCheckContext context,

View File

@@ -13,7 +13,19 @@
3. **Integration tests run from Mac against windev** — they use `Grpc.Net.Client` which is cross-platform. 3. **Integration tests run from Mac against windev** — they use `Grpc.Net.Client` which is cross-platform.
4. **All integration tests must pass before cutover**. 4. **All integration tests must pass before cutover**.
5. **API keys**: The existing `apikeys.json` on windev is the source of truth for valid keys. Read it to get test keys. 5. **API keys**: The existing `apikeys.json` on windev is the source of truth for valid keys. Read it to get test keys.
6. **Real MxAccess tags**: Use tags from the JoeAppEngine namespace which is the live AVEVA System Platform instance on windev. 6. **Real MxAccess tags**: Use the `TestChildObject` tags on windev's AVEVA System Platform instance. Available tags cover all TypedValue cases:
- `TestChildObject.TestBool` (bool)
- `TestChildObject.TestInt` (int)
- `TestChildObject.TestFloat` (float)
- `TestChildObject.TestDouble` (double)
- `TestChildObject.TestString` (string)
- `TestChildObject.TestDateTime` (datetime)
- `TestChildObject.TestBoolArray[]` (bool array)
- `TestChildObject.TestDateTimeArray[]` (datetime array)
- `TestChildObject.TestDoubleArray[]` (double array)
- `TestChildObject.TestFloatArray[]` (float array)
- `TestChildObject.TestIntArray[]` (int array)
- `TestChildObject.TestStringArray[]` (string array)
## Step 1: Build Host on windev ## Step 1: Build Host on windev
@@ -58,6 +70,11 @@ ssh windev "powershell -Command \"@'
\"ChannelCapacity\": 1000, \"ChannelCapacity\": 1000,
\"ChannelFullMode\": \"DropOldest\" \"ChannelFullMode\": \"DropOldest\"
}, },
\"HealthCheck\": {
\"Enabled\": true,
\"TestTagAddress\": \"TestChildObject.TestBool\",
\"MaxStaleDataMinutes\": 5
},
\"Tls\": { \"Tls\": {
\"Enabled\": false \"Enabled\": false
}, },
@@ -377,35 +394,62 @@ namespace ZB.MOM.WW.LmxProxy.Client.IntegrationTests;
public class ReadTests : IntegrationTestBase public class ReadTests : IntegrationTestBase
{ {
[Fact] [Fact]
public async Task Read_StringTag_ReturnsStringValue() public async Task Read_BoolTag_ReturnsBoolValue()
{ {
// JoeAppEngine.Area is a string attribute that should return "JoeDev" var vtq = await Client!.ReadAsync("TestChildObject.TestBool");
var vtq = await Client!.ReadAsync("JoeAppEngine.Area"); Assert.IsType<bool>(vtq.Value);
Assert.NotNull(vtq.Value); Assert.True(vtq.Quality.IsGood());
Assert.IsType<string>(vtq.Value);
Assert.Equal("JoeDev", vtq.Value);
// Quality should be Good (check via QualityExtensions.IsGood if available,
// or check vtq.Quality == Quality.Good)
} }
[Fact] [Fact]
public async Task Read_WritableTag_ReturnsTypedValue() public async Task Read_IntTag_ReturnsIntValue()
{ {
// JoeAppEngine.BTCS is a writable tag var vtq = await Client!.ReadAsync("TestChildObject.TestInt");
var vtq = await Client!.ReadAsync("JoeAppEngine.BTCS"); Assert.True(vtq.Value is int or long);
Assert.NotNull(vtq.Value); Assert.True(vtq.Quality.IsGood());
// Verify timestamp is recent (within last hour) }
[Fact]
public async Task Read_FloatTag_ReturnsFloatValue()
{
var vtq = await Client!.ReadAsync("TestChildObject.TestFloat");
Assert.True(vtq.Value is float or double);
Assert.True(vtq.Quality.IsGood());
}
[Fact]
public async Task Read_DoubleTag_ReturnsDoubleValue()
{
var vtq = await Client!.ReadAsync("TestChildObject.TestDouble");
Assert.IsType<double>(vtq.Value);
Assert.True(vtq.Quality.IsGood());
}
[Fact]
public async Task Read_StringTag_ReturnsStringValue()
{
var vtq = await Client!.ReadAsync("TestChildObject.TestString");
Assert.IsType<string>(vtq.Value);
Assert.True(vtq.Quality.IsGood());
}
[Fact]
public async Task Read_DateTimeTag_ReturnsDateTimeValue()
{
var vtq = await Client!.ReadAsync("TestChildObject.TestDateTime");
Assert.IsType<DateTime>(vtq.Value);
Assert.True(vtq.Quality.IsGood());
Assert.True(DateTime.UtcNow - vtq.Timestamp < TimeSpan.FromHours(1)); Assert.True(DateTime.UtcNow - vtq.Timestamp < TimeSpan.FromHours(1));
} }
[Fact] [Fact]
public async Task ReadBatch_MultiplesTags_ReturnsDictionary() public async Task ReadBatch_MultiplesTags_ReturnsDictionary()
{ {
var tags = new[] { "JoeAppEngine.Area", "JoeAppEngine.BTCS" }; var tags = new[] { "TestChildObject.TestString", "TestChildObject.TestString" };
var results = await Client!.ReadBatchAsync(tags); var results = await Client!.ReadBatchAsync(tags);
Assert.Equal(2, results.Count); Assert.Equal(2, results.Count);
Assert.True(results.ContainsKey("JoeAppEngine.Area")); Assert.True(results.ContainsKey("TestChildObject.TestString"));
Assert.True(results.ContainsKey("JoeAppEngine.BTCS")); Assert.True(results.ContainsKey("TestChildObject.TestString"));
} }
[Fact] [Fact]
@@ -437,12 +481,12 @@ public class WriteTests : IntegrationTestBase
{ {
string testValue = $"IntTest-{DateTime.UtcNow:HHmmss}"; string testValue = $"IntTest-{DateTime.UtcNow:HHmmss}";
// Write to a writable string tag // Write to a writable string tag
await Client!.WriteAsync("JoeAppEngine.BTCS", await Client!.WriteAsync("TestChildObject.TestString",
new TypedValue { StringValue = testValue }); new TypedValue { StringValue = testValue });
// Read back and verify // Read back and verify
await Task.Delay(500); // Allow time for write to propagate await Task.Delay(500); // Allow time for write to propagate
var vtq = await Client.ReadAsync("JoeAppEngine.BTCS"); var vtq = await Client.ReadAsync("TestChildObject.TestString");
Assert.Equal(testValue, vtq.Value); Assert.Equal(testValue, vtq.Value);
} }
@@ -453,7 +497,7 @@ public class WriteTests : IntegrationTestBase
await readOnlyClient.ConnectAsync(); await readOnlyClient.ConnectAsync();
var ex = await Assert.ThrowsAsync<Grpc.Core.RpcException>( var ex = await Assert.ThrowsAsync<Grpc.Core.RpcException>(
() => readOnlyClient.WriteAsync("JoeAppEngine.BTCS", () => readOnlyClient.WriteAsync("TestChildObject.TestString",
new TypedValue { StringValue = "should-fail" })); new TypedValue { StringValue = "should-fail" }));
Assert.Equal(Grpc.Core.StatusCode.PermissionDenied, ex.StatusCode); Assert.Equal(Grpc.Core.StatusCode.PermissionDenied, ex.StatusCode);
} }
@@ -476,7 +520,7 @@ public class SubscribeTests : IntegrationTestBase
var receivedEvent = new TaskCompletionSource<bool>(); var receivedEvent = new TaskCompletionSource<bool>();
var subscription = await Client!.SubscribeAsync( var subscription = await Client!.SubscribeAsync(
new[] { "JoeAppEngine.Scheduler.ScanTime" }, new[] { "TestChildObject.TestInt" },
(tag, vtq) => (tag, vtq) =>
{ {
received.Add((tag, vtq)); received.Add((tag, vtq));
@@ -493,7 +537,7 @@ public class SubscribeTests : IntegrationTestBase
// Verify the VTQ has correct structure // Verify the VTQ has correct structure
var first = received[0]; var first = received[0];
Assert.Equal("JoeAppEngine.Scheduler.ScanTime", first.Tag); Assert.Equal("TestChildObject.TestInt", first.Tag);
Assert.NotNull(first.Vtq.Value); Assert.NotNull(first.Vtq.Value);
// ScanTime should be a DateTime value // ScanTime should be a DateTime value
Assert.True(first.Vtq.Timestamp > DateTime.MinValue); Assert.True(first.Vtq.Timestamp > DateTime.MinValue);
@@ -516,18 +560,18 @@ public class WriteBatchAndWaitTests : IntegrationTestBase
public async Task WriteBatchAndWait_TypeAwareComparison() public async Task WriteBatchAndWait_TypeAwareComparison()
{ {
// This test requires a writable tag and a flag tag. // This test requires a writable tag and a flag tag.
// Adjust tag names based on available tags in JoeAppEngine. // Adjust tag names based on available tags in TestChildObject.
// Example: write values and poll a flag. // Example: write values and poll a flag.
var values = new Dictionary<string, TypedValue> var values = new Dictionary<string, TypedValue>
{ {
["JoeAppEngine.BTCS"] = new TypedValue { StringValue = "BatchTest" } ["TestChildObject.TestString"] = new TypedValue { StringValue = "BatchTest" }
}; };
// Poll the same tag we wrote to (simple self-check) // Poll the same tag we wrote to (simple self-check)
var response = await Client!.WriteBatchAndWaitAsync( var response = await Client!.WriteBatchAndWaitAsync(
values, values,
flagTag: "JoeAppEngine.BTCS", flagTag: "TestChildObject.TestString",
flagValue: new TypedValue { StringValue = "BatchTest" }, flagValue: new TypedValue { StringValue = "BatchTest" },
timeoutMs: 5000, timeoutMs: 5000,
pollIntervalMs: 200); pollIntervalMs: 200);
@@ -774,11 +818,11 @@ Replace placeholders with actual restore point ID and timestamp.
- [ ] Integration test project created at `tests/ZB.MOM.WW.LmxProxy.Client.IntegrationTests/` - [ ] Integration test project created at `tests/ZB.MOM.WW.LmxProxy.Client.IntegrationTests/`
- [ ] All integration tests pass against v2 on alternate ports: - [ ] All integration tests pass against v2 on alternate ports:
- [ ] Connect/disconnect lifecycle - [ ] Connect/disconnect lifecycle
- [ ] Read string tag `JoeAppEngine.Area` — value "JoeDev", Good quality - [ ] Read string tag `TestChildObject.TestString` — value "JoeDev", Good quality
- [ ] Read writable tag `JoeAppEngine.BTCS` - [ ] Read writable tag `TestChildObject.TestString`
- [ ] Write string then read-back verification - [ ] Write string then read-back verification
- [ ] ReadBatch multiple tags - [ ] ReadBatch multiple tags
- [ ] Subscribe to `JoeAppEngine.Scheduler.ScanTime` — verify updates received with TypedValue + QualityCode - [ ] Subscribe to `TestChildObject.TestInt` — verify updates received with TypedValue + QualityCode
- [ ] WriteBatchAndWait with type-aware flag comparison - [ ] WriteBatchAndWait with type-aware flag comparison
- [ ] CheckApiKey — valid ReadWrite, valid ReadOnly, invalid - [ ] CheckApiKey — valid ReadWrite, valid ReadOnly, invalid
- [ ] Write with ReadOnly key — PermissionDenied - [ ] Write with ReadOnly key — PermissionDenied