diff --git a/tools/Azure.Mcp.Tools.Communication/src/Services/CommunicationService.cs b/tools/Azure.Mcp.Tools.Communication/src/Services/CommunicationService.cs index 185e0f1367..1a8fee70a9 100644 --- a/tools/Azure.Mcp.Tools.Communication/src/Services/CommunicationService.cs +++ b/tools/Azure.Mcp.Tools.Communication/src/Services/CommunicationService.cs @@ -3,6 +3,7 @@ using Azure.Communication.Email; using Azure.Communication.Sms; +using Azure.Core.Pipeline; using Azure.Mcp.Core.Options; using Azure.Mcp.Core.Services.Azure; using Azure.Mcp.Core.Services.Azure.Tenant; @@ -44,7 +45,11 @@ public async Task> SendSmsAsync( { // Create SMS client using Azure credential from base class and endpoint var credential = await GetCredential(tenantId, cancellationToken); - var smsClient = new SmsClient(new Uri(endpoint), credential); + + var smsClientOptions = ConfigureRetryPolicy(AddDefaultPolicies(new SmsClientOptions()), retryPolicy); + smsClientOptions.Transport = new HttpClientTransport(TenantService.GetClient()); + + var smsClient = new SmsClient(new Uri(endpoint), credential, smsClientOptions); var sendOptions = new SmsSendOptions(enableDeliveryReport) { @@ -124,7 +129,11 @@ public async Task> SendSmsAsync( { // Create email client with credential from base class var credential = await GetCredential(tenantId, cancellationToken); - var emailClient = new EmailClient(new Uri(endpoint), credential); + + var emailClientOptions = ConfigureRetryPolicy(AddDefaultPolicies(new EmailClientOptions()), retryPolicy); + emailClientOptions.Transport = new HttpClientTransport(TenantService.GetClient()); + + var emailClient = new EmailClient(new Uri(endpoint), credential, emailClientOptions); // Create the email content var emailContent = new EmailContent(subject); diff --git a/tools/Azure.Mcp.Tools.Communication/tests/Azure.Mcp.Tools.Communication.LiveTests/CommunicationCommandTests.cs b/tools/Azure.Mcp.Tools.Communication/tests/Azure.Mcp.Tools.Communication.LiveTests/CommunicationCommandTests.cs index 3ea9f25e96..574c6d721d 100644 --- a/tools/Azure.Mcp.Tools.Communication/tests/Azure.Mcp.Tools.Communication.LiveTests/CommunicationCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Communication/tests/Azure.Mcp.Tools.Communication.LiveTests/CommunicationCommandTests.cs @@ -3,34 +3,110 @@ using System.Text.Json; using Azure.Mcp.Tests; using Azure.Mcp.Tests.Client; +using Azure.Mcp.Tests.Client.Helpers; +using Azure.Mcp.Tests.Generated.Models; using Xunit; namespace Azure.Mcp.Tools.Communication.LiveTests; [Trait("Command", "SmsSendCommand")] -public class CommunicationCommandTests : CommandTestsBase +public class CommunicationCommandTests(ITestOutputHelper output, TestProxyFixture fixture) : RecordedCommandTestsBase(output, fixture) { - public CommunicationCommandTests(ITestOutputHelper output) : base(output) + private const string EmptyGuid = "00000000-0000-0000-0000-000000000000"; + private string? endpointRecorded; + private string? fromSms; + private string? toSms; + public override bool EnableDefaultSanitizerAdditions => false; + + public override async ValueTask InitializeAsync() { + await LoadSettingsAsync(); + if (TestMode == Tests.Helpers.TestMode.Playback) + { + endpointRecorded = "https://sanitized.communication.azure.com"; + fromSms = "12345678900"; + toSms = "12345678901"; + } + else + { + Settings.DeploymentOutputs.TryGetValue("COMMUNICATION_SERVICES_ENDPOINT", out endpointRecorded); + Settings.DeploymentOutputs.TryGetValue("COMMUNICATION_SERVICES_FROM_PHONE", out var tempFromSms); + fromSms = tempFromSms!.Substring(1); // Remove '+' for regex matching + Settings.DeploymentOutputs.TryGetValue("COMMUNICATION_SERVICES_TO_PHONE", out var tempToSms); + toSms = tempToSms!.Substring(1); // Remove '+' for regex matching + } + + await base.InitializeAsync(); } + public override List GeneralRegexSanitizers => + [ + ..base.GeneralRegexSanitizers, + new GeneralRegexSanitizer(new GeneralRegexSanitizerBody() + { + Regex = Settings.ResourceBaseName, + Value = "Sanitized", + }), + new GeneralRegexSanitizer(new GeneralRegexSanitizerBody() + { + Regex = Settings.SubscriptionId, + Value = EmptyGuid, + }), + new GeneralRegexSanitizer(new GeneralRegexSanitizerBody() + { + Regex = endpointRecorded, + Value = "https://sanitized.communication.azure.com", + }) + ]; + + public override List BodyKeySanitizers => + [ + ..base.BodyKeySanitizers, + new BodyKeySanitizer(new BodyKeySanitizerBody("$..to") + { + Value = "12345678901" + }), + new BodyKeySanitizer(new BodyKeySanitizerBody("$.from") + { + Value = "12345678900" + }), + new BodyKeySanitizer(new BodyKeySanitizerBody("$..repeatabilityRequestId") + { + Value = EmptyGuid + }), + new BodyKeySanitizer(new BodyKeySanitizerBody("$..repeatabilityFirstSent") + { + Value = "Fri, 30 Jan 2026 01:02:04 GMT" + }) + ]; + + public override List HeaderRegexSanitizers => + [ + ..base.HeaderRegexSanitizers, + new HeaderRegexSanitizer(new HeaderRegexSanitizerBody("Operation-Id") + { + Value = EmptyGuid + }) + ]; + [Fact] public async Task Should_SendSms_WithValidParameters() { - // Get configuration from DeploymentOutputs in Settings - Settings.DeploymentOutputs.TryGetValue("COMMUNICATION_SERVICES_ENDPOINT", out var endpoint); - Settings.DeploymentOutputs.TryGetValue("COMMUNICATION_SERVICES_FROM_PHONE", out var fromPhone); - Settings.DeploymentOutputs.TryGetValue("COMMUNICATION_SERVICES_TO_PHONE", out var toPhone); - Assert.SkipWhen(string.IsNullOrEmpty(endpoint), "Communication Services endpoint not configured for live testing"); - Assert.SkipWhen(string.IsNullOrEmpty(fromPhone), "From phone number not configured for live testing"); - Assert.SkipWhen(string.IsNullOrEmpty(toPhone), "To phone number not configured for live testing"); + + if (TestMode != Tests.Helpers.TestMode.Playback) + { + Assert.SkipWhen(string.IsNullOrEmpty(endpointRecorded), "Communication Services endpoint not configured for live testing"); + Assert.SkipWhen(string.IsNullOrEmpty(fromSms), "From phone number not configured for live testing"); + Assert.SkipWhen(string.IsNullOrEmpty(toSms), "To phone number not configured for live testing"); + } + var result = await CallToolAsync( "communication_sms_send", new() { - { "endpoint", endpoint }, - { "from", fromPhone }, - { "to", new[] { toPhone } }, + { "endpoint", endpointRecorded }, + { "from", fromSms }, + { "to", new[] { toSms } }, { "message", "Test SMS from Azure MCP Live Test" }, { "enable-delivery-report", true }, { "tag", "live-test" } @@ -57,7 +133,7 @@ public async Task Should_SendSms_WithValidParameters() // Verify the result values Assert.NotNull(messageId); - Assert.StartsWith("+", to); + Assert.Equal(toSms, to); Assert.True(successful, "SMS was not sent successfully"); Assert.True(Guid.TryParse(messageId, out _), "MessageId should be a valid GUID"); diff --git a/tools/Azure.Mcp.Tools.Communication/tests/Azure.Mcp.Tools.Communication.LiveTests/Email/EmailSendCommandLiveTests.cs b/tools/Azure.Mcp.Tools.Communication/tests/Azure.Mcp.Tools.Communication.LiveTests/Email/EmailSendCommandLiveTests.cs index b9cfcba9bf..cffe17b772 100644 --- a/tools/Azure.Mcp.Tools.Communication/tests/Azure.Mcp.Tools.Communication.LiveTests/Email/EmailSendCommandLiveTests.cs +++ b/tools/Azure.Mcp.Tools.Communication/tests/Azure.Mcp.Tools.Communication.LiveTests/Email/EmailSendCommandLiveTests.cs @@ -5,41 +5,100 @@ using System.Text.Json; using Azure.Mcp.Tests; using Azure.Mcp.Tests.Client; +using Azure.Mcp.Tests.Client.Helpers; +using Azure.Mcp.Tests.Generated.Models; using Xunit; namespace Azure.Mcp.Tools.Communication.LiveTests.Email; [Trait("Command", "EmailSendCommand")] -public class EmailSendCommandLiveTests : CommandTestsBase +public class EmailSendCommandLiveTests(ITestOutputHelper output, TestProxyFixture fixture) : RecordedCommandTestsBase(output, fixture) { - public EmailSendCommandLiveTests(ITestOutputHelper output) : base(output) + private const string EmptyGuid = "00000000-0000-0000-0000-000000000000"; + private string? endpointRecorded; + private string? fromEmail; + private string? toEmail; + public override bool EnableDefaultSanitizerAdditions => false; + + public override async ValueTask InitializeAsync() { + await LoadSettingsAsync(); + if (TestMode == Tests.Helpers.TestMode.Playback) + { + endpointRecorded = "https://sanitized.communication.azure.com"; + fromEmail = "DoNotReply@domain.com"; + toEmail = "placeholder@microsoft.com"; + } + else + { + Settings.DeploymentOutputs.TryGetValue("COMMUNICATION_SERVICES_ENDPOINT", out endpointRecorded); + Settings.DeploymentOutputs.TryGetValue("COMMUNICATION_SERVICES_SENDER_EMAIL", out fromEmail); + Settings.DeploymentOutputs.TryGetValue("COMMUNICATION_SERVICES_TEST_EMAIL", out toEmail); + } + + await base.InitializeAsync(); } + public override List GeneralRegexSanitizers => + [ + ..base.GeneralRegexSanitizers, + new GeneralRegexSanitizer(new GeneralRegexSanitizerBody() + { + Regex = Settings.ResourceBaseName, + Value = "Sanitized", + }), + new GeneralRegexSanitizer(new GeneralRegexSanitizerBody() + { + Regex = Settings.SubscriptionId, + Value = EmptyGuid, + }), + new GeneralRegexSanitizer(new GeneralRegexSanitizerBody() + { + Regex = endpointRecorded, + Value = "https://sanitized.communication.azure.com", + }), + new GeneralRegexSanitizer(new GeneralRegexSanitizerBody() + { + Regex = fromEmail, + Value = "DoNotReply@domain.com", + }), + new GeneralRegexSanitizer(new GeneralRegexSanitizerBody() + { + Regex = toEmail, + Value = "placeholder@microsoft.com", + }), + ]; + + public override List HeaderRegexSanitizers => + [ + ..base.HeaderRegexSanitizers, + new HeaderRegexSanitizer(new HeaderRegexSanitizerBody("Operation-Id") + { + Value = EmptyGuid + }) + ]; + [Fact] public async Task Should_SendEmail_WithValidParameters() { - // Get configuration from DeploymentOutputs in Settings - Settings.DeploymentOutputs.TryGetValue("COMMUNICATION_SERVICES_ENDPOINT", out var endpoint); - Settings.DeploymentOutputs.TryGetValue("COMMUNICATION_SERVICES_SENDER_EMAIL", out var senderEmail); - Settings.DeploymentOutputs.TryGetValue("COMMUNICATION_SERVICES_TEST_EMAIL", out var testEmail); - // Output the values for debugging - Output.WriteLine($"Endpoint: {endpoint ?? "null"}"); - Output.WriteLine($"Sender Email: {senderEmail ?? "null"}"); - Output.WriteLine($"Test Email: {testEmail ?? "null"}"); - - Assert.SkipWhen(string.IsNullOrEmpty(endpoint), "Communication Services endpoint not configured for live testing"); - Assert.SkipWhen(string.IsNullOrEmpty(senderEmail), "Sender email not configured for live testing"); - Assert.SkipWhen(string.IsNullOrEmpty(testEmail), "Test recipient email not configured for live testing"); + Output.WriteLine($"Endpoint: {endpointRecorded ?? "null"}"); + Output.WriteLine($"Sender Email: {fromEmail ?? "null"}"); + Output.WriteLine($"Test Email: {toEmail ?? "null"}"); + if (TestMode != Tests.Helpers.TestMode.Playback) + { + Assert.SkipWhen(string.IsNullOrEmpty(endpointRecorded), "Communication Services endpoint not configured for live testing"); + Assert.SkipWhen(string.IsNullOrEmpty(fromEmail), "Sender email not configured for live testing"); + Assert.SkipWhen(string.IsNullOrEmpty(toEmail), "Test recipient email not configured for live testing"); + } var result = await CallToolAsync( "communication_email_send", new() { - { "endpoint", endpoint }, - { "from", senderEmail }, - { "to", new[] { testEmail } }, + { "endpoint", endpointRecorded }, + { "from", fromEmail }, + { "to", new[] { toEmail } }, { "subject", "Test Email from Azure MCP Live Test" }, { "message", "This is a test email sent from Azure MCP Live Test." }, { "is-html", false } diff --git a/tools/Azure.Mcp.Tools.Communication/tests/Azure.Mcp.Tools.Communication.LiveTests/assets.json b/tools/Azure.Mcp.Tools.Communication/tests/Azure.Mcp.Tools.Communication.LiveTests/assets.json new file mode 100644 index 0000000000..81253a11d8 --- /dev/null +++ b/tools/Azure.Mcp.Tools.Communication/tests/Azure.Mcp.Tools.Communication.LiveTests/assets.json @@ -0,0 +1,6 @@ +{ + "AssetsRepo": "Azure/azure-sdk-assets", + "AssetsRepoPrefixPath": "", + "TagPrefix": "Azure.Mcp.Tools.Communication.LiveTests", + "Tag": "Azure.Mcp.Tools.Communication.LiveTests_416767ef58" +} \ No newline at end of file