diff --git a/src/ZB.MOM.WW.ScadaBridge.CLI/Commands/NotificationCommands.cs b/src/ZB.MOM.WW.ScadaBridge.CLI/Commands/NotificationCommands.cs index 27a5bd79..913655ce 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CLI/Commands/NotificationCommands.cs +++ b/src/ZB.MOM.WW.ScadaBridge.CLI/Commands/NotificationCommands.cs @@ -144,6 +144,7 @@ public static class NotificationCommands updateCmd.Add(SmtpCredentialsOption); updateCmd.Add(SmtpOAuth2AuthorityOption); updateCmd.Add(SmtpOAuth2ScopeOption); + updateCmd.Add(SmtpTransportOption); updateCmd.SetAction(async (ParseResult result) => { return await CommandHelpers.ExecuteCommandAsync( @@ -187,6 +188,20 @@ public static class NotificationCommands "(optional; preserves existing / M365 default if omitted)", }; + private static readonly Option SmtpTransportOption = CreateTransportOption(); + + private static Option CreateTransportOption() + { + var option = new Option("--transport") + { + Description = "Email transport: Smtp or Ews (optional; preserves existing if omitted). " + + "Under Ews, --server is the full EWS URL (https://…/ews/exchange.asmx), " + + "--auth-mode must be basic, and --port/--tls-mode are unused.", + }; + option.AcceptOnlyFromAmong("Smtp", "Ews"); + return option; + } + private static Option CreateTlsModeOption() { var option = new Option("--tls-mode") @@ -200,8 +215,8 @@ public static class NotificationCommands /// /// Builds the from a parsed smtp update /// invocation. The optional --tls-mode / --credentials / - /// --oauth2-authority / --oauth2-scope flags map to null when omitted - /// so the server-side handler preserves the existing values. + /// --oauth2-authority / --oauth2-scope / --transport flags map to + /// null when omitted so the server-side handler preserves the existing values. /// /// The parsed command-line result from the smtp update invocation. /// An populated from the parsed result. @@ -216,7 +231,8 @@ public static class NotificationCommands var credentials = result.GetValue(SmtpCredentialsOption); var oauth2Authority = result.GetValue(SmtpOAuth2AuthorityOption); var oauth2Scope = result.GetValue(SmtpOAuth2ScopeOption); - return new UpdateSmtpConfigCommand(id, server, port, authMode, from, tlsMode, credentials, oauth2Authority, oauth2Scope); + var transport = result.GetValue(SmtpTransportOption); + return new UpdateSmtpConfigCommand(id, server, port, authMode, from, tlsMode, credentials, oauth2Authority, oauth2Scope, transport); } private static Command BuildSms(Option urlOption, Option formatOption, Option usernameOption, Option passwordOption) diff --git a/tests/ZB.MOM.WW.ScadaBridge.CLI.Tests/Commands/SmtpUpdateCommandTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CLI.Tests/Commands/SmtpUpdateCommandTests.cs index f55e4e5e..89d88fe7 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.CLI.Tests/Commands/SmtpUpdateCommandTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.CLI.Tests/Commands/SmtpUpdateCommandTests.cs @@ -6,10 +6,11 @@ namespace ZB.MOM.WW.ScadaBridge.CLI.Tests.Commands; /// /// Tests for the scadabridge notification smtp update subcommand. The command -/// gained two optional flags — --tls-mode and --credentials — that plumb -/// through to . These tests pin that the flags -/// parse, are genuinely optional (non-breaking), and that --tls-mode rejects -/// values outside the canonical {None, StartTLS, SSL} set. +/// gained optional flags — --tls-mode, --credentials and --transport — +/// that plumb through to . These tests pin that the +/// flags parse, are genuinely optional (non-breaking), that --tls-mode rejects +/// values outside the canonical {None, StartTLS, SSL} set, and that --transport +/// rejects values outside {Smtp, Ews}. /// public class SmtpUpdateCommandTests { @@ -91,6 +92,46 @@ public class SmtpUpdateCommandTests Assert.NotEmpty(parse.Errors); } + [Fact] + public void Update_WithTransportEws_ProducesCommandCarryingIt() + { + var parse = ParseUpdate( + "--id", "1", "--server", "https://mail.example.com/ews/exchange.asmx", "--port", "443", + "--auth-mode", "Basic", "--from-address", "noreply@example.com", + "--transport", "Ews"); + + Assert.Empty(parse.Errors); + var cmd = NotificationCommands.BuildUpdateSmtpConfigCommand(parse); + + Assert.Equal("Ews", cmd.Transport); + } + + [Fact] + public void Update_WithoutTransport_ProducesCommandWithNullTransport() + { + var parse = ParseUpdate( + "--id", "2", "--server", "smtp.example.com", "--port", "25", + "--auth-mode", "Basic", "--from-address", "noreply@example.com"); + + Assert.Empty(parse.Errors); + var cmd = NotificationCommands.BuildUpdateSmtpConfigCommand(parse); + + Assert.Null(cmd.Transport); + } + + [Theory] + [InlineData("Graph")] + [InlineData("ews")] // AcceptOnlyFromAmong is case-sensitive: constrain to canonical spelling + public void Update_TransportOption_RejectsValuesOutsideCanonicalSet(string value) + { + var parse = ParseUpdate( + "--id", "1", "--server", "smtp.example.com", "--port", "587", + "--auth-mode", "Basic", "--from-address", "noreply@example.com", + "--transport", value); + + Assert.NotEmpty(parse.Errors); + } + [Fact] public void Update_TlsModeAndCredentials_AreNotRequired() {