using System.Collections.Generic; using System.Threading.Tasks; using Opc.Ua; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Host.Configuration; using ZB.MOM.WW.OtOpcUa.Tests.Helpers; namespace ZB.MOM.WW.OtOpcUa.Tests.Integration { public class RedundancyTests { [Fact] public async Task Server_WithRedundancyDisabled_ReportsNone() { var fixture = OpcUaServerFixture.WithFakeMxAccessClient(); await fixture.InitializeAsync(); try { using var client = new OpcUaTestClient(); await client.ConnectAsync(fixture.EndpointUrl); var redundancySupport = client.Read(VariableIds.Server_ServerRedundancy_RedundancySupport); ((int)redundancySupport.Value).ShouldBe((int)RedundancySupport.None); var serviceLevel = client.Read(VariableIds.Server_ServiceLevel); ((byte)serviceLevel.Value).ShouldBe((byte)255); } finally { await fixture.DisposeAsync(); } } [Fact] public async Task Server_WithRedundancyEnabled_ReportsConfiguredMode() { var redundancy = new RedundancyConfiguration { Enabled = true, Mode = "Warm", Role = "Primary", ServiceLevelBase = 200, ServerUris = new List { "urn:test:primary", "urn:test:secondary" } }; var fixture = OpcUaServerFixture.WithFakeMxAccessClient( redundancy: redundancy, applicationUri: "urn:test:primary"); await fixture.InitializeAsync(); try { using var client = new OpcUaTestClient(); await client.ConnectAsync(fixture.EndpointUrl); var redundancySupport = client.Read(VariableIds.Server_ServerRedundancy_RedundancySupport); ((int)redundancySupport.Value).ShouldBe((int)RedundancySupport.Warm); } finally { await fixture.DisposeAsync(); } } [Fact] public async Task Server_Primary_HasHigherServiceLevel_ThanSecondary() { var sharedUris = new List { "urn:test:primary", "urn:test:secondary" }; var primaryRedundancy = new RedundancyConfiguration { Enabled = true, Mode = "Warm", Role = "Primary", ServiceLevelBase = 200, ServerUris = sharedUris }; var secondaryRedundancy = new RedundancyConfiguration { Enabled = true, Mode = "Warm", Role = "Secondary", ServiceLevelBase = 200, ServerUris = sharedUris }; var primaryFixture = OpcUaServerFixture.WithFakeMxAccessClient( redundancy: primaryRedundancy, applicationUri: "urn:test:primary"); var secondaryFixture = OpcUaServerFixture.WithFakeMxAccessClient( redundancy: secondaryRedundancy, applicationUri: "urn:test:secondary", serverName: "TestGalaxy2"); await primaryFixture.InitializeAsync(); await secondaryFixture.InitializeAsync(); try { using var primaryClient = new OpcUaTestClient(); await primaryClient.ConnectAsync(primaryFixture.EndpointUrl); var primaryLevel = (byte)primaryClient.Read(VariableIds.Server_ServiceLevel).Value; using var secondaryClient = new OpcUaTestClient(); await secondaryClient.ConnectAsync(secondaryFixture.EndpointUrl); var secondaryLevel = (byte)secondaryClient.Read(VariableIds.Server_ServiceLevel).Value; primaryLevel.ShouldBeGreaterThan(secondaryLevel); } finally { await secondaryFixture.DisposeAsync(); await primaryFixture.DisposeAsync(); } } [Fact] public async Task Server_WithRedundancyEnabled_ExposesServerUriArray() { var serverUris = new List { "urn:test:server1", "urn:test:server2" }; var redundancy = new RedundancyConfiguration { Enabled = true, Mode = "Warm", Role = "Primary", ServiceLevelBase = 200, ServerUris = serverUris }; var fixture = OpcUaServerFixture.WithFakeMxAccessClient( redundancy: redundancy, applicationUri: "urn:test:server1"); await fixture.InitializeAsync(); try { using var client = new OpcUaTestClient(); await client.ConnectAsync(fixture.EndpointUrl); var uriArrayValue = client.Read(VariableIds.Server_ServerRedundancy_ServerUriArray); // ServerUriArray may not be exposed if the SDK doesn't create the non-transparent // redundancy node type automatically. If the value is null, the server logged a // warning and the test is informational rather than a hard failure. if (uriArrayValue.Value != null) { var uris = (string[])uriArrayValue.Value; uris.Length.ShouldBe(2); uris.ShouldContain("urn:test:server1"); uris.ShouldContain("urn:test:server2"); } } finally { await fixture.DisposeAsync(); } } [Fact] public async Task TwoServers_BothExposeSameRedundantSet() { var sharedUris = new List { "urn:test:a", "urn:test:b" }; var configA = new RedundancyConfiguration { Enabled = true, Mode = "Warm", Role = "Primary", ServiceLevelBase = 200, ServerUris = sharedUris }; var configB = new RedundancyConfiguration { Enabled = true, Mode = "Warm", Role = "Secondary", ServiceLevelBase = 200, ServerUris = sharedUris }; var fixtureA = OpcUaServerFixture.WithFakeMxAccessClient( redundancy: configA, applicationUri: "urn:test:a"); var fixtureB = OpcUaServerFixture.WithFakeMxAccessClient( redundancy: configB, applicationUri: "urn:test:b", serverName: "TestGalaxy2"); await fixtureA.InitializeAsync(); await fixtureB.InitializeAsync(); try { using var clientA = new OpcUaTestClient(); await clientA.ConnectAsync(fixtureA.EndpointUrl); var modeA = (int)clientA.Read(VariableIds.Server_ServerRedundancy_RedundancySupport).Value; using var clientB = new OpcUaTestClient(); await clientB.ConnectAsync(fixtureB.EndpointUrl); var modeB = (int)clientB.Read(VariableIds.Server_ServerRedundancy_RedundancySupport).Value; modeA.ShouldBe((int)RedundancySupport.Warm); modeB.ShouldBe((int)RedundancySupport.Warm); } finally { await fixtureB.DisposeAsync(); await fixtureA.DisposeAsync(); } } } }