Skip to content

Commit

Permalink
Merge pull request #141 from stankovski/retry
Browse files Browse the repository at this point in the history
Added RetryPolicy
  • Loading branch information
stankovski authored Jul 21, 2020
2 parents f67a0b7 + 652f920 commit 4f0227f
Show file tree
Hide file tree
Showing 52 changed files with 1,410 additions and 1,956 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class ManagementApiE2ETests
private const string ContainerName = "ContainerName";

private NamespaceManager _namespaceManager;
private NamespaceManagerSettings _namespaceManagerSettings;
private NotificationHubSettings _notificationHubSettings;
private string _notificationHubName;
private readonly Uri _inputFileSasUri;
private readonly Uri _outputContainerSasUri;
Expand Down Expand Up @@ -190,22 +190,22 @@ public void ManagementApi_FailsWithAuthorizationException()
var namespaceManager = CreateNamespaceManager(_testServer.RecordingMode, IncorrectConnectionString);

// Check that CreateNotificationHub returns UnauthorizedAccessException when connection string is incorrect
Assert.Throws<UnauthorizedAccessException>(() => namespaceManager.CreateNotificationHub(_notificationHubName));
Assert.Throws<UnauthorizedException>(() => namespaceManager.CreateNotificationHub(_notificationHubName));

// We must create hub to recieve UnauthorizedAccessException when GetNotificationHub and DeleteNotificationHub execute
var notificationHubDescription = _namespaceManager.CreateNotificationHub(_notificationHubName);

// Check that GetNotificationHub returns UnauthorizedAccessException when connection string is incorrect
Assert.Throws<UnauthorizedAccessException>(() => namespaceManager.GetNotificationHub(_notificationHubName));
Assert.Throws<UnauthorizedException>(() => namespaceManager.GetNotificationHub(_notificationHubName));

// Check that NotificationHubExists returns UnauthorizedAccessException when connection string is incorrect
Assert.Throws<UnauthorizedAccessException>(() => namespaceManager.NotificationHubExists(_notificationHubName));
Assert.Throws<UnauthorizedException>(() => namespaceManager.NotificationHubExists(_notificationHubName));

// Check that UpdateNotificationHub returns UnauthorizedAccessException when connection string is incorrect
Assert.Throws<UnauthorizedAccessException>(() => namespaceManager.UpdateNotificationHub(notificationHubDescription));
Assert.Throws<UnauthorizedException>(() => namespaceManager.UpdateNotificationHub(notificationHubDescription));

