OpcUaServiceBuilder provides fluent API for constructing OpcUaService with dependency overrides (IMxProxy, IGalaxyRepository, IMxAccessClient). WithMxAccessClient skips the STA thread and COM interop entirely. OpcUaServerFixture wraps the service lifecycle with automatic port allocation (atomic counter starting at 16000), guaranteed cleanup via IAsyncLifetime, and factory methods for common test scenarios: - WithFakes() — FakeMxProxy + FakeGalaxyRepository with standard data - WithFakeMxAccessClient() — bypasses COM, fastest for most tests Also adds TestData helper with reusable hierarchy/attributes matching gr/layout.md, and 5 fixture tests verifying startup, shutdown, port isolation, and address space building. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
87 lines
2.8 KiB
C#
87 lines
2.8 KiB
C#
using System.Diagnostics;
|
|
using System.Threading.Tasks;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.LmxOpcUa.Host.Domain;
|
|
|
|
namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
|
{
|
|
public class OpcUaServerFixtureTests
|
|
{
|
|
[Fact]
|
|
public async Task WithFakes_StartsAndStops()
|
|
{
|
|
var fixture = OpcUaServerFixture.WithFakes();
|
|
await fixture.InitializeAsync();
|
|
|
|
fixture.Service.ShouldNotBeNull();
|
|
fixture.Service.MxClient.ShouldNotBeNull();
|
|
fixture.Service.MxClient!.State.ShouldBe(ConnectionState.Connected);
|
|
fixture.Service.GalaxyStatsInstance.ShouldNotBeNull();
|
|
fixture.Service.GalaxyStatsInstance!.GalaxyName.ShouldBe("TestGalaxy");
|
|
fixture.OpcUaPort.ShouldBeGreaterThan(16000);
|
|
fixture.EndpointUrl.ShouldContain(fixture.OpcUaPort.ToString());
|
|
|
|
await fixture.DisposeAsync();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task WithFakeMxAccessClient_SkipsCom()
|
|
{
|
|
var mxClient = new FakeMxAccessClient();
|
|
var fixture = OpcUaServerFixture.WithFakeMxAccessClient(mxClient);
|
|
await fixture.InitializeAsync();
|
|
|
|
fixture.Service.MxClient.ShouldBe(mxClient);
|
|
mxClient.State.ShouldBe(ConnectionState.Connected);
|
|
|
|
await fixture.DisposeAsync();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task MultipleFixtures_GetUniquePortsAutomatically()
|
|
{
|
|
var fixture1 = OpcUaServerFixture.WithFakeMxAccessClient();
|
|
var fixture2 = OpcUaServerFixture.WithFakeMxAccessClient();
|
|
|
|
fixture1.OpcUaPort.ShouldNotBe(fixture2.OpcUaPort);
|
|
|
|
// Both can start without port conflicts
|
|
await fixture1.InitializeAsync();
|
|
await fixture2.InitializeAsync();
|
|
|
|
fixture1.Service.ShouldNotBeNull();
|
|
fixture2.Service.ShouldNotBeNull();
|
|
|
|
await fixture1.DisposeAsync();
|
|
await fixture2.DisposeAsync();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Shutdown_CompletesWithin30Seconds()
|
|
{
|
|
var fixture = OpcUaServerFixture.WithFakes();
|
|
await fixture.InitializeAsync();
|
|
|
|
var sw = Stopwatch.StartNew();
|
|
await fixture.DisposeAsync();
|
|
sw.Stop();
|
|
|
|
sw.Elapsed.TotalSeconds.ShouldBeLessThan(30);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task WithFakes_BuildsAddressSpace()
|
|
{
|
|
var fixture = OpcUaServerFixture.WithFakes();
|
|
await fixture.InitializeAsync();
|
|
|
|
fixture.Service.GalaxyStatsInstance!.ObjectCount.ShouldBe(5);
|
|
fixture.Service.GalaxyStatsInstance.AttributeCount.ShouldBe(6);
|
|
fixture.Service.GalaxyStatsInstance.DbConnected.ShouldBe(true);
|
|
|
|
await fixture.DisposeAsync();
|
|
}
|
|
}
|
|
}
|