diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index 2060bca94..2cdc6c2c7 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -4,7 +4,7 @@ on: push: branches: [ main ] pull_request: - branches: [ main ] + branches: [ main, dev ] jobs: build: diff --git a/Nexmo.Api.Test.Unit/Nexmo.Api.Test.Unit.csproj b/Nexmo.Api.Test.Unit/Nexmo.Api.Test.Unit.csproj index d940a9954..4ccd6d63f 100644 --- a/Nexmo.Api.Test.Unit/Nexmo.Api.Test.Unit.csproj +++ b/Nexmo.Api.Test.Unit/Nexmo.Api.Test.Unit.csproj @@ -1,7 +1,7 @@ - net452;net46;net461;net462;netcoreapp2.0;netcoreapp2.1;netcoreapp2.2;net47;net471;net472;net48;netcoreapp3.0;netcoreapp3.1 + net452;net46;net462;netcoreapp2.0;netcoreapp2.1;netcoreapp2.2;net47;net471;net472;net48;netcoreapp3.0;netcoreapp3.1 1701;1702;0618 false Debug;Release;ReleaseSigned diff --git a/Vonage.Test.Unit/Data/MessagingTests/SendSmsAsyncBadResponse.json b/Vonage.Test.Unit/Data/MessagingTests/SendSmsAsyncBadResponse.json new file mode 100644 index 000000000..fd5bf1717 --- /dev/null +++ b/Vonage.Test.Unit/Data/MessagingTests/SendSmsAsyncBadResponse.json @@ -0,0 +1,9 @@ +{ + "message-count": "1", + "messages": [ + { + "status": "4", + "error-text": "invalid credentials" + } + ] +} \ No newline at end of file diff --git a/Vonage.Test.Unit/Data/MessagingTests/SendSmsAsyncWithAllPropertiesSet.json b/Vonage.Test.Unit/Data/MessagingTests/SendSmsAsyncWithAllPropertiesSet.json new file mode 100644 index 000000000..ecb69e77a --- /dev/null +++ b/Vonage.Test.Unit/Data/MessagingTests/SendSmsAsyncWithAllPropertiesSet.json @@ -0,0 +1,14 @@ +{ + "message-count": "1", + "messages": [ + { + "to": "447700900000", + "message-id": "0A0000000123ABCD1", + "status": "0", + "remaining-balance": "3.14159265", + "message-price": "0.03330000", + "network": "12345", + "account-ref": "customer1234" + } + ] +} \ No newline at end of file diff --git a/Vonage.Test.Unit/Data/MessagingTests/SendSmsBadResponse.json b/Vonage.Test.Unit/Data/MessagingTests/SendSmsBadResponse.json new file mode 100644 index 000000000..fd5bf1717 --- /dev/null +++ b/Vonage.Test.Unit/Data/MessagingTests/SendSmsBadResponse.json @@ -0,0 +1,9 @@ +{ + "message-count": "1", + "messages": [ + { + "status": "4", + "error-text": "invalid credentials" + } + ] +} \ No newline at end of file diff --git a/Vonage.Test.Unit/Data/MessagingTests/SendSmsTypicalUsageSimplifiedAsync.json b/Vonage.Test.Unit/Data/MessagingTests/SendSmsTypicalUsageSimplifiedAsync.json new file mode 100644 index 000000000..ecb69e77a --- /dev/null +++ b/Vonage.Test.Unit/Data/MessagingTests/SendSmsTypicalUsageSimplifiedAsync.json @@ -0,0 +1,14 @@ +{ + "message-count": "1", + "messages": [ + { + "to": "447700900000", + "message-id": "0A0000000123ABCD1", + "status": "0", + "remaining-balance": "3.14159265", + "message-price": "0.03330000", + "network": "12345", + "account-ref": "customer1234" + } + ] +} \ No newline at end of file diff --git a/Vonage.Test.Unit/Data/MessagingTests/SendSmsWithAllPropertiesSet.json b/Vonage.Test.Unit/Data/MessagingTests/SendSmsWithAllPropertiesSet.json new file mode 100644 index 000000000..ecb69e77a --- /dev/null +++ b/Vonage.Test.Unit/Data/MessagingTests/SendSmsWithAllPropertiesSet.json @@ -0,0 +1,14 @@ +{ + "message-count": "1", + "messages": [ + { + "to": "447700900000", + "message-id": "0A0000000123ABCD1", + "status": "0", + "remaining-balance": "3.14159265", + "message-price": "0.03330000", + "network": "12345", + "account-ref": "customer1234" + } + ] +} \ No newline at end of file diff --git a/Vonage.Test.Unit/Data/MessagingTests/SendSmsWrongCredentialsThrowsException.json b/Vonage.Test.Unit/Data/MessagingTests/SendSmsWrongCredentialsThrowsException.json new file mode 100644 index 000000000..fd5bf1717 --- /dev/null +++ b/Vonage.Test.Unit/Data/MessagingTests/SendSmsWrongCredentialsThrowsException.json @@ -0,0 +1,9 @@ +{ + "message-count": "1", + "messages": [ + { + "status": "4", + "error-text": "invalid credentials" + } + ] +} \ No newline at end of file diff --git a/Vonage.Test.Unit/MessagingTests.cs b/Vonage.Test.Unit/MessagingTests.cs index 3aa7c5059..849041857 100644 --- a/Vonage.Test.Unit/MessagingTests.cs +++ b/Vonage.Test.Unit/MessagingTests.cs @@ -1,9 +1,6 @@ using Newtonsoft.Json; using Vonage.Messaging; -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; using System.Web; using Xunit; @@ -16,28 +13,16 @@ public class MessagingTests : TestBase [Theory] [InlineData(false)] [InlineData(true)] - public void KitcenSinkSendSms(bool passCreds) + public void SendSmsWithAllPropertiesSet(bool passCreds) { - var expectedResponse = @"{ - ""message-count"": ""1"", - ""messages"": [ - { - ""to"": ""447700900000"", - ""message-id"": ""0A0000000123ABCD1"", - ""status"": ""0"", - ""remaining-balance"": ""3.14159265"", - ""message-price"": ""0.03330000"", - ""network"": ""12345"", - ""account-ref"": ""customer1234"" - } - ] - }"; + var expectedResponse = GetExpectedJson(); var expectedUri = $"{RestUrl}/sms/json?"; var expectedRequestContent = $"from=AcmeInc&to=447700900000&text={HttpUtility.UrlEncode("Hello World!")}" + $"&ttl=900000&status-report-req=true&callback={HttpUtility.UrlEncode("https://example.com/sms-dlr")}&message-class=0" + $"&type=text&vcard=none&vcal=none&body=638265253311&udh=06050415811581&protocol-id=127&title=welcome&url={HttpUtility.UrlEncode("https://example.com")}" + $"&validity=300000&client-ref=my-personal-reference&account-ref=customer1234&entity-id=testEntity&content-id=testcontent&api_key={ApiKey}&api_secret={ApiSecret}&"; - var request = new Messaging.SendSmsRequest + + var request = new SendSmsRequest { AccountRef = "customer1234", Body = "638265253311", @@ -59,21 +44,68 @@ public void KitcenSinkSendSms(bool passCreds) Url = "https://example.com", ContentId = "testcontent", EntityId = "testEntity" + }; + + var creds = Request.Credentials.FromApiKeyAndSecret(ApiKey, ApiSecret); + Setup(expectedUri, expectedResponse, expectedRequestContent); + var client = new VonageClient(creds); + + var response = passCreds + ? client.SmsClient.SendAnSms(request, creds) + : client.SmsClient.SendAnSms(request); + + Assert.Equal("1", response.MessageCount); + Assert.Equal("447700900000", response.Messages[0].To); + Assert.Equal("0A0000000123ABCD1", response.Messages[0].MessageId); + Assert.Equal("0", response.Messages[0].Status); + Assert.Equal("3.14159265", response.Messages[0].RemainingBalance); + Assert.Equal("12345", response.Messages[0].Network); + Assert.Equal("customer1234", response.Messages[0].AccountRef); + } + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task SendSmsAsyncWithAllPropertiesSet(bool passCreds) + { + var expectedResponse = GetExpectedJson(); + var expectedUri = $"{RestUrl}/sms/json?"; + var expectedRequestContent = $"from=AcmeInc&to=447700900000&text={HttpUtility.UrlEncode("Hello World!")}" + + $"&ttl=900000&status-report-req=true&callback={HttpUtility.UrlEncode("https://example.com/sms-dlr")}&message-class=0" + + $"&type=text&vcard=none&vcal=none&body=638265253311&udh=06050415811581&protocol-id=127&title=welcome&url={HttpUtility.UrlEncode("https://example.com")}" + + $"&validity=300000&client-ref=my-personal-reference&account-ref=customer1234&entity-id=testEntity&content-id=testcontent&api_key={ApiKey}&api_secret={ApiSecret}&"; + var request = new SendSmsRequest + { + AccountRef = "customer1234", + Body = "638265253311", + Callback = "https://example.com/sms-dlr", + ClientRef = "my-personal-reference", + From = "AcmeInc", + To = "447700900000", + MessageClass = 0, + ProtocolId = 127, + StatusReportReq = true, + Text = "Hello World!", + Title = "welcome", + Ttl = 900000, + Type = SmsType.text, + Udh = "06050415811581", + Validity = "300000", + Vcal = "none", + Vcard = "none", + Url = "https://example.com", + ContentId = "testcontent", + EntityId = "testEntity" }; + var creds = Request.Credentials.FromApiKeyAndSecret(ApiKey, ApiSecret); Setup(expectedUri, expectedResponse, expectedRequestContent); var client = new VonageClient(creds); - Messaging.SendSmsResponse response; - if (passCreds) - { - response = client.SmsClient.SendAnSms(request, creds); - } - else - { - response = client.SmsClient.SendAnSms(request); - } + + var response = passCreds + ? await client.SmsClient.SendAnSmsAsync(request, creds) + : await client.SmsClient.SendAnSmsAsync(request); Assert.Equal("1", response.MessageCount); Assert.Equal("447700900000", response.Messages[0].To); @@ -149,22 +181,9 @@ public void SendSmsTypicalUsageSimplified() } [Fact] - public async void SendSmsTypicalUsageSimplifiedAsync() + public async Task SendSmsTypicalUsageSimplifiedAsync() { - var expectedResponse = @"{ - ""message-count"": ""1"", - ""messages"": [ - { - ""to"": ""447700900000"", - ""message-id"": ""0A0000000123ABCD1"", - ""status"": ""0"", - ""remaining-balance"": ""3.14159265"", - ""message-price"": ""0.03330000"", - ""network"": ""12345"", - ""account-ref"": ""customer1234"" - } - ] - }"; + var expectedResponse = GetExpectedJson(); var expectedUri = $"{RestUrl}/sms/json?"; var expectedRequestContent = $"from=AcmeInc&to=447700900000&text={HttpUtility.UrlEncode("Hello World!")}&type=text&api_key={ApiKey}&api_secret={ApiSecret}&"; Setup(expectedUri, expectedResponse, expectedRequestContent); @@ -215,29 +234,35 @@ public void SendSmsUnicode() [Fact] public void SendSmsBadResponse() { - var expectedResponse = @"{ - ""message-count"": ""1"", - ""messages"": [ - { - ""status"": ""4"", - ""error-text"":""invalid credentials"" - } - ] - }"; + var expectedResponse = GetExpectedJson(); var expectedUri = $"{RestUrl}/sms/json?"; var expectedRequestContent = $"from=AcmeInc&to=447700900000&text={HttpUtility.UrlEncode("Hello World!")}&api_key={ApiKey}&api_secret={ApiSecret}&"; Setup(expectedUri, expectedResponse, expectedRequestContent); var client = new VonageClient(Request.Credentials.FromApiKeyAndSecret(ApiKey, ApiSecret)); - try - { - var response = client.SmsClient.SendAnSms(new Messaging.SendSmsRequest { From = "AcmeInc", To = "447700900000", Text = "Hello World!" }); - Assert.True(false); - } - catch (Messaging.VonageSmsResponseException nex) - { - Assert.Equal($"SMS Request Failed with status: {nex.Response.Messages[0].Status} and error message: {nex.Response.Messages[0].ErrorText}", nex.Message); - Assert.Equal(SmsStatusCode.InvalidCredentials, nex.Response.Messages[0].StatusCode); - } + + var exception = Assert.Throws(() => + client.SmsClient.SendAnSms(new SendSmsRequest { From = "AcmeInc", To = "447700900000", Text = "Hello World!" })); + + Assert.NotNull(exception); + Assert.Equal($"SMS Request Failed with status: {exception.Response.Messages[0].Status} and error message: {exception.Response.Messages[0].ErrorText}", exception.Message); + Assert.Equal(SmsStatusCode.InvalidCredentials, exception.Response.Messages[0].StatusCode); + } + + [Fact] + public async Task SendSmsAsyncBadResponse() + { + var expectedResponse = GetExpectedJson(); + var expectedUri = $"{RestUrl}/sms/json?"; + var expectedRequestContent = $"from=AcmeInc&to=447700900000&text={HttpUtility.UrlEncode("Hello World!")}&api_key={ApiKey}&api_secret={ApiSecret}&"; + Setup(expectedUri, expectedResponse, expectedRequestContent); + var client = new VonageClient(Request.Credentials.FromApiKeyAndSecret(ApiKey, ApiSecret)); + + var exception = await Assert.ThrowsAsync(async () => + await client.SmsClient.SendAnSmsAsync(new SendSmsRequest { From = "AcmeInc", To = "447700900000", Text = "Hello World!" })); + + Assert.NotNull(exception); + Assert.Equal($"SMS Request Failed with status: {exception.Response.Messages[0].Status} and error message: {exception.Response.Messages[0].ErrorText}", exception.Message); + Assert.Equal(SmsStatusCode.InvalidCredentials, exception.Response.Messages[0].StatusCode); } [Fact] @@ -246,17 +271,15 @@ public void NullMessagesResponse() var expectedResponse = @""; var expectedUri = $"{RestUrl}/sms/json?"; var expectedRequestContent = $"from=AcmeInc&to=447700900000&text={HttpUtility.UrlEncode("Hello World!")}&api_key={ApiKey}&api_secret={ApiSecret}&"; + Setup(expectedUri, expectedResponse, expectedRequestContent); var client = new VonageClient(Request.Credentials.FromApiKeyAndSecret(ApiKey, ApiSecret)); - try - { - var response = client.SmsClient.SendAnSms(new Messaging.SendSmsRequest { From = "AcmeInc", To = "447700900000", Text = "Hello World!" }); - Assert.True(false); - } - catch (Messaging.VonageSmsResponseException nex) - { - Assert.Equal($"Encountered an Empty SMS response", nex.Message); - } + + var exception = Assert.Throws(() => + client.SmsClient.SendAnSms(new SendSmsRequest { From = "AcmeInc", To = "447700900000", Text = "Hello World!" })); + + Assert.NotNull(exception); + Assert.Equal($"Encountered an Empty SMS response", exception.Message); } [Fact] @@ -390,7 +413,9 @@ public void TestValidateSignatureMd5() var TestSigningSecret = "Y6dI3wtDP8myVH5tnDoIaTxEvAJhgDVCczBa1mHniEqsdlnnebg"; var json = JsonConvert.SerializeObject(inboundSmsShell, Formatting.None, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore }); var dict = JsonConvert.DeserializeObject>(json); - inboundSmsShell.Sig = Cryptography.SmsSignatureGenerator.GenerateSignature(InboundSms.ConstructSignatureStringFromDictionary(dict), TestSigningSecret, Cryptography.SmsSignatureGenerator.Method.md5); + + inboundSmsShell.Sig = Cryptography.SmsSignatureGenerator.GenerateSignature(Messaging.InboundSms.ConstructSignatureStringFromDictionary(dict), TestSigningSecret, Cryptography.SmsSignatureGenerator.Method.md5); + Assert.True(inboundSmsShell.ValidateSignature(TestSigningSecret, Cryptography.SmsSignatureGenerator.Method.md5)); } diff --git a/Vonage.Test.Unit/TestBase.cs b/Vonage.Test.Unit/TestBase.cs index 3a2cd4b77..a75599965 100644 --- a/Vonage.Test.Unit/TestBase.cs +++ b/Vonage.Test.Unit/TestBase.cs @@ -9,8 +9,6 @@ using System.Threading.Tasks; using Moq; using Moq.Protected; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace Vonage.Test.Unit { @@ -59,18 +57,19 @@ public static string AssemblyDirectory public void Setup(string uri, string responseContent, string requestContent = null, HttpStatusCode expectedCode = HttpStatusCode.OK) { - typeof(Configuration).GetField("_client", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(Configuration.Instance, null); + typeof(Configuration).GetField("_client", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(Configuration.Instance, null); var mockHandler = new Mock(MockBehavior.Strict); mockHandler .Protected() .Setup>(MOCKED_METHOD, - ItExpr.Is( - x => - string.Equals(x.RequestUri.AbsoluteUri, uri, StringComparison.OrdinalIgnoreCase) && - (requestContent == null) || - (string.Equals(x.Content.ReadAsStringAsync().Result, requestContent, StringComparison.OrdinalIgnoreCase))), - ItExpr.IsAny()) - .ReturnsAsync(new HttpResponseMessage() + ItExpr.Is( + x => + string.Equals(x.RequestUri.AbsoluteUri, uri, StringComparison.OrdinalIgnoreCase) && + requestContent == null || + string.Equals(x.Content.ReadAsStringAsync().Result, requestContent, StringComparison.OrdinalIgnoreCase)), + ItExpr.IsAny() + ) + .ReturnsAsync(new HttpResponseMessage { StatusCode = expectedCode, Content = new StringContent(responseContent) @@ -89,6 +88,7 @@ private void Setup(string uri, HttpContent httpContent, HttpStatusCode expectedC { typeof(Configuration).GetField("_client", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(Configuration.Instance, null); Mock mockHandler = new Mock(MockBehavior.Strict); + mockHandler .Protected() .Setup>(MOCKED_METHOD, @@ -96,8 +96,7 @@ private void Setup(string uri, HttpContent httpContent, HttpStatusCode expectedC x => string.Equals(x.RequestUri.AbsoluteUri, uri, StringComparison.OrdinalIgnoreCase) && (requestContent == null) || string.Equals(x.Content.ReadAsStringAsync().Result, requestContent, StringComparison.OrdinalIgnoreCase) ), - ItExpr.IsAny() - ) + ItExpr.IsAny()) .ReturnsAsync(new HttpResponseMessage() { StatusCode = expectedCode, diff --git a/Vonage.Test.Unit/VoiceTests.cs b/Vonage.Test.Unit/VoiceTests.cs index bb46bc72b..16db7363a 100644 --- a/Vonage.Test.Unit/VoiceTests.cs +++ b/Vonage.Test.Unit/VoiceTests.cs @@ -681,7 +681,7 @@ public void CreateCallWithEndpointAndNcco() [Theory] [InlineData(true)] [InlineData(false)] - public async void CreateCallAsync(bool passCreds) + public async Task CreateCallAsync(bool passCreds) { var expectedUri = "https://api.nexmo.com/v1/calls/"; var expectedResponse = @"{ @@ -738,7 +738,7 @@ public async void CreateCallAsync(bool passCreds) [Theory] [InlineData(true, true)] [InlineData(false, false)] - public async void TestListCallsAsync(bool passCreds, bool kitchenSink) + public async Task TestListCallsAsync(bool passCreds, bool kitchenSink) { var expectedResponse = @"{ ""count"": 100, @@ -848,7 +848,7 @@ public async void TestListCallsAsync(bool passCreds, bool kitchenSink) [Theory] [InlineData(true)] [InlineData(false)] - public async void TestGetSpecificCallAsync(bool passCreds) + public async Task TestGetSpecificCallAsync(bool passCreds) { var uuid = "63f61863-4a51-4f6b-86e1-46edebcf9356"; var expectedResponse = @"{ @@ -919,7 +919,7 @@ public async void TestGetSpecificCallAsync(bool passCreds) [InlineData(true)] [InlineData(false)] - public async void TestUpdateCallAsync(bool passCreds) + public async Task TestUpdateCallAsync(bool passCreds) { var uuid = "63f61863-4a51-4f6b-86e1-46edebcf9356"; var expectedUri = $"{ApiUrl}/v1/calls/{uuid}"; @@ -944,7 +944,7 @@ public async void TestUpdateCallAsync(bool passCreds) [Theory] [InlineData(true)] [InlineData(false)] - public async void TestStartStreamAsync(bool passCreds) + public async Task TestStartStreamAsync(bool passCreds) { var uuid = "63f61863-4a51-4f6b-86e1-46edebcf9356"; var expectedUri = $"{ApiUrl}/v1/calls/{uuid}/stream"; @@ -980,7 +980,7 @@ public async void TestStartStreamAsync(bool passCreds) [Theory] [InlineData(true)] [InlineData(false)] - public async void StopStreamAsync(bool passCreds) + public async Task StopStreamAsync(bool passCreds) { var uuid = "63f61863-4a51-4f6b-86e1-46edebcf9356"; var expectedUri = $"{ApiUrl}/v1/calls/{uuid}/stream"; @@ -1010,7 +1010,7 @@ public async void StopStreamAsync(bool passCreds) [Theory] [InlineData(true)] [InlineData(false)] - public async void TestStartTalkAsync(bool passCreds) + public async Task TestStartTalkAsync(bool passCreds) { var uuid = "63f61863-4a51-4f6b-86e1-46edebcf9356"; var expectedUri = $"{ApiUrl}/v1/calls/{uuid}/talk"; @@ -1046,7 +1046,7 @@ public async void TestStartTalkAsync(bool passCreds) [Theory] [InlineData(true)] [InlineData(false)] - public async void StopTalkAsync(bool passCreds) + public async Task StopTalkAsync(bool passCreds) { var uuid = "63f61863-4a51-4f6b-86e1-46edebcf9356"; var expectedUri = $"{ApiUrl}/v1/calls/{uuid}/talk"; @@ -1076,7 +1076,7 @@ public async void StopTalkAsync(bool passCreds) [Theory] [InlineData(true)] [InlineData(false)] - public async void TestStartDtmfAsync(bool passCreds) + public async Task TestStartDtmfAsync(bool passCreds) { var uuid = "63f61863-4a51-4f6b-86e1-46edebcf9356"; var expectedUri = $"{ApiUrl}/v1/calls/{uuid}/talk"; @@ -1107,7 +1107,7 @@ public async void TestStartDtmfAsync(bool passCreds) [Theory] [InlineData(true)] [InlineData(false)] - public async void TestGetRecordingsAsync(bool passCreds) + public async Task TestGetRecordingsAsync(bool passCreds) { var expectedUri = $"{ApiUrl}/v1/calls/63f61863-4a51-4f6b-86e1-46edebcf9356"; var creds = Request.Credentials.FromAppIdAndPrivateKey(AppId, PrivateKey); @@ -1131,7 +1131,7 @@ public async void TestGetRecordingsAsync(bool passCreds) } [Fact] - public async void CreateCallWithStringParametersAsync() + public async Task CreateCallWithStringParametersAsync() { var expectedUri = $"{ApiUrl}/v1/calls/"; var expectedResponse = @"{ @@ -1155,7 +1155,7 @@ public async void CreateCallWithStringParametersAsync() } [Fact] - public async void CreateCallWithEndpointAndNccoAsync() + public async Task CreateCallWithEndpointAndNccoAsync() { var expectedUri = $"{ApiUrl}/v1/calls/"; var expectedResponse = @"{ @@ -1168,16 +1168,64 @@ public async void CreateCallWithEndpointAndNccoAsync() Setup(expectedUri, expectedResponse, expectedRequesetContent); var creds = Request.Credentials.FromAppIdAndPrivateKey(AppId, PrivateKey); var client = new VonageClient(creds); - CallResponse response; - var toEndpoint = new PhoneEndpoint() { Number = "14155550100" }; - response = await client.VoiceClient.CreateCallAsync( + var toEndpoint = new PhoneEndpoint { Number = "14155550100" }; + var response = await client.VoiceClient.CreateCallAsync( toEndpoint, "14155550100", new Voice.Nccos.Ncco(new Voice.Nccos.TalkAction { Text = "Hello World" })); - Assert.Equal("63f61863-4a51-4f6b-86e1-46edebcf9356", response.Uuid); Assert.Equal("CON-f972836a-550f-45fa-956c-12a2ab5b7d22", response.ConversationUuid); Assert.Equal("outbound", response.Direction); Assert.Equal("started", response.Status); } + + [Fact] + public async Task CreateCallAsyncWithWrongCredsThrowsAuthException() + { + var expectedUri = $"{ApiUrl}/v1/calls/"; + var expectedResponse = @"{ + ""uuid"": ""63f61863-4a51-4f6b-86e1-46edebcf9356"", + ""status"": ""started"", + ""direction"": ""outbound"", + ""conversation_uuid"": ""CON-f972836a-550f-45fa-956c-12a2ab5b7d22"" + }"; + var expectedRequesetContent = @"{""to"":[{""number"":""14155550100"",""type"":""phone""}],""from"":{""number"":""14155550100"",""type"":""phone""},""ncco"":[{""text"":""Hello World"",""action"":""talk""}]}"; + + Setup(expectedUri, expectedResponse, expectedRequesetContent); + + var creds = Request.Credentials.FromApiKeyAndSecret(ApiKey, ApiSecret); + var client = new VonageClient(creds); + var toEndpoint = new PhoneEndpoint { Number = "14155550100" }; + + var exception = await Assert.ThrowsAsync(async () => await client.VoiceClient.CreateCallAsync( + toEndpoint, "14155550100", new Voice.Nccos.Ncco(new Voice.Nccos.TalkAction {Text = "Hello World"}))); + + Assert.NotNull(exception); + Assert.Equal("AppId or Private Key Path missing.", exception.Message); + } + + [Fact] + public void CreateCallWithWrongCredsThrowsAuthException() + { + var expectedUri = $"{ApiUrl}/v1/calls/"; + var expectedResponse = @"{ + ""uuid"": ""63f61863-4a51-4f6b-86e1-46edebcf9356"", + ""status"": ""started"", + ""direction"": ""outbound"", + ""conversation_uuid"": ""CON-f972836a-550f-45fa-956c-12a2ab5b7d22"" + }"; + var expectedRequesetContent = @"{""to"":[{""number"":""14155550100"",""type"":""phone""}],""from"":{""number"":""14155550100"",""type"":""phone""},""ncco"":[{""text"":""Hello World"",""action"":""talk""}]}"; + + Setup(expectedUri, expectedResponse, expectedRequesetContent); + + var creds = Request.Credentials.FromApiKeyAndSecret(ApiKey, ApiSecret); + var client = new VonageClient(creds); + var toEndpoint = new PhoneEndpoint { Number = "14155550100" }; + + var exception = Assert.Throws(() => client.VoiceClient.CreateCall( + toEndpoint, "14155550100", new Voice.Nccos.Ncco(new Voice.Nccos.TalkAction { Text = "Hello World" }))); + + Assert.NotNull(exception); + Assert.Equal("AppId or Private Key Path missing.", exception.Message); + } } } diff --git a/Vonage.Test.Unit/Vonage.Test.Unit.csproj b/Vonage.Test.Unit/Vonage.Test.Unit.csproj index 6930ba28a..99c915cd4 100644 --- a/Vonage.Test.Unit/Vonage.Test.Unit.csproj +++ b/Vonage.Test.Unit/Vonage.Test.Unit.csproj @@ -1,7 +1,7 @@ - net452;net46;net461;net462;netcoreapp2.0;netcoreapp2.1;netcoreapp2.2;net47;net471;net472;net48;netcoreapp3.0;netcoreapp3.1 + net452;net46;net462;netcoreapp2.0;netcoreapp2.1;netcoreapp2.2;net47;net471;net472;net48;netcoreapp3.0;netcoreapp3.1 1701;1702;0618 false Debug;Release;ReleaseSigned @@ -38,6 +38,24 @@ + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -241,4 +259,8 @@ + + + + diff --git a/Vonage/Messaging/NexmoSmsResponseException.cs b/Vonage/Messaging/NexmoSmsResponseException.cs index 24d5ac421..643882606 100644 --- a/Vonage/Messaging/NexmoSmsResponseException.cs +++ b/Vonage/Messaging/NexmoSmsResponseException.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Vonage.Messaging +namespace Vonage.Messaging { public class VonageSmsResponseException : VonageException { diff --git a/Vonage/Messaging/SendSmsResponse.cs b/Vonage/Messaging/SendSmsResponse.cs index 150171d9e..537d9c4c5 100644 --- a/Vonage/Messaging/SendSmsResponse.cs +++ b/Vonage/Messaging/SendSmsResponse.cs @@ -1,4 +1,3 @@ -using Vonage.Common; using Newtonsoft.Json; namespace Vonage.Messaging { diff --git a/Vonage/Request/ApiRequest.cs b/Vonage/Request/ApiRequest.cs index 94073c804..1e303f862 100644 --- a/Vonage/Request/ApiRequest.cs +++ b/Vonage/Request/ApiRequest.cs @@ -2,10 +2,8 @@ using Vonage.Cryptography; using System; using System.Collections.Generic; -using System.ComponentModel; using System.Globalization; using System.IO; -using System.Linq; using System.Net; using System.Net.Http; using System.Reflection; @@ -296,12 +294,18 @@ private static T SendGetRequest(Uri uri, AuthType authType, Credentials creds if (authType == AuthType.Basic) { + if (string.IsNullOrEmpty(apiKey) || string.IsNullOrEmpty(apiSecret)) + throw new VonageAuthenticationException("API Key or API Secret missing."); + var authBytes = Encoding.UTF8.GetBytes(apiKey + ":" + apiSecret); req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(authBytes)); } else if (authType == AuthType.Bearer) { + if (string.IsNullOrEmpty(appId) || string.IsNullOrEmpty(appKeyPath)) + throw new VonageAuthenticationException("AppId or Private Key Path missing."); + req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Jwt.CreateToken(appId, appKeyPath)); } @@ -335,12 +339,18 @@ private static async Task SendGetRequestAsync(Uri uri, AuthType authType, if (authType == AuthType.Basic) { + if (string.IsNullOrEmpty(apiKey) || string.IsNullOrEmpty(apiSecret)) + throw new VonageAuthenticationException("API Key or API Secret missing."); + var authBytes = Encoding.UTF8.GetBytes(apiKey + ":" + apiSecret); req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(authBytes)); } else if (authType == AuthType.Bearer) { + if (string.IsNullOrEmpty(appId) || string.IsNullOrEmpty(appKeyPath)) + throw new VonageAuthenticationException("AppId or Private Key Path missing."); + req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Jwt.CreateToken(appId, appKeyPath)); } @@ -528,12 +538,18 @@ public static async Task DoRequestWithJsonContentAsync(string method, Uri if (authType == AuthType.Basic) { + if (string.IsNullOrEmpty(apiKey) || string.IsNullOrEmpty(apiSecret)) + throw new VonageAuthenticationException("API Key or API Secret missing."); + var authBytes = Encoding.UTF8.GetBytes(apiKey + ":" + apiSecret); req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(authBytes)); } else if (authType == AuthType.Bearer) { + if (string.IsNullOrEmpty(appId) || string.IsNullOrEmpty(appKeyPath)) + throw new VonageAuthenticationException("AppId or Private Key Path missing."); + // attempt bearer token auth req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Jwt.CreateToken(appId, appKeyPath)); @@ -587,15 +603,22 @@ public static T DoRequestWithJsonContent(string method, Uri uri, object paylo switch (authType) { case AuthType.Basic: + if (string.IsNullOrEmpty(apiKey) || string.IsNullOrEmpty(apiSecret)) + throw new VonageAuthenticationException("API Key or API Secret missing."); + var authBytes = Encoding.UTF8.GetBytes(apiKey + ":" + apiSecret); req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(authBytes)); break; case AuthType.Bearer: + if (string.IsNullOrEmpty(appId) || string.IsNullOrEmpty(appKeyPath)) + throw new VonageAuthenticationException("AppId or Private Key Path missing."); + + // attempt bearer token auth req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", - Jwt.CreateToken(appId, appKeyPath)); - break; + Jwt.CreateToken(appId, appKeyPath)); + break; case AuthType.Query: var sb = BuildQueryString(new Dictionary(), creds); @@ -607,6 +630,7 @@ public static T DoRequestWithJsonContent(string method, Uri uri, object paylo } var json = JsonConvert.SerializeObject(payload, Formatting.None, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore }); + logger.LogDebug($"Request URI: {uri}"); logger.LogDebug($"JSON Payload: {json}"); diff --git a/Vonage/VonageAuthenticationException.cs b/Vonage/VonageAuthenticationException.cs new file mode 100644 index 000000000..e9b37bcf9 --- /dev/null +++ b/Vonage/VonageAuthenticationException.cs @@ -0,0 +1,9 @@ +using System; + +namespace Vonage +{ + public class VonageAuthenticationException : Exception + { + public VonageAuthenticationException(string message) : base(message) { } + } +} \ No newline at end of file diff --git a/Vonage/NexmoException.cs b/Vonage/VonageException.cs similarity index 61% rename from Vonage/NexmoException.cs rename to Vonage/VonageException.cs index 675929588..a0a5ca765 100644 --- a/Vonage/NexmoException.cs +++ b/Vonage/VonageException.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Vonage {