using System.Net; using Bit.Core.AdminConsole.Models.Data.EventIntegrations; using Bit.Core.Services; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using Bit.Test.Common.Helpers; using Bit.Test.Common.MockedHttpClient; using Microsoft.Extensions.Time.Testing; using NSubstitute; using Xunit; namespace Bit.Core.Test.Services; [SutProviderCustomize] public class WebhookIntegrationHandlerTests { private readonly MockedHttpMessageHandler _handler; private readonly HttpClient _httpClient; private const string _webhookUrl = "http://localhost/test/event"; public WebhookIntegrationHandlerTests() { _handler = new MockedHttpMessageHandler(); _handler.Fallback .WithStatusCode(HttpStatusCode.OK) .WithContent(new StringContent("testtest")); _httpClient = _handler.ToHttpClient(); } private SutProvider GetSutProvider() { var clientFactory = Substitute.For(); clientFactory.CreateClient(WebhookIntegrationHandler.HttpClientName).Returns(_httpClient); return new SutProvider() .SetDependency(clientFactory) .WithFakeTimeProvider() .Create(); } [Theory, BitAutoData] public async Task HandleAsync_SuccessfulRequest_ReturnsSuccess(IntegrationMessage message) { var sutProvider = GetSutProvider(); message.Configuration = new WebhookIntegrationConfigurationDetails(_webhookUrl); var result = await sutProvider.Sut.HandleAsync(message); Assert.True(result.Success); Assert.Equal(result.Message, message); sutProvider.GetDependency().Received(1).CreateClient( Arg.Is(AssertHelper.AssertPropertyEqual(WebhookIntegrationHandler.HttpClientName)) ); Assert.Single(_handler.CapturedRequests); var request = _handler.CapturedRequests[0]; Assert.NotNull(request); var returned = await request.Content.ReadAsStringAsync(); Assert.Equal(HttpMethod.Post, request.Method); Assert.Equal(_webhookUrl, request.RequestUri.ToString()); AssertHelper.AssertPropertyEqual(message.RenderedTemplate, returned); } [Theory, BitAutoData] public async Task HandleAsync_TooManyRequests_ReturnsFailureSetsDelayUntilDate(IntegrationMessage message) { var sutProvider = GetSutProvider(); var now = new DateTime(2014, 3, 2, 1, 0, 0, DateTimeKind.Utc); var retryAfter = now.AddSeconds(60); sutProvider.GetDependency().SetUtcNow(now); message.Configuration = new WebhookIntegrationConfigurationDetails(_webhookUrl); _handler.Fallback .WithStatusCode(HttpStatusCode.TooManyRequests) .WithHeader("Retry-After", "60") .WithContent(new StringContent("testtest")); var result = await sutProvider.Sut.HandleAsync(message); Assert.False(result.Success); Assert.True(result.Retryable); Assert.Equal(result.Message, message); Assert.True(result.DelayUntilDate.HasValue); Assert.Equal(retryAfter, result.DelayUntilDate.Value); Assert.Equal("Too Many Requests", result.FailureReason); } [Theory, BitAutoData] public async Task HandleAsync_TooManyRequestsWithDate_ReturnsFailureSetsDelayUntilDate(IntegrationMessage message) { var sutProvider = GetSutProvider(); var now = new DateTime(2014, 3, 2, 1, 0, 0, DateTimeKind.Utc); var retryAfter = now.AddSeconds(60); message.Configuration = new WebhookIntegrationConfigurationDetails(_webhookUrl); _handler.Fallback .WithStatusCode(HttpStatusCode.TooManyRequests) .WithHeader("Retry-After", retryAfter.ToString("r")) .WithContent(new StringContent("testtest")); var result = await sutProvider.Sut.HandleAsync(message); Assert.False(result.Success); Assert.True(result.Retryable); Assert.Equal(result.Message, message); Assert.True(result.DelayUntilDate.HasValue); Assert.Equal(retryAfter, result.DelayUntilDate.Value); Assert.Equal("Too Many Requests", result.FailureReason); } [Theory, BitAutoData] public async Task HandleAsync_InternalServerError_ReturnsFailureSetsRetryable(IntegrationMessage message) { var sutProvider = GetSutProvider(); message.Configuration = new WebhookIntegrationConfigurationDetails(_webhookUrl); _handler.Fallback .WithStatusCode(HttpStatusCode.InternalServerError) .WithContent(new StringContent("testtest")); var result = await sutProvider.Sut.HandleAsync(message); Assert.False(result.Success); Assert.True(result.Retryable); Assert.Equal(result.Message, message); Assert.False(result.DelayUntilDate.HasValue); Assert.Equal("Internal Server Error", result.FailureReason); } [Theory, BitAutoData] public async Task HandleAsync_UnexpectedRedirect_ReturnsFailureNotRetryable(IntegrationMessage message) { var sutProvider = GetSutProvider(); message.Configuration = new WebhookIntegrationConfigurationDetails(_webhookUrl); _handler.Fallback .WithStatusCode(HttpStatusCode.TemporaryRedirect) .WithContent(new StringContent("testtest")); var result = await sutProvider.Sut.HandleAsync(message); Assert.False(result.Success); Assert.False(result.Retryable); Assert.Equal(result.Message, message); Assert.Null(result.DelayUntilDate); Assert.Equal("Temporary Redirect", result.FailureReason); } }