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

@@ -13,7 +13,19 @@
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**.
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
@@ -58,6 +70,11 @@ ssh windev "powershell -Command \"@'
\"ChannelCapacity\": 1000,
\"ChannelFullMode\": \"DropOldest\"
},
\"HealthCheck\": {
\"Enabled\": true,
\"TestTagAddress\": \"TestChildObject.TestBool\",
\"MaxStaleDataMinutes\": 5
},
\"Tls\": {
\"Enabled\": false
},
@@ -377,35 +394,62 @@ namespace ZB.MOM.WW.LmxProxy.Client.IntegrationTests;
public class ReadTests : IntegrationTestBase
{
[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("JoeAppEngine.Area");
Assert.NotNull(vtq.Value);
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)
var vtq = await Client!.ReadAsync("TestChildObject.TestBool");
Assert.IsType<bool>(vtq.Value);
Assert.True(vtq.Quality.IsGood());
}
[Fact]
public async Task Read_WritableTag_ReturnsTypedValue()
public async Task Read_IntTag_ReturnsIntValue()
{
// JoeAppEngine.BTCS is a writable tag
var vtq = await Client!.ReadAsync("JoeAppEngine.BTCS");
Assert.NotNull(vtq.Value);
// Verify timestamp is recent (within last hour)
var vtq = await Client!.ReadAsync("TestChildObject.TestInt");
Assert.True(vtq.Value is int or long);
Assert.True(vtq.Quality.IsGood());
}
[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));
}
[Fact]
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);
Assert.Equal(2, results.Count);
Assert.True(results.ContainsKey("JoeAppEngine.Area"));
Assert.True(results.ContainsKey("JoeAppEngine.BTCS"));
Assert.True(results.ContainsKey("TestChildObject.TestString"));
Assert.True(results.ContainsKey("TestChildObject.TestString"));
}
[Fact]
@@ -437,12 +481,12 @@ public class WriteTests : IntegrationTestBase
{
string testValue = $"IntTest-{DateTime.UtcNow:HHmmss}";
// Write to a writable string tag
await Client!.WriteAsync("JoeAppEngine.BTCS",
await Client!.WriteAsync("TestChildObject.TestString",
new TypedValue { StringValue = testValue });
// Read back and verify
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);
}
@@ -453,7 +497,7 @@ public class WriteTests : IntegrationTestBase
await readOnlyClient.ConnectAsync();
var ex = await Assert.ThrowsAsync<Grpc.Core.RpcException>(
() => readOnlyClient.WriteAsync("JoeAppEngine.BTCS",
() => readOnlyClient.WriteAsync("TestChildObject.TestString",
new TypedValue { StringValue = "should-fail" }));
Assert.Equal(Grpc.Core.StatusCode.PermissionDenied, ex.StatusCode);
}
@@ -476,7 +520,7 @@ public class SubscribeTests : IntegrationTestBase
var receivedEvent = new TaskCompletionSource<bool>();
var subscription = await Client!.SubscribeAsync(
new[] { "JoeAppEngine.Scheduler.ScanTime" },
new[] { "TestChildObject.TestInt" },
(tag, vtq) =>
{
received.Add((tag, vtq));
@@ -493,7 +537,7 @@ public class SubscribeTests : IntegrationTestBase
// Verify the VTQ has correct structure
var first = received[0];
Assert.Equal("JoeAppEngine.Scheduler.ScanTime", first.Tag);
Assert.Equal("TestChildObject.TestInt", first.Tag);
Assert.NotNull(first.Vtq.Value);
// ScanTime should be a DateTime value
Assert.True(first.Vtq.Timestamp > DateTime.MinValue);
@@ -516,18 +560,18 @@ public class WriteBatchAndWaitTests : IntegrationTestBase
public async Task WriteBatchAndWait_TypeAwareComparison()
{
// 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.
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)
var response = await Client!.WriteBatchAndWaitAsync(
values,
flagTag: "JoeAppEngine.BTCS",
flagTag: "TestChildObject.TestString",
flagValue: new TypedValue { StringValue = "BatchTest" },
timeoutMs: 5000,
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/`
- [ ] All integration tests pass against v2 on alternate ports:
- [ ] Connect/disconnect lifecycle
- [ ] Read string tag `JoeAppEngine.Area` — value "JoeDev", Good quality
- [ ] Read writable tag `JoeAppEngine.BTCS`
- [ ] Read string tag `TestChildObject.TestString` — value "JoeDev", Good quality
- [ ] Read writable tag `TestChildObject.TestString`
- [ ] Write string then read-back verification
- [ ] 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
- [ ] CheckApiKey — valid ReadWrite, valid ReadOnly, invalid
- [ ] Write with ReadOnly key — PermissionDenied