From 30b51c0387c7c05c67a94b5fea6fbccbe5bd0f46 Mon Sep 17 00:00:00 2001
From: Mark Ridgwell <273118822+dnyw4l3n13@users.noreply.github.com>
Date: Thu, 2 Jul 2026 15:59:15 +0000
Subject: [PATCH 01/10] feat: make DiscordChannelAdapter testable and add unit
tests
Prompt: Work on issue #374 in funfair-tech/BuildBot.
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d39e792e..780e2628 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
From 8a1f945b52a98616aa3ed4ae5998a0639972480a Mon Sep 17 00:00:00 2001
From: Mark Ridgwell <273118822+dnyw4l3n13@users.noreply.github.com>
Date: Thu, 2 Jul 2026 16:19:04 +0000
Subject: [PATCH 02/10] feat: make DiscordChannelAdapter testable and add unit
tests
- Changed DiscordChannelAdapter constructor from sealed SocketTextChannel to ITextChannel interface
- Changed SendMessageAsync return type from RestUserMessage to IUserMessage
- Made DiscordChannelAdapter public so tests can instantiate it directly (InternalsVisibleTo is prohibited by FFS0051)
- Added explicit Discord.Net package reference to BuildBot.Discord.Tests project
- Added unit tests for Name, EnterTypingState, and SendMessageAsync
Prompt: Work on pull request #387 in funfair-tech/BuildBot.
---
.../BuildBot.Discord.Tests.csproj | 3 +-
.../Services/DiscordChannelAdapterTests.cs | 73 +++++++++++++++++++
.../Services/DiscordChannelAdapter.cs | 10 +--
3 files changed, 79 insertions(+), 7 deletions(-)
create mode 100644 src/BuildBot.Discord.Tests/Services/DiscordChannelAdapterTests.cs
diff --git a/src/BuildBot.Discord.Tests/BuildBot.Discord.Tests.csproj b/src/BuildBot.Discord.Tests/BuildBot.Discord.Tests.csproj
index a05fb59c..a3b7259d 100644
--- a/src/BuildBot.Discord.Tests/BuildBot.Discord.Tests.csproj
+++ b/src/BuildBot.Discord.Tests/BuildBot.Discord.Tests.csproj
@@ -44,6 +44,7 @@
+
@@ -70,4 +71,4 @@
-
\ No newline at end of file
+
diff --git a/src/BuildBot.Discord.Tests/Services/DiscordChannelAdapterTests.cs b/src/BuildBot.Discord.Tests/Services/DiscordChannelAdapterTests.cs
new file mode 100644
index 00000000..1e90bdf4
--- /dev/null
+++ b/src/BuildBot.Discord.Tests/Services/DiscordChannelAdapterTests.cs
@@ -0,0 +1,73 @@
+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();
+ 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();
+ IDisposable typingState = GetSubstitute();
+ channel.EnterTypingState(Arg.Any()).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();
+ IUserMessage message = GetSubstitute();
+ IMessageChannel messageChannel = GetSubstitute();
+
+ messageChannel.Name.Returns("sent-channel");
+ message.Channel.Returns(messageChannel);
+ message.CleanContent.Returns("clean content");
+
+ channel
+ .SendMessageAsync(
+ Arg.Any(),
+ Arg.Any(),
+ Arg.Any