Skip to content

Commit

Permalink
Changing the lifetime of the IRecaptchaService to scoped.
Browse files Browse the repository at this point in the history
  • Loading branch information
jooni91 committed Aug 14, 2022
1 parent 40dc647 commit c00fdb7
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 13 deletions.
5 changes: 5 additions & 0 deletions docs/Griesoft.AspNetCore.ReCaptcha.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/ReCaptcha/Configuration/RecaptchaServiceConstants.cs
Expand Up @@ -24,5 +24,10 @@ public class RecaptchaServiceConstants
/// The section name in the appsettings.json from which the settings are read.
/// </summary>
public const string SettingsSectionKey = "RecaptchaSettings";

/// <summary>
/// The named HttpClient name that we use in the IRecpatchaService.
/// </summary>
public const string RecaptchaServiceHttpClientName = "ReCaptchaValidationClient";
}
}
4 changes: 3 additions & 1 deletion src/ReCaptcha/Extensions/RecaptchaServiceExtensions.cs
Expand Up @@ -26,11 +26,13 @@ public static IServiceCollection AddRecaptchaService(this IServiceCollection ser

services.Configure(options ??= opt => { });

services.AddHttpClient<IRecaptchaService, RecaptchaService>(client =>
services.AddHttpClient(RecaptchaServiceConstants.RecaptchaServiceHttpClientName, client =>
{
client.BaseAddress = new Uri(RecaptchaServiceConstants.GoogleRecaptchaEndpoint);
});

services.AddScoped<IRecaptchaService, RecaptchaService>();

services.AddTransient<IValidateRecaptchaFilter, ValidateRecaptchaFilter>();

return services;
Expand Down
9 changes: 5 additions & 4 deletions src/ReCaptcha/Services/RecaptchaService.cs
Expand Up @@ -17,14 +17,14 @@ namespace Griesoft.AspNetCore.ReCaptcha.Services
internal class RecaptchaService : IRecaptchaService
{
private readonly RecaptchaSettings _settings;
private readonly HttpClient _httpClient;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<RecaptchaService> _logger;

public RecaptchaService(IOptionsMonitor<RecaptchaSettings> settings,
HttpClient httpClient, ILogger<RecaptchaService> logger)
IHttpClientFactory httpClientFactory, ILogger<RecaptchaService> logger)
{
_settings = settings.CurrentValue;
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
_logger = logger;
}

Expand All @@ -38,7 +38,8 @@ public async Task<ValidationResponse> ValidateRecaptchaResponse(string token, st

try
{
var response = await _httpClient.PostAsync($"?secret={_settings.SecretKey}&response={token}{(remoteIp != null ? $"&remoteip={remoteIp}" : "")}", null!)
var httpClient = _httpClientFactory.CreateClient(RecaptchaServiceConstants.RecaptchaServiceHttpClientName);
var response = await httpClient.PostAsync($"?secret={_settings.SecretKey}&response={token}{(remoteIp != null ? $"&remoteip={remoteIp}" : "")}", null!)
.ConfigureAwait(true);

response.EnsureSuccessStatusCode();
Expand Down
Expand Up @@ -17,8 +17,8 @@ public void AddRecaptchaService_ShouldAddAllRequired_WithDefaultOptions()
services.AddRecaptchaService();

// Assert
Assert.IsTrue(services.Any(service => service.ServiceType.FullName == "Griesoft.AspNetCore.ReCaptcha.Services.IRecaptchaService"));
Assert.IsTrue(services.Any(service => service.ServiceType.FullName == "Griesoft.AspNetCore.ReCaptcha.Filters.IValidateRecaptchaFilter"));
Assert.IsTrue(services.Any(service => service.ServiceType.FullName == "Griesoft.AspNetCore.ReCaptcha.Services.IRecaptchaService" && service.Lifetime == ServiceLifetime.Scoped));
Assert.IsTrue(services.Any(service => service.ServiceType.FullName == "Griesoft.AspNetCore.ReCaptcha.Filters.IValidateRecaptchaFilter" && service.Lifetime == ServiceLifetime.Transient));
Assert.IsTrue(services.Any(service => service.ServiceType.FullName == "Microsoft.Extensions.Options.IConfigureOptions`1[[Griesoft.AspNetCore.ReCaptcha.Configuration.RecaptchaOptions, Griesoft.AspNetCore.ReCaptcha, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"));
Assert.IsTrue(services.Any(service => service.ServiceType.FullName == "Microsoft.Extensions.Options.IConfigureOptions`1[[Griesoft.AspNetCore.ReCaptcha.Configuration.RecaptchaSettings, Griesoft.AspNetCore.ReCaptcha, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"));
}
Expand Down
31 changes: 25 additions & 6 deletions tests/ReCaptcha.Tests/Services/RecaptchaServiceTests.cs
Expand Up @@ -26,6 +26,7 @@ public class RecaptchaServiceTests
private Mock<IOptionsMonitor<RecaptchaSettings>> _settingsMock;
private Mock<HttpMessageHandler> _httpMessageHandlerMock;
private HttpClient _httpClient;
private Mock<IHttpClientFactory> _httpClientFactory;

[SetUp]
public void Initialize()
Expand Down Expand Up @@ -59,6 +60,11 @@ public void Initialize()
{
BaseAddress = new Uri("http://test.com/"),
};

_httpClientFactory = new Mock<IHttpClientFactory>();
_httpClientFactory.Setup(instance => instance.CreateClient(It.Is<string>(val => val == RecaptchaServiceConstants.RecaptchaServiceHttpClientName)))
.Returns(_httpClient)
.Verifiable();
}

[Test]
Expand All @@ -68,7 +74,7 @@ public void Construction_IsSuccessful()


// Act
var instance = new RecaptchaService(_settingsMock.Object, _httpClient, _logger);
var instance = new RecaptchaService(_settingsMock.Object, _httpClientFactory.Object, _logger);

// Assert
Assert.NotNull(instance);
Expand All @@ -79,7 +85,7 @@ public void Construction_IsSuccessful()
public void ValidateRecaptchaResponse_ShouldThrow_ArgumentNullException()
{
// Arrange
var service = new RecaptchaService(_settingsMock.Object, _httpClient, _logger);
var service = new RecaptchaService(_settingsMock.Object, _httpClientFactory.Object, _logger);

// Act

Expand Down Expand Up @@ -110,13 +116,19 @@ public async Task ValidateRecaptchaResponse_ShouldReturn_HttpRequestError()
BaseAddress = new Uri("http://test.com/"),
};

var service = new RecaptchaService(_settingsMock.Object, _httpClient, _logger);
_httpClientFactory = new Mock<IHttpClientFactory>();
_httpClientFactory.Setup(instance => instance.CreateClient(It.Is<string>(val => val == RecaptchaServiceConstants.RecaptchaServiceHttpClientName)))
.Returns(_httpClient)
.Verifiable();

var service = new RecaptchaService(_settingsMock.Object, _httpClientFactory.Object, _logger);

// Act
var response = await service.ValidateRecaptchaResponse(Token);

// Assert
_httpMessageHandlerMock.Verify();
_httpClientFactory.Verify();
Assert.GreaterOrEqual(response.Errors.Count(), 1);
Assert.AreEqual(ValidationError.HttpRequestFailed, response.Errors.First());
}
Expand All @@ -140,21 +152,27 @@ public void ValidateRecaptchaResponse_ShouldThrowAnyOtherThan_HttpRequestExcepti
BaseAddress = new Uri("http://test.com/"),
};

