using Shouldly; using Xunit; namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests; /// /// Gitea #516 — FOCAS was excluded from the first pass because it derives MORE than options from config: /// the Backend key selects its , injected at construction. Adopting /// a new FocasDriverOptions alone would poll a NEW device/tag set through the OLD backend, which /// looks like it worked and does not. /// ParseBinding now returns options + backend together and InitializeAsync adopts both /// or neither. These tests pin the BACKEND moving — the half that was missing — because a test that only /// checked options would have passed against the broken version. /// [Trait("Category", "Unit")] public sealed class FocasReinitConfigAdoptionTests { /// /// Reinitializing onto Backend: "unimplemented" must make the driver adopt that backend, whose /// EnsureUsable() throws by design. A driver still holding the constructor's wire /// backend would not throw — so the throw is a direct read of which backend is live. /// [Fact] public async Task Reinitialize_adopts_a_changed_backend_not_just_the_options() { var driver = FocasDriverFactoryExtensions.CreateInstance( "focas-1", """{"backend":"wire","devices":[]}"""); var ex = await Record.ExceptionAsync(() => driver.ReinitializeAsync( """{"backend":"unimplemented","devices":[]}""", CancellationToken.None)); ex.ShouldBeOfType( "FocasDriver adopted a reinitialized config but kept its ORIGINAL backend — the half-applied " + "adoption (#516) that would poll a new device/tag set through the old backend"); } /// The other direction: moving OFF the unimplemented backend must also take effect, so the /// adoption is not a one-way latch. [Fact] public async Task Reinitialize_away_from_the_unimplemented_backend_also_takes_effect() { var driver = FocasDriverFactoryExtensions.CreateInstance( "focas-1", """{"backend":"unimplemented","devices":[]}"""); // Sanity: the constructor-supplied backend is the throwing one. (await Record.ExceptionAsync(() => driver.InitializeAsync("{}", CancellationToken.None))) .ShouldBeOfType(); var ex = await Record.ExceptionAsync(() => driver.ReinitializeAsync( """{"backend":"wire","devices":[]}""", CancellationToken.None)); ex.ShouldNotBeOfType( "FocasDriver stayed on the unimplemented backend after a reinitialize that selected 'wire'"); } /// An empty/placeholder document keeps the constructor-supplied pair, so the lifecycle tests /// that pass "{}" keep meaning what they meant. [Fact] public async Task Reinitialize_with_an_empty_document_keeps_the_constructor_backend() { var driver = FocasDriverFactoryExtensions.CreateInstance( "focas-1", """{"backend":"unimplemented","devices":[]}"""); (await Record.ExceptionAsync(() => driver.ReinitializeAsync("{}", CancellationToken.None))) .ShouldBeOfType(); } }