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"); } } }