var service = new RecaptchaService(_settingsMock.Object, _httpClient, _logger);
_httpClientFactory = new Mock<IHttpClientFactory>();
_httpClientFactory.Setup(instance => instance.CreateClient(It.Is<string>(val => val == RecaptchaServiceConstants.RecaptchaServiceHttpClientName)))
.Returns(_httpClient)
.Verifiable();

var service = new RecaptchaService(_settingsMock.Object, _httpClientFactory.Object, _logger);

// Act


// Assert
Assert.ThrowsAsync<Exception>(() => service.ValidateRecaptchaResponse(Token));
_httpMessageHandlerMock.Verify();
_httpClientFactory.Verify();
}

[Test]
public async Task ValidateRecaptchaResponse_ShouldReturn_DeserializedResponse()
{
// Arrange
var service = new RecaptchaService(_settingsMock.Object, _httpClient, _logger);
var service = new RecaptchaService(_settingsMock.Object, _httpClientFactory.Object, _logger);

// Act
var response = await service.ValidateRecaptchaResponse(Token);
Expand All @@ -169,13 +187,14 @@ public async Task ValidateRecaptchaResponse_ShouldReturn_DeserializedResponse()
public async Task ValidateRecaptchaResponse_Should_DeserializedResponse_AndSet_ValidationResponseProperty()
{
// Arrange
var service = new RecaptchaService(_settingsMock.Object, _httpClient, _logger);
var service = new RecaptchaService(_settingsMock.Object, _httpClientFactory.Object, _logger);

// Act
var response = await service.ValidateRecaptchaResponse(Token);

// Assert
_httpMessageHandlerMock.Verify();
_httpClientFactory.Verify();
Assert.IsTrue(response.Success);
Assert.AreEqual(0, response.Errors.Count());
Assert.NotNull(service.ValidationResponse);
Expand Down

0 comments on commit c00fdb7

Please sign in to comment.