// Check that DeleteNotificationHub returns UnauthorizedAccessException when connection string is incorrect
Assert.Throws<UnauthorizedAccessException>(() => namespaceManager.DeleteNotificationHub(_notificationHubName));
Assert.Throws<UnauthorizedException>(() => namespaceManager.DeleteNotificationHub(_notificationHubName));
}
finally
{
Expand Down Expand Up @@ -299,10 +299,9 @@ private NamespaceManager CreateNamespaceManager(RecordingMode recordingMode, str
_namespaceUriString = "https://sample.servicebus.windows.net/";
}

var namespaceManagerSettings = new NamespaceManagerSettings();
namespaceManagerSettings.TokenProvider = SharedAccessSignatureTokenProvider.CreateSharedAccessSignatureTokenProvider(connectionString);
var namespaceManagerSettings = new NotificationHubSettings();
namespaceManagerSettings.MessageHandler = _testServer;
return new NamespaceManager(new Uri(_namespaceUriString), namespaceManagerSettings);
return new NamespaceManager(connectionString, namespaceManagerSettings);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="RichardSzalay.MockHttp" Version="6.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for
// license information.
//------------------------------------------------------------

namespace Microsoft.Azure.NotificationHubs.DotNetCore.Tests
{
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Threading.Tasks;
using Microsoft.Azure.NotificationHubs.Messaging;
using RichardSzalay.MockHttp;
using Xunit;

public class NamespaceManagerRetryPolicyTests
{
private readonly string _connectionString;
private readonly string _hubName;
private NamespaceManager _namespaceClient;
private MockHttpMessageHandler _mockHttp;
private const string _hubResponse = "<entry xmlns=\"http://www.w3.org/2005/Atom\"><id>https://sample.servicebus.windows.net/sample?api-version=2017-04</id><title type=\"text\">sample</title><published>2020-07-02T18:03:10Z</published><updated>2020-07-02T18:03:11Z</updated><author><name>sample</name></author><link rel=\"self\" href=\"https://sample.servicebus.windows.net/sample?api-version=2017-04\"/><content type=\"application/xml\"><NotificationHubDescription xmlns=\"http://schemas.microsoft.com/netservices/2010/10/servicebus/connect\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"><RegistrationTtl>P10675199DT2H48M5.4775807S</RegistrationTtl><AuthorizationRules><AuthorizationRule i:type=\"SharedAccessAuthorizationRule\"><ClaimType>SharedAccessKey</ClaimType><ClaimValue>None</ClaimValue><Rights><AccessRights>Listen</AccessRights></Rights><CreatedTime>2020-07-02T18:03:10.772227Z</CreatedTime><ModifiedTime>2020-07-02T18:03:10.772227Z</ModifiedTime><KeyName>DefaultListenSharedAccessSignature</KeyName><PrimaryKey>xxxx</PrimaryKey><SecondaryKey>xxxx</SecondaryKey></AuthorizationRule><AuthorizationRule i:type=\"SharedAccessAuthorizationRule\"><ClaimType>SharedAccessKey</ClaimType><ClaimValue>None</ClaimValue><Rights><AccessRights>Listen</AccessRights><AccessRights>Manage</AccessRights><AccessRights>Send</AccessRights></Rights><CreatedTime>2020-07-02T18:03:10.772227Z</CreatedTime><ModifiedTime>2020-07-02T18:03:10.772227Z</ModifiedTime><KeyName>DefaultFullSharedAccessSignature</KeyName><PrimaryKey>xxxx</PrimaryKey><SecondaryKey>xxxx</SecondaryKey></AuthorizationRule></AuthorizationRules></NotificationHubDescription></content></entry>";

public NamespaceManagerRetryPolicyTests()
{
_connectionString = "Endpoint=sb://sample.servicebus.windows.net/;SharedAccessKeyName=DefaultListenSharedAccessSignature;SharedAccessKey=xxxxxx";
_hubName = "hub-name";
_mockHttp = new MockHttpMessageHandler();
_namespaceClient = new NamespaceManager(_connectionString, new NotificationHubSettings
{
HttpClient = _mockHttp.ToHttpClient(),
RetryOptions = new NotificationHubRetryOptions
{
Delay = TimeSpan.FromMilliseconds(10)
}
});
}

[Theory]
[InlineData(HttpStatusCode.InternalServerError)]
[InlineData(HttpStatusCode.ServiceUnavailable)]
[InlineData(HttpStatusCode.GatewayTimeout)]
[InlineData(HttpStatusCode.RequestTimeout)]
public async Task RetryPolicyRetriesOnTransientErrorInPut(HttpStatusCode errorCode)
{
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name")
.Respond(errorCode);
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name")
.Respond(HttpStatusCode.OK, "application/xml", _hubResponse);

await _namespaceClient.CreateNotificationHubAsync(_hubName);

_mockHttp.VerifyNoOutstandingExpectation();
}

[Fact]
public async Task RetryPolicyRetriesConnectionErrors()
{
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name")
.Throw(new TimeoutException());
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name")
.Throw(new HttpRequestException("", new SocketException((int)SocketError.TimedOut)));
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name")
.Respond(HttpStatusCode.OK, "application/xml", _hubResponse);

await _namespaceClient.CreateNotificationHubAsync(_hubName);

_mockHttp.VerifyNoOutstandingExpectation();
}

[Fact]
public async Task RetryPolicyRetriesOnThrottling()
{
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name")
.Respond((HttpStatusCode)403, new Dictionary<string, string> { { "Retry-After", "1" }}, new StringContent(""));
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name")
.Respond((HttpStatusCode)429, new Dictionary<string, string> { { "Retry-After", "1" } }, new StringContent(""));
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name")
.Respond(HttpStatusCode.OK, "application/xml", _hubResponse);

await _namespaceClient.CreateNotificationHubAsync(_hubName);

_mockHttp.VerifyNoOutstandingExpectation();
}

[Fact]
public async Task RetryPolicyRethrowsNonTransientErrors()
{
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name")
.Respond(HttpStatusCode.NotFound);
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name")
.Respond(HttpStatusCode.OK, "application/xml", _hubResponse);

await Assert.ThrowsAsync<MessagingEntityNotFoundException>(() => _namespaceClient.CreateNotificationHubAsync(_hubName));
}

[Fact]
public async Task RetryPolicyGivesUpAfterTimeout()
{
_namespaceClient = new NamespaceManager(_connectionString, new NotificationHubSettings
{
MessageHandler = _mockHttp,
RetryOptions = new NotificationHubRetryOptions
{
Delay = TimeSpan.FromMilliseconds(10),
MaxRetries = 1
}
});

_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name")
.Throw(new TimeoutException());
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name")
.Throw(new TimeoutException());
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name")
.Respond(HttpStatusCode.OK, "application/xml", _hubResponse);

await Assert.ThrowsAsync<TimeoutException>(() => _namespaceClient.CreateNotificationHubAsync(_hubName));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for
// license information.
//------------------------------------------------------------

namespace Microsoft.Azure.NotificationHubs.DotNetCore.Tests
{
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Threading.Tasks;
using Microsoft.Azure.NotificationHubs.Messaging;
using RichardSzalay.MockHttp;
using Xunit;

public class NotificationHubClientRetryPolicyTests
{
private readonly string _connectionString;
private readonly string _hubName;
private NotificationHubClient _nhClient;
private MockHttpMessageHandler _mockHttp;

public NotificationHubClientRetryPolicyTests()
{
_connectionString = "Endpoint=sb://sample.servicebus.windows.net/;SharedAccessKeyName=DefaultListenSharedAccessSignature;SharedAccessKey=xxxxxx";
_hubName = "hub-name";
_mockHttp = new MockHttpMessageHandler();
_nhClient = new NotificationHubClient(_connectionString, _hubName, new NotificationHubSettings
{
HttpClient = _mockHttp.ToHttpClient(),
RetryOptions = new NotificationHubRetryOptions
{
Delay = TimeSpan.FromMilliseconds(10)
}
});
}

[Theory]
[InlineData(HttpStatusCode.InternalServerError)]
[InlineData(HttpStatusCode.ServiceUnavailable)]
[InlineData(HttpStatusCode.GatewayTimeout)]
[InlineData(HttpStatusCode.RequestTimeout)]
public async Task RetryPolicyRetriesOnTransientErrorInSend(HttpStatusCode errorCode)
{
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name/messages")
.Respond(errorCode);
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name/messages")
.Respond(HttpStatusCode.OK);

await _nhClient.SendDirectNotificationAsync(new FcmNotification("{}"), "123");

_mockHttp.VerifyNoOutstandingExpectation();
}

[Theory]
[InlineData(HttpStatusCode.InternalServerError)]
[InlineData(HttpStatusCode.ServiceUnavailable)]
[InlineData(HttpStatusCode.GatewayTimeout)]
[InlineData(HttpStatusCode.RequestTimeout)]
public async Task RetryPolicyRetriesOnTransientErrorInRegister(HttpStatusCode errorCode)
{
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name/registrations")
.Respond(errorCode);
var registrationXml = "<entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:a=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\"><id>https://sample.servicebus.windows.net/hub-name/registrations/123456?api-version=2017-04</id><title type=\"text\">4757098718499783238-6462592605842469809-1</title><published>2019-05-13T17:12:18Z</published><updated>2019-05-13T17:12:18Z</updated><link rel=\"self\" href=\"https://sdk-sample-namespace.servicebus.windows.net/sdk-sample-nh/registrations/4757098718499783238-6462592605842469809-1?api-version=2017-04\"/><content type=\"application/xml\"><GcmRegistrationDescription xmlns=\"http://schemas.microsoft.com/netservices/2010/10/servicebus/connect\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"><ETag>2</ETag><ExpirationTime>9999-12-31T23:59:59.999</ExpirationTime><RegistrationId>4757098718499783238-6462592605842469809-1</RegistrationId><Tags>tag2</Tags><GcmRegistrationId>amzn1.adm-registration.v2.123</GcmRegistrationId></GcmRegistrationDescription></content></entry>";
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name/registrations")
.Respond("application/atom+xml", registrationXml);

var registration = await _nhClient.CreateFcmNativeRegistrationAsync("123456");

_mockHttp.VerifyNoOutstandingExpectation();
}

[Fact]
public async Task RetryPolicyRetriesConnectionErrors()
{
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name/messages")
.Throw(new TimeoutException());
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name/messages")
.Throw(new HttpRequestException("", new SocketException((int)SocketError.TimedOut)));
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name/messages")
.Respond(HttpStatusCode.OK);

await _nhClient.SendDirectNotificationAsync(new FcmNotification("{}"), "123");

_mockHttp.VerifyNoOutstandingExpectation();
}

[Fact]
public async Task RetryPolicyRetriesOnThrottling()
{
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name/messages")
.Respond((HttpStatusCode)403, new Dictionary<string, string> { { "Retry-After", "1" }}, new StringContent(""));
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name/messages")
.Respond((HttpStatusCode)429, new Dictionary<string, string> { { "Retry-After", "1" } }, new StringContent(""));
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name/messages")
.Respond(HttpStatusCode.OK);

await _nhClient.SendDirectNotificationAsync(new FcmNotification("{}"), "123");

_mockHttp.VerifyNoOutstandingExpectation();
}

[Fact]
public async Task RetryPolicyRethrowsNonTransientErrors()
{
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name/messages")
.Respond(HttpStatusCode.NotFound);
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name/messages")
.Respond(HttpStatusCode.OK);

await Assert.ThrowsAsync<MessagingEntityNotFoundException>(() => _nhClient.SendDirectNotificationAsync(new FcmNotification("{}"), "123"));
}

[Fact]
public async Task RetryPolicyGivesUpAfterTimeout()
{
_nhClient = new NotificationHubClient(_connectionString, _hubName, new NotificationHubSettings
{
MessageHandler = _mockHttp,
RetryOptions = new NotificationHubRetryOptions
{
Delay = TimeSpan.FromMilliseconds(10),
MaxRetries = 1
}
});

_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name/messages")
.Throw(new TimeoutException());
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name/messages")
.Throw(new TimeoutException());
_mockHttp.Expect("https://sample.servicebus.windows.net/hub-name/messages")
.Respond(HttpStatusCode.OK);

await Assert.ThrowsAsync<TimeoutException>(() => _nhClient.SendDirectNotificationAsync(new FcmNotification("{}"), "123"));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public NotificationHubClientTest()
_configuration = builder.Build();
_testServer = new TestServerProxy();

var settings = new NotificationHubClientSettings
var settings = new NotificationHubSettings
{
MessageHandler = _testServer
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
using Xunit;
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for
// license information.
//------------------------------------------------------------

namespace Microsoft.Azure.NotificationHubs.Tests
{
using Xunit;

public class NotificationHubConnectionStringBuilderTests
{
[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<PackageReference Include="xunit.runner.console" Version="2.3.1" />
<PackageReference Include="xunit.runner.msbuild" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<PackageReference Include="RichardSzalay.MockHttp" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.Azure.NotificationHubs\Microsoft.Azure.NotificationHubs.csproj" />
Expand Down
Loading

0 comments on commit 4f0227f

Please sign in to comment.