diff --git a/lmxproxy/docs/plans/phase-4-host-health-metrics.md b/lmxproxy/docs/plans/phase-4-host-health-metrics.md index 9095de2..aa12daf 100644 --- a/lmxproxy/docs/plans/phase-4-host-health-metrics.md +++ b/lmxproxy/docs/plans/phase-4-host-health-metrics.md @@ -165,7 +165,7 @@ public class DetailedHealthCheckService : IHealthCheck private readonly IScadaClient _scadaClient; private readonly string _testTagAddress; - public DetailedHealthCheckService(IScadaClient scadaClient, string testTagAddress = "System.Heartbeat") { ... } + public DetailedHealthCheckService(IScadaClient scadaClient, string testTagAddress = "TestChildObject.TestBool") { ... } public async Task CheckHealthAsync( HealthCheckContext context, diff --git a/lmxproxy/docs/plans/phase-7-integration-deployment.md b/lmxproxy/docs/plans/phase-7-integration-deployment.md index 42ead77..25ceac5 100644 --- a/lmxproxy/docs/plans/phase-7-integration-deployment.md +++ b/lmxproxy/docs/plans/phase-7-integration-deployment.md @@ -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(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(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(vtq.Value); + Assert.True(vtq.Quality.IsGood()); + } + + [Fact] + public async Task Read_StringTag_ReturnsStringValue() + { + var vtq = await Client!.ReadAsync("TestChildObject.TestString"); + Assert.IsType(vtq.Value); + Assert.True(vtq.Quality.IsGood()); + } + + [Fact] + public async Task Read_DateTimeTag_ReturnsDateTimeValue() + { + var vtq = await Client!.ReadAsync("TestChildObject.TestDateTime"); + Assert.IsType(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( - () => 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(); 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 { - ["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