using Shouldly; using Xunit; namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Cli.Tests; /// /// Driver.FOCAS.Cli-004: every FOCAS CLI command must own one disposal mechanism for /// the FocasDriver, not two. The chosen mechanism is await using var driver /// = ...FocasDriver.DisposeAsync already calls ShutdownAsync, so /// an additional explicit driver.ShutdownAsync(...) in a finally block /// runs shutdown twice. These tests guard against that regression by scanning the /// command source files. /// [Trait("Category", "Unit")] public sealed class CommandDisposalConventionsTests { private static readonly string CommandsDir = LocateCommandsDir(); [Theory] [InlineData("ProbeCommand.cs")] [InlineData("ReadCommand.cs")] [InlineData("WriteCommand.cs")] [InlineData("SubscribeCommand.cs")] public void Command_does_not_call_ShutdownAsync_explicitly(string commandFile) { var path = Path.Combine(CommandsDir, commandFile); File.Exists(path).ShouldBeTrue($"Expected {path} to exist."); var source = File.ReadAllText(path); // The await-using statement is the single disposal mechanism. An explicit // driver.ShutdownAsync(...) call (typically inside a finally block) re-invokes // a shutdown path that DisposeAsync already runs and is the smell -004 flags. source.ShouldNotContain("driver.ShutdownAsync("); } [Theory] [InlineData("ProbeCommand.cs")] [InlineData("ReadCommand.cs")] [InlineData("WriteCommand.cs")] [InlineData("SubscribeCommand.cs")] public void Command_uses_await_using_for_FocasDriver(string commandFile) { var path = Path.Combine(CommandsDir, commandFile); var source = File.ReadAllText(path); source.ShouldContain("await using var driver = new FocasDriver("); } private static string LocateCommandsDir() { // Walk up from the test assembly bin/ folder to the repo root, then into the // source project's Commands/ directory. The test-host puts CWD somewhere under // bin/Debug/net10.0 so we resolve relative to AppContext.BaseDirectory. var dir = new DirectoryInfo(AppContext.BaseDirectory); while (dir is not null && !File.Exists(Path.Combine(dir.FullName, "ZB.MOM.WW.OtOpcUa.slnx"))) dir = dir.Parent; dir.ShouldNotBeNull("Could not find solution root (ZB.MOM.WW.OtOpcUa.slnx)."); return Path.Combine( dir!.FullName, "src", "Drivers", "Cli", "ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Cli", "Commands"); } }