Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Please ADD ALL Changes to the UNRELEASED SECTION and not a specific release
- SnsMessage: Token property was never populated from the constructor argument
- Added null guards for botMessageChannel and botReleaseMessageChannel parameters in BotService constructor
- Pass ILogger to HealthCheckClient.ExecuteAsync to match new API in Credfeto.Docker.HealthCheck.Http.Client 0.0.72.928
- Made DiscordChannelAdapter testable by accepting ITextChannel instead of the sealed SocketTextChannel, and added unit tests
### Changed
- Dependencies - Updated NSubstitute.Analyzers.CSharp to 1.0.17
- Switched to use minimal APIs
Expand Down
2 changes: 1 addition & 1 deletion src/BuildBot.Discord.Tests/BuildBot.Discord.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,4 @@
<PackageReference Include="ToStringWithoutOverrideAnalyzer" Version="0.6.0" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="xunit.analyzers" Version="1.27.0" PrivateAssets="All" ExcludeAssets="runtime" />
</ItemGroup>
</Project>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System;
using System.Threading.Tasks;
using BuildBot.Discord.Services;
using Discord;
using FunFair.Test.Common;
using NSubstitute;
using Xunit;

namespace BuildBot.Discord.Tests.Services;

public sealed class DiscordChannelAdapterTests : TestBase
{
[Fact]
public void Name_ReturnsChannelName()
{
ITextChannel channel = GetSubstitute<ITextChannel>();
channel.Name.Returns("test-channel");

DiscordChannelAdapter adapter = new(channel);

Assert.Equal(expected: "test-channel", actual: adapter.Name);
}

[Fact]
public void EnterTypingState_ReturnsTypingStateFromChannel()
{
ITextChannel channel = GetSubstitute<ITextChannel>();
IDisposable typingState = GetSubstitute<IDisposable>();
channel.EnterTypingState(Arg.Any<RequestOptions>()).Returns(typingState);

DiscordChannelAdapter adapter = new(channel);

IDisposable result = adapter.EnterTypingState();

Assert.Same(expected: typingState, actual: result);
}

[Fact]
public async Task SendMessageAsync_ReturnsChannelNameAndCleanContent()
{
ITextChannel channel = GetSubstitute<ITextChannel>();
IUserMessage message = GetSubstitute<IUserMessage>();

channel.Name.Returns("sent-channel");

// In production CleanContent is always "" because the adapter sends text: string.Empty (embed-only).
message.CleanContent.Returns("clean content");
Comment thread
credfeto marked this conversation as resolved.

channel.SendMessageAsync(text: string.Empty, embed: default).ReturnsForAnyArgs(Task.FromResult(message));

DiscordChannelAdapter adapter = new(channel);

Embed embed = new EmbedBuilder().Build();
(string sentToChannel, string messageContent) = await adapter.SendMessageAsync(embed);

Assert.Equal(expected: "sent-channel", actual: sentToChannel);
Assert.Equal(expected: "clean content", actual: messageContent);
Comment thread
credfeto marked this conversation as resolved.

await channel
.Received(1)
.SendMessageAsync(
text: string.Empty,
isTTS: Arg.Any<bool>(),
embed: embed,
options: Arg.Any<RequestOptions>(),
allowedMentions: Arg.Any<AllowedMentions>(),
messageReference: Arg.Any<MessageReference>(),
components: Arg.Any<MessageComponent>(),
stickers: Arg.Any<ISticker[]>(),
embeds: Arg.Any<Embed[]>(),
flags: Arg.Any<MessageFlags>(),
poll: Arg.Any<PollProperties>()
);
}
}
14 changes: 6 additions & 8 deletions src/BuildBot.Discord/Services/DiscordChannelAdapter.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Rest;
using Discord.WebSocket;

namespace BuildBot.Discord.Services;

internal sealed class DiscordChannelAdapter : IDiscordChannel
public sealed class DiscordChannelAdapter : IDiscordChannel
Comment thread
credfeto marked this conversation as resolved.
{
private readonly SocketTextChannel _channel;
private readonly ITextChannel _channel;

public DiscordChannelAdapter(SocketTextChannel channel)
public DiscordChannelAdapter(ITextChannel channel)
{
this._channel = channel;
this._channel = channel ?? throw new ArgumentNullException(nameof(channel));
}

public string Name => this._channel.Name;
Expand All @@ -24,8 +22,8 @@ public IDisposable EnterTypingState()

public async Task<(string SentToChannel, string MessageContent)> SendMessageAsync(Embed embed)
{
RestUserMessage msg = await this._channel.SendMessageAsync(text: string.Empty, embed: embed);
IUserMessage msg = await this._channel.SendMessageAsync(text: string.Empty, embed: embed);

return (msg.Channel.Name, msg.CleanContent);
return (this._channel.Name, msg.CleanContent);
}
Comment thread
credfeto marked this conversation as resolved.
}
Loading