mirror of
https://github.com/bitwarden/server.git
synced 2025-04-04 20:50:21 -05:00
Refactored SlackOAuthController to use SlackService as an injected dependency; added tests for SlackService
This commit is contained in:
parent
2227b3b867
commit
297046be5b
@ -1,29 +1,23 @@
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.AdminConsole.Controllers;
|
||||
|
||||
[Route("slack/oauth")]
|
||||
public class SlackOAuthController(
|
||||
IHttpClientFactory httpClientFactory,
|
||||
GlobalSettings globalSettings)
|
||||
: Controller
|
||||
public class SlackOAuthController(ISlackService slackService) : Controller
|
||||
{
|
||||
private readonly string _clientId = globalSettings.Slack.ClientId;
|
||||
private readonly string _clientSecret = globalSettings.Slack.ClientSecret;
|
||||
private readonly string _scopes = globalSettings.Slack.Scopes;
|
||||
private readonly string _redirectUrl = globalSettings.Slack.RedirectUrl;
|
||||
private readonly HttpClient _httpClient = httpClientFactory.CreateClient(HttpClientName);
|
||||
|
||||
public const string HttpClientName = "SlackOAuthContollerHttpClient";
|
||||
|
||||
[HttpGet("redirect")]
|
||||
public IActionResult RedirectToSlack()
|
||||
{
|
||||
string slackOAuthUrl = $"https://slack.com/oauth/v2/authorize?client_id={_clientId}&scope={_scopes}&redirect_uri={_redirectUrl}";
|
||||
string callbackUrl = Url.RouteUrl(nameof(OAuthCallback));
|
||||
var redirectUrl = slackService.GetRedirectUrl(callbackUrl);
|
||||
|
||||
return Redirect(slackOAuthUrl);
|
||||
if (string.IsNullOrEmpty(redirectUrl))
|
||||
{
|
||||
return BadRequest("Slack not currently supported.");
|
||||
}
|
||||
|
||||
return Redirect(redirectUrl);
|
||||
}
|
||||
|
||||
[HttpGet("callback")]
|
||||
@ -34,34 +28,20 @@ public class SlackOAuthController(
|
||||
return BadRequest("Missing code from Slack.");
|
||||
}
|
||||
|
||||
var tokenResponse = await _httpClient.PostAsync("https://slack.com/api/oauth.v2.access",
|
||||
new FormUrlEncodedContent(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("client_id", _clientId),
|
||||
new KeyValuePair<string, string>("client_secret", _clientSecret),
|
||||
new KeyValuePair<string, string>("code", code),
|
||||
new KeyValuePair<string, string>("redirect_uri", _redirectUrl)
|
||||
}));
|
||||
string callbackUrl = Url.RouteUrl(nameof(OAuthCallback));
|
||||
var token = await slackService.ObtainTokenViaOAuth(code, callbackUrl);
|
||||
|
||||
var responseBody = await tokenResponse.Content.ReadAsStringAsync();
|
||||
var jsonDoc = JsonDocument.Parse(responseBody);
|
||||
var root = jsonDoc.RootElement;
|
||||
|
||||
if (!root.GetProperty("ok").GetBoolean())
|
||||
if (string.IsNullOrEmpty(token))
|
||||
{
|
||||
return BadRequest($"OAuth failed: {root.GetProperty("error").GetString()}");
|
||||
return BadRequest("Invalid response from Slack.");
|
||||
}
|
||||
|
||||
string botToken = root.GetProperty("access_token").GetString();
|
||||
string teamId = root.GetProperty("team").GetProperty("id").GetString();
|
||||
|
||||
SaveTokenToDatabase(teamId, botToken);
|
||||
|
||||
SaveTokenToDatabase(token);
|
||||
return Ok("Slack OAuth successful. Your bot is now installed.");
|
||||
}
|
||||
|
||||
private void SaveTokenToDatabase(string teamId, string botToken)
|
||||
private void SaveTokenToDatabase(string botToken)
|
||||
{
|
||||
Console.WriteLine($"Stored bot token for team {teamId}: {botToken}");
|
||||
Console.WriteLine($"Stored bot token for team: {botToken}");
|
||||
}
|
||||
}
|
||||
|
@ -27,8 +27,10 @@ using Bit.Core.OrganizationFeatures.OrganizationSubscriptions;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Bit.Api.Auth.Models.Request.WebAuthn;
|
||||
using Bit.Core.AdminConsole.Services.NoopImplementations;
|
||||
using Bit.Core.Auth.Models.Data;
|
||||
using Bit.Core.Auth.Identity.TokenProviders;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tools.ImportFeatures;
|
||||
using Bit.Core.Tools.ReportFeatures;
|
||||
|
||||
@ -212,6 +214,19 @@ public class Startup
|
||||
{
|
||||
services.AddHostedService<Core.HostedServices.ApplicationCacheHostedService>();
|
||||
}
|
||||
|
||||
// Slack
|
||||
if (CoreHelpers.SettingHasValue(globalSettings.Slack.ClientId) &&
|
||||
CoreHelpers.SettingHasValue(globalSettings.Slack.ClientSecret) &&
|
||||
CoreHelpers.SettingHasValue(globalSettings.Slack.Scopes))
|
||||
{
|
||||
services.AddHttpClient(SlackService.HttpClientName);
|
||||
services.AddSingleton<ISlackService, SlackService>();
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddSingleton<ISlackService, NoopSlackService>();
|
||||
}
|
||||
}
|
||||
|
||||
public void Configure(
|
||||
|
@ -27,6 +27,18 @@ public class SlackUserResponse : SlackApiResponse
|
||||
public SlackUser User { get; set; } = new();
|
||||
}
|
||||
|
||||
public class SlackOAuthResponse : SlackApiResponse
|
||||
{
|
||||
[JsonPropertyName("access_token")]
|
||||
public string AccessToken { get; set; } = string.Empty;
|
||||
public SlackTeam Team { get; set; } = new();
|
||||
}
|
||||
|
||||
public class SlackTeam
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class SlackChannel
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
@ -5,5 +5,7 @@ public interface ISlackService
|
||||
Task<string> GetChannelIdAsync(string token, string channelName);
|
||||
Task<List<string>> GetChannelIdsAsync(string token, List<string> channelNames);
|
||||
Task<string> GetDmChannelByEmailAsync(string token, string email);
|
||||
Task SendSlackMessageByChannelId(string token, string message, string channelId);
|
||||
string GetRedirectUrl(string redirectUrl);
|
||||
Task SendSlackMessageByChannelIdAsync(string token, string message, string channelId);
|
||||
Task<string> ObtainTokenViaOAuth(string code, string redirectUrl);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ public class SlackEventHandler(
|
||||
|
||||
foreach (var configuration in configurations)
|
||||
{
|
||||
await slackService.SendSlackMessageByChannelId(
|
||||
await slackService.SendSlackMessageByChannelIdAsync(
|
||||
configuration.Configuration.Token,
|
||||
TemplateProcessor.ReplaceTokens(configuration.Template, eventMessage),
|
||||
configuration.Configuration.ChannelId
|
||||
|
@ -2,15 +2,20 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Web;
|
||||
using Bit.Core.Models.Slack;
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
public class SlackService(
|
||||
IHttpClientFactory httpClientFactory,
|
||||
GlobalSettings globalSettings,
|
||||
ILogger<SlackService> logger) : ISlackService
|
||||
{
|
||||
private readonly HttpClient _httpClient = httpClientFactory.CreateClient(HttpClientName);
|
||||
private readonly string _clientId = globalSettings.Slack.ClientId;
|
||||
private readonly string _clientSecret = globalSettings.Slack.ClientSecret;
|
||||
private readonly string _scopes = globalSettings.Slack.Scopes;
|
||||
|
||||
public const string HttpClientName = "SlackServiceHttpClient";
|
||||
|
||||
@ -19,6 +24,46 @@ public class SlackService(
|
||||
return (await GetChannelIdsAsync(token, new List<string> { channelName })).FirstOrDefault();
|
||||
}
|
||||
|
||||
public string GetRedirectUrl(string redirectUrl)
|
||||
{
|
||||
return $"https://slack.com/oauth/v2/authorize?client_id={_clientId}&scope={_scopes}&redirect_uri={redirectUrl}";
|
||||
}
|
||||
|
||||
public async Task<string> ObtainTokenViaOAuth(string code, string redirectUrl)
|
||||
{
|
||||
var tokenResponse = await _httpClient.PostAsync("https://slack.com/api/oauth.v2.access",
|
||||
new FormUrlEncodedContent(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("client_id", _clientId),
|
||||
new KeyValuePair<string, string>("client_secret", _clientSecret),
|
||||
new KeyValuePair<string, string>("code", code),
|
||||
new KeyValuePair<string, string>("redirect_uri", redirectUrl)
|
||||
}));
|
||||
|
||||
SlackOAuthResponse result;
|
||||
try
|
||||
{
|
||||
result = await tokenResponse.Content.ReadFromJsonAsync<SlackOAuthResponse>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
result = null;
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
logger.LogError("Error obtaining token via OAuth: Unknown error");
|
||||
return string.Empty;
|
||||
}
|
||||
if (!result.Ok)
|
||||
{
|
||||
logger.LogError("Error obtaining token via OAuth: " + result.Error);
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return result.AccessToken;
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetChannelIdsAsync(string token, List<string> channelNames)
|
||||
{
|
||||
var matchingChannelIds = new List<string>();
|
||||
@ -66,7 +111,7 @@ public class SlackService(
|
||||
return await OpenDmChannel(token, userId);
|
||||
}
|
||||
|
||||
public async Task SendSlackMessageByChannelId(string token, string message, string channelId)
|
||||
public async Task SendSlackMessageByChannelIdAsync(string token, string message, string channelId)
|
||||
{
|
||||
var payload = JsonContent.Create(new { channel = channelId, text = message });
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, "https://slack.com/api/chat.postMessage");
|
||||
|
@ -0,0 +1,36 @@
|
||||
using Bit.Core.Services;
|
||||
|
||||
namespace Bit.Core.AdminConsole.Services.NoopImplementations;
|
||||
|
||||
public class NoopSlackService : ISlackService
|
||||
{
|
||||
public Task<string> GetChannelIdAsync(string token, string channelName)
|
||||
{
|
||||
return Task.FromResult(string.Empty);
|
||||
}
|
||||
|
||||
public Task<List<string>> GetChannelIdsAsync(string token, List<string> channelNames)
|
||||
{
|
||||
return Task.FromResult(new List<string>());
|
||||
}
|
||||
|
||||
public Task<string> GetDmChannelByEmailAsync(string token, string email)
|
||||
{
|
||||
return Task.FromResult(string.Empty);
|
||||
}
|
||||
|
||||
public string GetRedirectUrl(string redirectUrl)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public Task SendSlackMessageByChannelIdAsync(string token, string message, string channelId)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<string> ObtainTokenViaOAuth(string code, string redirectUrl)
|
||||
{
|
||||
return Task.FromResult(string.Empty);
|
||||
}
|
||||
}
|
@ -286,8 +286,6 @@ public class GlobalSettings : IGlobalSettings
|
||||
public AzureServiceBusSettings AzureServiceBus { get; set; } = new AzureServiceBusSettings();
|
||||
public virtual List<SlackConfiguration> SlackConfigurations { get; set; } = new List<SlackConfiguration>();
|
||||
public virtual List<WebhookConfiguration> WebhookConfigurations { get; set; } = new List<WebhookConfiguration>();
|
||||
public virtual string SlackChannel { get; set; }
|
||||
public virtual string SlackToken { get; set; }
|
||||
public virtual string WebhookUrl { get; set; }
|
||||
public RabbitMqSettings RabbitMq { get; set; } = new RabbitMqSettings();
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Globalization;
|
||||
using Bit.Core.AdminConsole.Services.Implementations;
|
||||
using Bit.Core.AdminConsole.Services.NoopImplementations;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Repositories;
|
||||
@ -120,10 +121,18 @@ public class Startup
|
||||
|
||||
services.AddSingleton<IOrganizationIntegrationConfigurationRepository, OrganizationIntegrationConfigurationRepository>();
|
||||
|
||||
services.AddHttpClient(SlackService.HttpClientName);
|
||||
services.AddSingleton<ISlackService, SlackService>();
|
||||
if (CoreHelpers.SettingHasValue(globalSettings.Slack.ClientId) &&
|
||||
CoreHelpers.SettingHasValue(globalSettings.Slack.ClientSecret) &&
|
||||
CoreHelpers.SettingHasValue(globalSettings.Slack.Scopes))
|
||||
{
|
||||
services.AddHttpClient(SlackService.HttpClientName);
|
||||
services.AddSingleton<ISlackService, SlackService>();
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddSingleton<ISlackService, NoopSlackService>();
|
||||
}
|
||||
services.AddSingleton<SlackEventHandler>();
|
||||
|
||||
services.AddSingleton<IHostedService>(provider =>
|
||||
new RabbitMqEventListenerService(
|
||||
provider.GetRequiredService<SlackEventHandler>(),
|
||||
@ -131,6 +140,7 @@ public class Startup
|
||||
globalSettings,
|
||||
globalSettings.EventLogging.RabbitMq.SlackQueueName));
|
||||
|
||||
services.AddHttpClient(WebhookEventHandler.HttpClientName);
|
||||
services.AddSingleton<WebhookEventHandler>();
|
||||
services.AddSingleton<IHostedService>(provider =>
|
||||
new RabbitMqEventListenerService(
|
||||
|
@ -0,0 +1,75 @@
|
||||
using Bit.Api.AdminConsole.Controllers;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Api.Test.AdminConsole.Controllers;
|
||||
|
||||
[ControllerCustomize(typeof(SlackOAuthController))]
|
||||
[SutProviderCustomize]
|
||||
public class SlackOAuthControllerTests
|
||||
{
|
||||
[Theory, BitAutoData]
|
||||
public async Task OAuthCallback_ThrowsBadResultWhenCodeIsEmpty(SutProvider<SlackOAuthController> sutProvider)
|
||||
{
|
||||
sutProvider.Sut.Url = Substitute.For<IUrlHelper>();
|
||||
|
||||
var requestAction = await sutProvider.Sut.OAuthCallback(string.Empty);
|
||||
|
||||
Assert.IsType<BadRequestObjectResult>(requestAction);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task OAuthCallback_ThrowsBadResultWhenSlackServiceReturnsEmpty(SutProvider<SlackOAuthController> sutProvider)
|
||||
{
|
||||
sutProvider.Sut.Url = Substitute.For<IUrlHelper>();
|
||||
sutProvider.GetDependency<ISlackService>()
|
||||
.ObtainTokenViaOAuth(Arg.Any<string>(), Arg.Any<string>())
|
||||
.Returns(string.Empty);
|
||||
|
||||
var requestAction = await sutProvider.Sut.OAuthCallback("A_test_code");
|
||||
|
||||
Assert.IsType<BadRequestObjectResult>(requestAction);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task OAuthCallback_CompletesSuccessfully(SutProvider<SlackOAuthController> sutProvider)
|
||||
{
|
||||
sutProvider.Sut.Url = Substitute.For<IUrlHelper>();
|
||||
sutProvider.GetDependency<ISlackService>()
|
||||
.ObtainTokenViaOAuth(Arg.Any<string>(), Arg.Any<string>())
|
||||
.Returns("xoxb-test-token");
|
||||
|
||||
var requestAction = await sutProvider.Sut.OAuthCallback("A_test_code");
|
||||
|
||||
Assert.IsType<OkObjectResult>(requestAction);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void Redirect_ShouldRedirectToSlack(SutProvider<SlackOAuthController> sutProvider)
|
||||
{
|
||||
var expectedUrl = "https://localhost/";
|
||||
|
||||
sutProvider.Sut.Url = Substitute.For<IUrlHelper>();
|
||||
sutProvider.GetDependency<ISlackService>().GetRedirectUrl(Arg.Any<string>()).Returns(expectedUrl);
|
||||
|
||||
var requestAction = sutProvider.Sut.RedirectToSlack();
|
||||
|
||||
var redirectResult = Assert.IsType<RedirectResult>(requestAction);
|
||||
Assert.Equal(expectedUrl, redirectResult.Url);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void Redirect_ThrowsBadResultWhenSlackServiceReturnsEmpty(SutProvider<SlackOAuthController> sutProvider)
|
||||
{
|
||||
sutProvider.Sut.Url = Substitute.For<IUrlHelper>();
|
||||
sutProvider.GetDependency<ISlackService>().GetRedirectUrl(Arg.Any<string>()).Returns(string.Empty);
|
||||
|
||||
var requestAction = sutProvider.Sut.RedirectToSlack();
|
||||
|
||||
Assert.IsType<BadRequestObjectResult>(requestAction);
|
||||
}
|
||||
}
|
@ -86,7 +86,7 @@ public class SlackEventHandlerTests
|
||||
var sutProvider = GetSutProvider(OneConfiguration());
|
||||
|
||||
await sutProvider.Sut.HandleEventAsync(eventMessage);
|
||||
sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelId(
|
||||
sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
|
||||
Arg.Is(AssertHelper.AssertPropertyEqual(_token)),
|
||||
Arg.Is(AssertHelper.AssertPropertyEqual(
|
||||
$"Date: {eventMessage.Date}, Type: {eventMessage.Type}, UserId: {eventMessage.UserId}")),
|
||||
@ -100,13 +100,13 @@ public class SlackEventHandlerTests
|
||||
var sutProvider = GetSutProvider(TwoConfigurations());
|
||||
|
||||
await sutProvider.Sut.HandleEventAsync(eventMessage);
|
||||
sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelId(
|
||||
sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
|
||||
Arg.Is(AssertHelper.AssertPropertyEqual(_token)),
|
||||
Arg.Is(AssertHelper.AssertPropertyEqual(
|
||||
$"Date: {eventMessage.Date}, Type: {eventMessage.Type}, UserId: {eventMessage.UserId}")),
|
||||
Arg.Is(AssertHelper.AssertPropertyEqual(_channelId))
|
||||
);
|
||||
sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelId(
|
||||
sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
|
||||
Arg.Is(AssertHelper.AssertPropertyEqual(_token2)),
|
||||
Arg.Is(AssertHelper.AssertPropertyEqual(
|
||||
$"Date: {eventMessage.Date}, Type: {eventMessage.Type}, UserId: {eventMessage.UserId}")),
|
||||
|
@ -6,6 +6,7 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Bit.Test.Common.MockedHttpClient;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
using GlobalSettings = Bit.Core.Settings.GlobalSettings;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
@ -195,6 +196,70 @@ public class SlackServiceTests
|
||||
Assert.Equal(dmChannelId, result);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void GetRedirectUrl_ReturnsCorrectUrl()
|
||||
{
|
||||
var sutProvider = GetSutProvider();
|
||||
var ClientId = sutProvider.GetDependency<GlobalSettings>().Slack.ClientId;
|
||||
var Scopes = sutProvider.GetDependency<GlobalSettings>().Slack.Scopes;
|
||||
var redirectUrl = "https://example.com/callback";
|
||||
var expectedUrl = $"https://slack.com/oauth/v2/authorize?client_id={ClientId}&scope={Scopes}&redirect_uri={redirectUrl}";
|
||||
var result = sutProvider.Sut.GetRedirectUrl(redirectUrl);
|
||||
Assert.Equal(expectedUrl, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ObtainTokenViaOAuth_ReturnsAccessToken_WhenSuccessful()
|
||||
{
|
||||
var sutProvider = GetSutProvider();
|
||||
var jsonResponse = JsonSerializer.Serialize(new
|
||||
{
|
||||
ok = true,
|
||||
access_token = "test-access-token"
|
||||
});
|
||||
|
||||
_handler.When("https://slack.com/api/oauth.v2.access")
|
||||
.RespondWith(HttpStatusCode.OK)
|
||||
.WithContent(new StringContent(jsonResponse));
|
||||
|
||||
var result = await sutProvider.Sut.ObtainTokenViaOAuth("test-code", "https://example.com/callback");
|
||||
|
||||
Assert.Equal("test-access-token", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ObtainTokenViaOAuth_ReturnsEmptyString_WhenErrorResponse()
|
||||
{
|
||||
var sutProvider = GetSutProvider();
|
||||
var jsonResponse = JsonSerializer.Serialize(new
|
||||
{
|
||||
ok = false,
|
||||
error = "invalid_code"
|
||||
});
|
||||
|
||||
_handler.When("https://slack.com/api/oauth.v2.access")
|
||||
.RespondWith(HttpStatusCode.OK)
|
||||
.WithContent(new StringContent(jsonResponse));
|
||||
|
||||
var result = await sutProvider.Sut.ObtainTokenViaOAuth("test-code", "https://example.com/callback");
|
||||
|
||||
Assert.Equal(string.Empty, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ObtainTokenViaOAuth_ReturnsEmptyString_WhenHttpCallFails()
|
||||
{
|
||||
var sutProvider = GetSutProvider();
|
||||
_handler.When("https://slack.com/api/oauth.v2.access")
|
||||
.RespondWith(HttpStatusCode.InternalServerError)
|
||||
.WithContent(new StringContent(string.Empty));
|
||||
|
||||
var result = await sutProvider.Sut.ObtainTokenViaOAuth("test-code", "https://example.com/callback");
|
||||
|
||||
Assert.Equal(string.Empty, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendSlackMessageByChannelId_Sends_Correct_Message()
|
||||
{
|
||||
@ -206,7 +271,7 @@ public class SlackServiceTests
|
||||
.RespondWith(HttpStatusCode.OK)
|
||||
.WithContent(new StringContent(string.Empty));
|
||||
|
||||
await sutProvider.Sut.SendSlackMessageByChannelId(_token, message, channelId);
|
||||
await sutProvider.Sut.SendSlackMessageByChannelIdAsync(_token, message, channelId);
|
||||
|
||||
Assert.Single(_handler.CapturedRequests);
|
||||
var request = _handler.CapturedRequests[0];
|
||||
|
Loading…
x
Reference in New Issue
Block a user