diff --git a/sdk/communication/Azure.Communication.Sms/src/OptOutsClient.cs b/sdk/communication/Azure.Communication.Sms/src/OptOutsClient.cs index fefadc9600e0a..c62bb86500229 100644 --- a/sdk/communication/Azure.Communication.Sms/src/OptOutsClient.cs +++ b/sdk/communication/Azure.Communication.Sms/src/OptOutsClient.cs @@ -47,9 +47,9 @@ protected OptOutsClient() /// The server returned an error. See for details returned from the server. /// is null. /// is null. - public virtual async Task> CheckAsync(string from, IEnumerable to, CancellationToken cancellationToken = default) + public virtual async Task>> CheckAsync(string from, IEnumerable to, CancellationToken cancellationToken = default) { - using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(SmsClient)}.{nameof(OptOutsClient)}.{nameof(CheckAsync)}"); + using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(OptOutsClient)}.{nameof(Check)}"); scope.Start(); try { @@ -58,7 +58,7 @@ public virtual async Task> CheckAsync(string from, IEnu IEnumerable recipients = to.Select(x => new OptOutRecipient(Argument.CheckNotNullOrEmpty(x, nameof(to)))); Response response = await OptOutsRestClient.CheckAsync(from, recipients, cancellationToken).ConfigureAwait(false); - return Response.FromValue(response.Value, response.GetRawResponse()); + return Response.FromValue(response.Value.Value, response.GetRawResponse()); } catch (Exception ex) { @@ -76,9 +76,9 @@ public virtual async Task> CheckAsync(string from, IEnu /// The server returned an error. See for details returned from the server. /// is null. /// is null. - public virtual Response Check(string from, IEnumerable to, CancellationToken cancellationToken = default) + public virtual Response> Check(string from, IEnumerable to, CancellationToken cancellationToken = default) { - using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(SmsClient)}.{nameof(OptOutsClient)}.{nameof(Check)}"); + using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(OptOutsClient)}.{nameof(Check)}"); scope.Start(); try { @@ -88,7 +88,7 @@ public virtual Response Check(string from, IEnumerable t IEnumerable recipients = to.Select(x => new OptOutRecipient(Argument.CheckNotNullOrEmpty(x, nameof(to)))); Response response = OptOutsRestClient.Check(from, recipients, cancellationToken); - return Response.FromValue(response.Value, response.GetRawResponse()); + return Response.FromValue(response.Value.Value, response.GetRawResponse()); } catch (Exception ex) { @@ -106,9 +106,9 @@ public virtual Response Check(string from, IEnumerable t /// The server returned an error. See for details returned from the server. /// is null. /// is null. - public virtual async Task> AddAsync(string from, IEnumerable to, CancellationToken cancellationToken = default) + public virtual async Task>> AddAsync(string from, IEnumerable to, CancellationToken cancellationToken = default) { - using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(SmsClient)}.{nameof(OptOutsClient)}.{nameof(AddAsync)}"); + using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(OptOutsClient)}.{nameof(Add)}"); scope.Start(); try { @@ -119,7 +119,7 @@ public virtual async Task> AddAsync(string from, Response response = await OptOutsRestClient.AddAsync(from, recipients, cancellationToken).ConfigureAwait(false); OptOutChangeResponse result = new OptOutChangeResponse(response.Value.Value.Select(r => new OptOutChangeResponseItem(r.To, r.HttpStatusCode, r.ErrorMessage))); - return Response.FromValue(result, response.GetRawResponse()); + return Response.FromValue(result.Value, response.GetRawResponse()); } catch (Exception ex) { @@ -137,9 +137,9 @@ public virtual async Task> AddAsync(string from, /// The server returned an error. See for details returned from the server. /// is null. /// is null. - public virtual Response Add(string from, IEnumerable to, CancellationToken cancellationToken = default) + public virtual Response> Add(string from, IEnumerable to, CancellationToken cancellationToken = default) { - using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(SmsClient)}.{nameof(OptOutsClient)}.{nameof(Add)}"); + using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(OptOutsClient)}.{nameof(Add)}"); scope.Start(); try { @@ -150,7 +150,7 @@ public virtual Response Add(string from, IEnumerable response = OptOutsRestClient.Add(from, recipients, cancellationToken); OptOutChangeResponse result = new OptOutChangeResponse(response.Value.Value.Select(r => new OptOutChangeResponseItem(r.To, r.HttpStatusCode, r.ErrorMessage))); - return Response.FromValue(result, response.GetRawResponse()); + return Response.FromValue(result.Value, response.GetRawResponse()); } catch (Exception ex) { @@ -168,9 +168,9 @@ public virtual Response Add(string from, IEnumerableThe server returned an error. See for details returned from the server. /// is null. /// is null. - public virtual async Task> RemoveAsync(string from, IEnumerable to, CancellationToken cancellationToken = default) + public virtual async Task>> RemoveAsync(string from, IEnumerable to, CancellationToken cancellationToken = default) { - using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(SmsClient)}.{nameof(OptOutsClient)}.{nameof(RemoveAsync)}"); + using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(OptOutsClient)}.{nameof(Remove)}"); scope.Start(); try { @@ -181,7 +181,7 @@ public virtual async Task> RemoveAsync(string fro Response response = await OptOutsRestClient.RemoveAsync(from, recipients, cancellationToken).ConfigureAwait(false); OptOutChangeResponse result = new OptOutChangeResponse(response.Value.Value.Select(r => new OptOutChangeResponseItem(r.To, r.HttpStatusCode, r.ErrorMessage))); - return Response.FromValue(result, response.GetRawResponse()); + return Response.FromValue(result.Value, response.GetRawResponse()); } catch (Exception ex) { @@ -199,9 +199,9 @@ public virtual async Task> RemoveAsync(string fro /// The server returned an error. See for details returned from the server. /// is null. /// is null. - public virtual Response Remove(string from, IEnumerable to, CancellationToken cancellationToken = default) + public virtual Response> Remove(string from, IEnumerable to, CancellationToken cancellationToken = default) { - using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(SmsClient)}.{nameof(OptOutsClient)}.{nameof(Remove)}"); + using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(OptOutsClient)}.{nameof(Remove)}"); scope.Start(); try { @@ -212,7 +212,7 @@ public virtual Response Remove(string from, IEnumerable response = OptOutsRestClient.Remove(from, recipients, cancellationToken); OptOutChangeResponse result = new OptOutChangeResponse(response.Value.Value.Select(r => new OptOutChangeResponseItem(r.To, r.HttpStatusCode, r.ErrorMessage))); - return Response.FromValue(result, response.GetRawResponse()); + return Response.FromValue(result.Value, response.GetRawResponse()); } catch (Exception ex) { diff --git a/sdk/communication/Azure.Communication.Sms/tests/OptOutsClientTest.cs b/sdk/communication/Azure.Communication.Sms/tests/OptOutsClientTest.cs index a3fdf06404ac1..509d00d6250d0 100644 --- a/sdk/communication/Azure.Communication.Sms/tests/OptOutsClientTest.cs +++ b/sdk/communication/Azure.Communication.Sms/tests/OptOutsClientTest.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; @@ -47,7 +46,7 @@ public void OptOutsClient_ThrowsWithNullEndpoint() public async Task CheckAsyncOverload_PassesToGeneratedOne(string expectedFrom, IEnumerable expectedTo) { Mock mockClient = new Mock() { CallBase = true }; - Response? expectedResponse = default; + Response>? expectedResponse = default; CancellationToken cancellationToken = new CancellationTokenSource().Token; var callExpression = BuildExpression(x => x.CheckAsync(It.IsAny(), It.IsAny>(), It.IsAny())); @@ -58,10 +57,10 @@ public async Task CheckAsyncOverload_PassesToGeneratedOne(string expectedFrom, I Assert.AreEqual(expectedFrom, from); Assert.AreEqual(expectedTo, to); Assert.AreEqual(cancellationToken, token); - return expectedResponse = new Mock>().Object; + return expectedResponse = new Mock>>().Object; }); - Response actualResponse = await mockClient.Object.CheckAsync(expectedFrom, expectedTo, cancellationToken); + Response> actualResponse = await mockClient.Object.CheckAsync(expectedFrom, expectedTo, cancellationToken); mockClient.Verify(callExpression, Times.Once()); Assert.AreEqual(expectedResponse, actualResponse); @@ -71,7 +70,7 @@ public async Task CheckAsyncOverload_PassesToGeneratedOne(string expectedFrom, I public void CheckOverload_PassesToGeneratedOne(string expectedFrom, IEnumerable expectedTo) { Mock mockClient = new Mock() { CallBase = true }; - Response? expectedResponse = default; + Response>? expectedResponse = default; CancellationToken cancellationToken = new CancellationTokenSource().Token; var callExpression = BuildExpression(x => x.Check(It.IsAny(), It.IsAny>(), It.IsAny())); @@ -82,10 +81,10 @@ public void CheckOverload_PassesToGeneratedOne(string expectedFrom, IEnumerable< Assert.AreEqual(expectedFrom, from); Assert.AreEqual(expectedTo, to); Assert.AreEqual(cancellationToken, token); - return expectedResponse = new Mock>().Object; + return expectedResponse = new Mock>>().Object; }); - Response actualResponse = mockClient.Object.Check(expectedFrom, expectedTo, cancellationToken); + Response> actualResponse = mockClient.Object.Check(expectedFrom, expectedTo, cancellationToken); mockClient.Verify(callExpression, Times.Once()); Assert.AreEqual(expectedResponse, actualResponse); @@ -95,7 +94,7 @@ public void CheckOverload_PassesToGeneratedOne(string expectedFrom, IEnumerable< public async Task AddAsyncOverload_PassesToGeneratedOne(string expectedFrom, IEnumerable expectedTo) { Mock mockClient = new Mock() { CallBase = true }; - Response? expectedResponse = default; + Response>? expectedResponse = default; CancellationToken cancellationToken = new CancellationTokenSource().Token; var callExpression = BuildExpression(x => x.AddAsync(It.IsAny(), It.IsAny>(), It.IsAny())); @@ -106,10 +105,10 @@ public async Task AddAsyncOverload_PassesToGeneratedOne(string expectedFrom, IEn Assert.AreEqual(expectedFrom, from); Assert.AreEqual(expectedTo, to); Assert.AreEqual(cancellationToken, token); - return expectedResponse = new Mock>().Object; + return expectedResponse = new Mock>>().Object; }); - Response actualResponse = await mockClient.Object.AddAsync(expectedFrom, expectedTo, cancellationToken); + Response> actualResponse = await mockClient.Object.AddAsync(expectedFrom, expectedTo, cancellationToken); mockClient.Verify(callExpression, Times.Once()); Assert.AreEqual(expectedResponse, actualResponse); @@ -119,7 +118,7 @@ public async Task AddAsyncOverload_PassesToGeneratedOne(string expectedFrom, IEn public void AddOverload_PassesToGeneratedOne(string expectedFrom, IEnumerable expectedTo) { Mock mockClient = new Mock() { CallBase = true }; - Response? expectedResponse = default; + Response>? expectedResponse = default; CancellationToken cancellationToken = new CancellationTokenSource().Token; var callExpression = BuildExpression(x => x.Add(It.IsAny(), It.IsAny>(), It.IsAny())); @@ -130,10 +129,10 @@ public void AddOverload_PassesToGeneratedOne(string expectedFrom, IEnumerable>().Object; + return expectedResponse = new Mock>>().Object; }); - Response actualResponse = mockClient.Object.Add(expectedFrom, expectedTo, cancellationToken); + Response> actualResponse = mockClient.Object.Add(expectedFrom, expectedTo, cancellationToken); mockClient.Verify(callExpression, Times.Once()); Assert.AreEqual(expectedResponse, actualResponse); @@ -143,7 +142,7 @@ public void AddOverload_PassesToGeneratedOne(string expectedFrom, IEnumerable expectedTo) { Mock mockClient = new Mock() { CallBase = true }; - Response? expectedResponse = default; + Response>? expectedResponse = default; CancellationToken cancellationToken = new CancellationTokenSource().Token; var callExpression = BuildExpression(x => x.RemoveAsync(It.IsAny(), It.IsAny>(), It.IsAny())); @@ -154,10 +153,10 @@ public async Task RemoveAsyncOverload_PassesToGeneratedOne(string expectedFrom, Assert.AreEqual(expectedFrom, from); Assert.AreEqual(expectedTo, to); Assert.AreEqual(cancellationToken, token); - return expectedResponse = new Mock>().Object; + return expectedResponse = new Mock>>().Object; }); - Response actualResponse = await mockClient.Object.RemoveAsync(expectedFrom, expectedTo, cancellationToken); + Response> actualResponse = await mockClient.Object.RemoveAsync(expectedFrom, expectedTo, cancellationToken); mockClient.Verify(callExpression, Times.Once()); Assert.AreEqual(expectedResponse, actualResponse); @@ -167,7 +166,7 @@ public async Task RemoveAsyncOverload_PassesToGeneratedOne(string expectedFrom, public void RemoveOverload_PassesToGeneratedOne(string expectedFrom, IEnumerable expectedTo) { Mock mockClient = new Mock() { CallBase = true }; - Response? expectedResponse = default; + Response>? expectedResponse = default; CancellationToken cancellationToken = new CancellationTokenSource().Token; var callExpression = BuildExpression(x => x.Remove(It.IsAny(), It.IsAny>(), It.IsAny())); @@ -178,10 +177,10 @@ public void RemoveOverload_PassesToGeneratedOne(string expectedFrom, IEnumerable Assert.AreEqual(expectedFrom, from); Assert.AreEqual(expectedTo, to); Assert.AreEqual(cancellationToken, token); - return expectedResponse = new Mock>().Object; + return expectedResponse = new Mock>>().Object; }); - Response actualResponse = mockClient.Object.Remove(expectedFrom, expectedTo, cancellationToken); + Response> actualResponse = mockClient.Object.Remove(expectedFrom, expectedTo, cancellationToken); mockClient.Verify(callExpression, Times.Once()); Assert.AreEqual(expectedResponse, actualResponse); diff --git a/sdk/communication/Azure.Communication.Sms/tests/SmsClientLiveTests.cs b/sdk/communication/Azure.Communication.Sms/tests/SmsClientLiveTests.cs index 6ca4be841d952..77b83a6296ec2 100644 --- a/sdk/communication/Azure.Communication.Sms/tests/SmsClientLiveTests.cs +++ b/sdk/communication/Azure.Communication.Sms/tests/SmsClientLiveTests.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using NUnit.Framework; +using Azure.Communication.Sms.Models; namespace Azure.Communication.Sms.Tests { @@ -31,8 +32,8 @@ public async Task SendingSmsMessage() message: "Hi"); SmsSendResult result = response.Value; Console.WriteLine($"Sms id: {result.MessageId}"); - AssertHappyPath(result); - AssertRawResponseHappyPath(response.GetRawResponse().ContentStream ?? new MemoryStream()); + AssertSmsSendingHappyPath(result); + AssertSmsSendingRawResponseHappyPath(response.GetRawResponse().ContentStream ?? new MemoryStream()); } catch (RequestFailedException ex) { @@ -57,8 +58,8 @@ public async Task SendingSmsMessageUsingTokenCredential() message: "Hi"); SmsSendResult result = response.Value; Console.WriteLine($"Sms id: {result.MessageId}"); - AssertHappyPath(result); - AssertRawResponseHappyPath(response.GetRawResponse().ContentStream ?? new MemoryStream()); + AssertSmsSendingHappyPath(result); + AssertSmsSendingRawResponseHappyPath(response.GetRawResponse().ContentStream ?? new MemoryStream()); } catch (RequestFailedException ex) { @@ -110,11 +111,11 @@ public async Task SendingSmsMessageToGroupWithOptions() Tag = "marketing", // custom tags DeliveryReportTimeoutInSeconds = 90 // OPTIONAL }); - AssertRawResponseHappyPath(response.GetRawResponse().ContentStream ?? new MemoryStream()); + AssertSmsSendingRawResponseHappyPath(response.GetRawResponse().ContentStream ?? new MemoryStream()); foreach (SmsSendResult result in response.Value) { Console.WriteLine($"Sms id: {result.MessageId}"); - AssertHappyPath(result); + AssertSmsSendingHappyPath(result); } } catch (RequestFailedException ex) @@ -143,15 +144,15 @@ public async Task SendingTwoSmsMessages() to: TestEnvironment.ToPhoneNumber, message: "Hi"); - AssertRawResponseHappyPath(firstMessageResponse.GetRawResponse().ContentStream ?? new MemoryStream()); - AssertRawResponseHappyPath(secondMessageResponse.GetRawResponse().ContentStream ?? new MemoryStream()); + AssertSmsSendingRawResponseHappyPath(firstMessageResponse.GetRawResponse().ContentStream ?? new MemoryStream()); + AssertSmsSendingRawResponseHappyPath(secondMessageResponse.GetRawResponse().ContentStream ?? new MemoryStream()); SmsSendResult firstMessageResult = firstMessageResponse.Value; SmsSendResult secondMessageResult = secondMessageResponse.Value; Assert.AreNotEqual(firstMessageResult.MessageId, secondMessageResult.MessageId); - AssertHappyPath(firstMessageResult); - AssertHappyPath(secondMessageResult); + AssertSmsSendingHappyPath(firstMessageResult); + AssertSmsSendingHappyPath(secondMessageResult); } catch (RequestFailedException ex) { @@ -203,14 +204,134 @@ public async Task SendingSmsToNullNumberShouldThrow() Assert.Fail("SendAsync should have thrown an exception."); } - private void AssertHappyPath(SmsSendResult sendResult) + [Test] + public async Task CheckOptOutFromNullNumberShouldThrow() + { + SmsClient client = CreateSmsClient(); + try + { + IEnumerable? to = new string[] { TestEnvironment.ToPhoneNumber }; + Response> result = await client.OptOuts.CheckAsync( + from: null, + to: to); + } + catch (ArgumentNullException ex) + { + Assert.AreEqual("from", ex.ParamName); + return; + } + Assert.Fail("CheckAsync should have thrown an exception."); + } + + [Test] + public async Task CheckOptOutToNullNumberShouldThrow() + { + SmsClient client = CreateSmsClient(); + try + { + IEnumerable? to = null; + Response> result = await client.OptOuts.CheckAsync( + from: TestEnvironment.FromPhoneNumber, + to: to); + } + catch (ArgumentNullException ex) + { + Assert.AreEqual("to", ex.ParamName); + return; + } + Assert.Fail("CheckAsync should have thrown an exception."); + } + + [Test] + public async Task CheckOptOutToCollectionContainingNullShouldThrow() + { + SmsClient client = CreateSmsClient(); + try + { +#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. + IEnumerable? to = new string[] + { + TestEnvironment.ToPhoneNumber, + null + }; +#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. + + Response> result = await client.OptOuts.CheckAsync( + from: TestEnvironment.FromPhoneNumber, + to: to); + } + catch (ArgumentNullException ex) + { + Assert.AreEqual("to", ex.ParamName); + return; + } + Assert.Fail("CheckAsync should have thrown an exception."); + } + + [Test] + public async Task AddOptOutEndpointShouldMarkRecipientAsOptedOut() + { + SmsClient client = CreateSmsClient(); + try + { + IEnumerable? to = new[] { TestEnvironment.ToPhoneNumber }; + + Response> addResult = await client.OptOuts.AddAsync( + from: TestEnvironment.FromPhoneNumber, + to: to); + + Response> checkResult = await client.OptOuts.CheckAsync( + from: TestEnvironment.FromPhoneNumber, + to: to); + + Assert.IsTrue(checkResult.Value[0].IsOptedOut); + } + catch (Exception ex) + { + Assert.Fail("Exception should not have been thrown."); + Console.WriteLine(ex); + return; + } + } + + [Test] + public async Task RemoveOptOutEndpointShouldMarkRecipientAsOptedIn() + { + SmsClient client = CreateSmsClient(); + try + { + IEnumerable? to = new[] { TestEnvironment.ToPhoneNumber }; + + Response> addResult = await client.OptOuts.AddAsync( + from: TestEnvironment.FromPhoneNumber, + to: to); + + Response> removeResult = await client.OptOuts.RemoveAsync( + from: TestEnvironment.FromPhoneNumber, + to: to); + + Response> checkResult = await client.OptOuts.CheckAsync( + from: TestEnvironment.FromPhoneNumber, + to: to); + + Assert.IsFalse(checkResult.Value[0].IsOptedOut); + } + catch (Exception ex) + { + Assert.Fail("Exception should not have been thrown."); + Console.WriteLine(ex); + return; + } + } + + private void AssertSmsSendingHappyPath(SmsSendResult sendResult) { Assert.True(sendResult.Successful); Assert.AreEqual(202, sendResult.HttpStatusCode); Assert.IsFalse(string.IsNullOrWhiteSpace(sendResult.MessageId)); } - private void AssertRawResponseHappyPath(Stream contentStream) + private void AssertSmsSendingRawResponseHappyPath(Stream contentStream) { if (contentStream.Length > 0) {