1
0
mirror of https://github.com/bitwarden/server synced 2025-12-24 12:13:17 +00:00
Files
server/test/Core.Test/AdminConsole/Services/SlackIntegrationHandlerTests.cs
Brant DeBow 886ba9ae6d Refactor IntegrationHandlerResult to provide more detail around failures (#6736)
* Refactor IntegrationHandlerResult to provide more detail around failures

* ServiceUnavailable now retryable, more explicit http status handling, more consistency with different handlers, additional xmldocs

* Address PR feedback
2025-12-17 11:43:53 -05:00

140 lines
5.6 KiB
C#

using Bit.Core.AdminConsole.Models.Data.EventIntegrations;
using Bit.Core.Models.Slack;
using Bit.Core.Services;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Bit.Test.Common.Helpers;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services;
[SutProviderCustomize]
public class SlackIntegrationHandlerTests
{
private readonly ISlackService _slackService = Substitute.For<ISlackService>();
private readonly string _channelId = "C12345";
private readonly string _token = "xoxb-test-token";
private SutProvider<SlackIntegrationHandler> GetSutProvider()
{
return new SutProvider<SlackIntegrationHandler>()
.SetDependency(_slackService)
.Create();
}
[Theory, BitAutoData]
public async Task HandleAsync_SuccessfulRequest_ReturnsSuccess(IntegrationMessage<SlackIntegrationConfigurationDetails> message)
{
var sutProvider = GetSutProvider();
message.Configuration = new SlackIntegrationConfigurationDetails(_channelId, _token);
_slackService.SendSlackMessageByChannelIdAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>())
.Returns(new SlackSendMessageResponse() { Ok = true, Channel = _channelId });
var result = await sutProvider.Sut.HandleAsync(message);
Assert.True(result.Success);
Assert.Equal(result.Message, message);
await sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
Arg.Is(AssertHelper.AssertPropertyEqual(_token)),
Arg.Is(AssertHelper.AssertPropertyEqual(message.RenderedTemplate)),
Arg.Is(AssertHelper.AssertPropertyEqual(_channelId))
);
}
[Theory]
[InlineData("service_unavailable")]
[InlineData("ratelimited")]
[InlineData("rate_limited")]
[InlineData("internal_error")]
[InlineData("message_limit_exceeded")]
public async Task HandleAsync_FailedRetryableRequest_ReturnsFailureWithRetryable(string error)
{
var sutProvider = GetSutProvider();
var message = new IntegrationMessage<SlackIntegrationConfigurationDetails>()
{
Configuration = new SlackIntegrationConfigurationDetails(_channelId, _token),
MessageId = "MessageId",
RenderedTemplate = "Test Message"
};
_slackService.SendSlackMessageByChannelIdAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>())
.Returns(new SlackSendMessageResponse() { Ok = false, Channel = _channelId, Error = error });
var result = await sutProvider.Sut.HandleAsync(message);
Assert.False(result.Success);
Assert.True(result.Retryable);
Assert.NotNull(result.FailureReason);
Assert.Equal(result.Message, message);
await sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
Arg.Is(AssertHelper.AssertPropertyEqual(_token)),
Arg.Is(AssertHelper.AssertPropertyEqual(message.RenderedTemplate)),
Arg.Is(AssertHelper.AssertPropertyEqual(_channelId))
);
}
[Theory]
[InlineData("access_denied")]
[InlineData("channel_not_found")]
[InlineData("token_expired")]
[InlineData("token_revoked")]
public async Task HandleAsync_FailedNonRetryableRequest_ReturnsNonRetryableFailure(string error)
{
var sutProvider = GetSutProvider();
var message = new IntegrationMessage<SlackIntegrationConfigurationDetails>()
{
Configuration = new SlackIntegrationConfigurationDetails(_channelId, _token),
MessageId = "MessageId",
RenderedTemplate = "Test Message"
};
_slackService.SendSlackMessageByChannelIdAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>())
.Returns(new SlackSendMessageResponse() { Ok = false, Channel = _channelId, Error = error });
var result = await sutProvider.Sut.HandleAsync(message);
Assert.False(result.Success);
Assert.False(result.Retryable);
Assert.NotNull(result.FailureReason);
Assert.Equal(result.Message, message);
await sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
Arg.Is(AssertHelper.AssertPropertyEqual(_token)),
Arg.Is(AssertHelper.AssertPropertyEqual(message.RenderedTemplate)),
Arg.Is(AssertHelper.AssertPropertyEqual(_channelId))
);
}
[Fact]
public async Task HandleAsync_NullResponse_ReturnsRetryableFailure()
{
var sutProvider = GetSutProvider();
var message = new IntegrationMessage<SlackIntegrationConfigurationDetails>()
{
Configuration = new SlackIntegrationConfigurationDetails(_channelId, _token),
MessageId = "MessageId",
RenderedTemplate = "Test Message"
};
_slackService.SendSlackMessageByChannelIdAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>())
.Returns((SlackSendMessageResponse?)null);
var result = await sutProvider.Sut.HandleAsync(message);
Assert.False(result.Success);
Assert.True(result.Retryable); // Null response is classified as TransientError (retryable)
Assert.Equal("Slack response was null", result.FailureReason);
Assert.Equal(result.Message, message);
await sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
Arg.Is(AssertHelper.AssertPropertyEqual(_token)),
Arg.Is(AssertHelper.AssertPropertyEqual(message.RenderedTemplate)),
Arg.Is(AssertHelper.AssertPropertyEqual(_channelId))
);
}
}