using Microsoft.Extensions.Logging.Abstractions;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Browsing;
using ZB.MOM.WW.OtOpcUa.Commons.Browsing;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Browsing;
///
/// lmxopcua#815: the AdminUI live browse (Galaxy / OpcUaClient bespoke browsers, and the Discover-backed universal
/// browser) resolves the config's credential references and sends them to the author-chosen endpoint — an
/// out-of-namespace reference is refused before any browser is opened.
///
[Trait("Category", "Unit")]
public sealed class BrowserSessionCredentialScopeTests
{
[Theory]
[InlineData(DriverTypeNames.Galaxy, """{"Gateway":{"Endpoint":"http://evil:5001","ApiKeySecretRef":"env:ConnectionStrings__ConfigDb"}}""")]
[InlineData(DriverTypeNames.OpcUaClient, """{"EndpointUrl":"opc.tcp://evil:4840","Password":"secret:otopcua/historian/api-key"}""")]
[InlineData(DriverTypeNames.Heidenhain, """{"Access":{"InspectPassword":"secret:galaxy/main/apikey"}}""")]
public async Task Out_of_namespace_reference_never_opens_a_browser(string driverType, string config)
{
var bespoke = new RecordingBrowser(DriverTypeNames.Galaxy);
var bespoke2 = new RecordingBrowser(DriverTypeNames.OpcUaClient);
var universal = new RecordingUniversal();
var svc = new BrowserSessionService(
[bespoke, bespoke2], new BrowseSessionRegistry(), NullLogger.Instance, universal, null!, null!);
var result = await svc.OpenAsync(driverType, config, TestContext.Current.CancellationToken);
result.Ok.ShouldBeFalse();
result.Message.ShouldNotBeNull().ShouldContain("lmxopcua#815");
bespoke.Opens.ShouldBe(0);
bespoke2.Opens.ShouldBe(0);
universal.Opens.ShouldBe(0);
}
/// Positive control: a namespaced reference reaches the browser.
[Fact]
public async Task A_namespaced_reference_reaches_the_browser()
{
var bespoke = new RecordingBrowser(DriverTypeNames.Galaxy);
var svc = new BrowserSessionService(
[bespoke], new BrowseSessionRegistry(), NullLogger.Instance, new RecordingUniversal(), null!, null!);
await svc.OpenAsync(DriverTypeNames.Galaxy, """{"Gateway":{"ApiKeySecretRef":"secret:galaxy/main/apikey"}}""",
TestContext.Current.CancellationToken);
bespoke.Opens.ShouldBe(1);
}
private sealed class RecordingBrowser(string driverType) : IDriverBrowser
{
public int Opens { get; private set; }
public string DriverType => driverType;
public Task OpenAsync(string configJson, CancellationToken cancellationToken)
{
Opens++;
throw new InvalidOperationException("opened");
}
}
private sealed class RecordingUniversal : IUniversalDriverBrowser
{
public int Opens { get; private set; }
public bool CanBrowse(string driverType, string configJson) => true;
public Task OpenAsync(string driverType, string configJson, CancellationToken ct)
{
Opens++;
throw new InvalidOperationException("opened");
}
}
}