using System;
using System.Threading;
using System.Threading.Tasks;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend.Galaxy;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend.MxAccess;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Sta;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Shared.Contracts;
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests
{
///
/// End-to-end smoke against the live MXAccess COM runtime + Galaxy ZB DB on this dev box.
/// Skipped when ArchestrA bootstrap (aaBootstrap) isn't running. Verifies the
/// ported can connect to LMXProxyServer, the
/// can answer Discover against the live ZB schema,
/// and a one-shot read returns a valid VTQ for the first deployed attribute it finds.
///
[Trait("Category", "LiveMxAccess")]
public sealed class MxAccessLiveSmokeTests
{
private static GalaxyRepositoryOptions DevZb() => new()
{
ConnectionString = "Server=localhost;Database=ZB;Integrated Security=True;TrustServerCertificate=True;Encrypt=False;Connect Timeout=2;",
CommandTimeoutSeconds = 10,
};
private static async Task ArchestraReachableAsync()
{
try
{
var repo = new GalaxyRepository(DevZb());
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
if (!await repo.TestConnectionAsync(cts.Token)) return false;
using var sc = new System.ServiceProcess.ServiceController("aaBootstrap");
return sc.Status == System.ServiceProcess.ServiceControllerStatus.Running;
}
catch { return false; }
}
[Fact]
public async Task Connect_to_local_LMXProxyServer_succeeds()
{
if (!await ArchestraReachableAsync()) return;
using var pump = new StaPump("MxA-test-pump");
await pump.WaitForStartedAsync();
using var mx = new MxAccessClient(pump, new MxProxyAdapter(), "OtOpcUa-MxAccessSmoke");
var handle = await mx.ConnectAsync();
handle.ShouldBeGreaterThan(0);
mx.IsConnected.ShouldBeTrue();
}
[Fact]
public async Task Backend_OpenSession_then_Discover_returns_objects_with_attributes()
{
if (!await ArchestraReachableAsync()) return;
using var pump = new StaPump("MxA-test-pump");
await pump.WaitForStartedAsync();
using var mx = new MxAccessClient(pump, new MxProxyAdapter(), "OtOpcUa-MxAccessSmoke");
var backend = new MxAccessGalaxyBackend(new GalaxyRepository(DevZb()), mx);
var session = await backend.OpenSessionAsync(new OpenSessionRequest { DriverInstanceId = "smoke" }, CancellationToken.None);
session.Success.ShouldBeTrue(session.Error);
var resp = await backend.DiscoverAsync(new DiscoverHierarchyRequest { SessionId = session.SessionId }, CancellationToken.None);
resp.Success.ShouldBeTrue(resp.Error);
resp.Objects.Length.ShouldBeGreaterThan(0);
await backend.CloseSessionAsync(new CloseSessionRequest { SessionId = session.SessionId }, CancellationToken.None);
}
///
/// Live one-shot read against any attribute we discover. Best-effort — passes silently
/// if no readable attribute is exposed (some Galaxy installs are configuration-only;
/// we only assert the call shape is correct, not a specific value).
///
[Fact]
public async Task Backend_ReadValues_against_discovered_attribute_returns_a_response_shape()
{
if (!await ArchestraReachableAsync()) return;
using var pump = new StaPump("MxA-test-pump");
await pump.WaitForStartedAsync();
using var mx = new MxAccessClient(pump, new MxProxyAdapter(), "OtOpcUa-MxAccessSmoke");
var backend = new MxAccessGalaxyBackend(new GalaxyRepository(DevZb()), mx);
var session = await backend.OpenSessionAsync(new OpenSessionRequest { DriverInstanceId = "smoke" }, CancellationToken.None);
var disc = await backend.DiscoverAsync(new DiscoverHierarchyRequest { SessionId = session.SessionId }, CancellationToken.None);
var firstAttr = System.Linq.Enumerable.FirstOrDefault(disc.Objects, o => o.Attributes.Length > 0);
if (firstAttr is null)
{
await backend.CloseSessionAsync(new CloseSessionRequest { SessionId = session.SessionId }, CancellationToken.None);
return;
}
var fullRef = $"{firstAttr.TagName}.{firstAttr.Attributes[0].AttributeName}";
var read = await backend.ReadValuesAsync(
new ReadValuesRequest { SessionId = session.SessionId, TagReferences = new[] { fullRef } },
CancellationToken.None);
read.Success.ShouldBeTrue();
read.Values.Length.ShouldBe(1);
// We don't assert the value (it may be Bad/Uncertain depending on what's running);
// we only assert the response shape is correct end-to-end.
read.Values[0].TagReference.ShouldBe(fullRef);
await backend.CloseSessionAsync(new CloseSessionRequest { SessionId = session.SessionId }, CancellationToken.None);
}
}
}