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 .markdownlintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CHANGELOG.md
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Please ADD ALL Changes to the UNRELEASED SECTION and not a specific release
- BuildBot.Health.Tests: new test project covering BuildBot.Health at 100% line and branch coverage
- BuildBot.Json.Tests: new test project covering BuildBot.Json at 100% line and branch coverage
- BuildBot.ServiceModel.Tests: Added test coverage for all types in BuildBot.ServiceModel
- BuildBot.Discord.Tests: Added unit tests to increase code coverage to 100%
### Fixed
- Suppress known Scriban 6.2.0 vulnerabilities pending upgrade
- SnsMessage: Token property was never populated from the constructor argument
Expand Down Expand Up @@ -57,6 +58,7 @@ Please ADD ALL Changes to the UNRELEASED SECTION and not a specific release
- Dependencies - Updated Discord.Net to 3.19.1
- Dependencies - Updated Microsoft.Extensions to 10.0.5
- SDK - Updated DotNet SDK to 10.0.301
- BuildBot.Discord.Tests: Added branch coverage tests for null-title paths in DiscordBot.PublishCommonAsync
### Removed
### Deployment Changes
<!--
Expand Down
73 changes: 73 additions & 0 deletions src/BuildBot.Discord.Tests/BuildBot.Discord.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AnalysisLevel>latest</AnalysisLevel>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
<DebuggerSupport>true</DebuggerSupport>
<DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder>
<EnableMicrosoftExtensionsConfigurationBinderSourceGenerator>true</EnableMicrosoftExtensionsConfigurationBinderSourceGenerator>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<EnableTrimAnalyzer>false</EnableTrimAnalyzer>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<EnforceExtendedAnalyzerRules>false</EnforceExtendedAnalyzerRules>
<Features>strict;flow-analysis</Features>
<GenerateNeutralResourcesLanguageAttribute>true</GenerateNeutralResourcesLanguageAttribute>
<IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>
<IlcOptimizationPreference>Size</IlcOptimizationPreference>
<ImplicitUsings>disable</ImplicitUsings>
<IsPackable>false</IsPackable>
<IsPublishable>false</IsPublishable>
<IsTrimmable>false</IsTrimmable>
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
<LangVersion>latest</LangVersion>
<NoWarn />
<NuGetAudit>true</NuGetAudit>
<NuGetAuditLevel>high</NuGetAuditLevel>
<NuGetAuditMode>all</NuGetAuditMode>
<Nullable>enable</Nullable>
<OptimizationPreference>speed</OptimizationPreference>
<OutputType>Exe</OutputType>
<RunAOTCompilation>false</RunAOTCompilation>
<TargetFramework>net10.0</TargetFramework>
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
<TieredCompilation>true</TieredCompilation>
<TieredPGO>true</TieredPGO>
<TreatSpecificWarningsAsErrors />
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner>
<UseSystemResourceKeys>true</UseSystemResourceKeys>
<WarnOnPackingNonPackableProject>false</WarnOnPackingNonPackableProject>
<WarningsAsErrors />
</PropertyGroup>
<Import Project="$(SolutionDir)UnitTests.props" Condition="Exists('$(SolutionDir)UnitTests.props')" />
<ItemGroup>
<ProjectReference Include="..\BuildBot.Discord\BuildBot.Discord.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FunFair.Test.Common" Version="6.2.25.2243" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="xunit.v3.mtp-v2" Version="3.2.2" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AsyncFixer" Version="2.1.0" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="codecracker.CSharp" Version="1.1.0" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="Credfeto.Enumeration.Source.Generation" Version="1.2.139.1741" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="CSharpIsNullAnalyzer" Version="0.1.593" PrivateAssets="all" ExcludeAssets="runtime" />
<PackageReference Include="FunFair.CodeAnalysis" Version="7.1.35.1745" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="FunFair.Test.Source.Generator" Version="6.2.25.2243" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="Meziantou.Analyzer" Version="3.0.22" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.14.15" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="NSubstitute.Analyzers.CSharp" Version="1.0.17" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="Nullable.Extended.Analyzer" Version="1.15.6581" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="Philips.CodeAnalysis.DuplicateCodeAnalyzer" Version="1.6.4" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="Philips.CodeAnalysis.MaintainabilityAnalyzers" Version="1.9.1" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="Roslynator.Analyzers" Version="4.15.0" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="SecurityCodeScan.VS2019" Version="5.6.7" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="SmartAnalyzers.CSharpExtensions.Annotations" Version="4.2.11" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="10.20.0.135146" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="SourceLink.Create.CommandLine" Version="2.8.3" PrivateAssets="All" ExcludeAssets="runtime" />
<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>
83 changes: 83 additions & 0 deletions src/BuildBot.Discord.Tests/DependencyInjectionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using BuildBot.Discord;
using BuildBot.Discord.Models;
using BuildBot.Discord.Services;
using BuildBot.ServiceModel.ComponentStatus;
using Discord;
using FunFair.Test.Common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Xunit;

namespace BuildBot.Discord.Tests;

public sealed class DependencyInjectionTests : DependencyInjectionTestsBase
{
private static readonly DiscordBotConfiguration TestConfig = new(
token: "test-token",
server: "test-server",
channel: "test-channel",
releaseChannel: "test-release-channel"
);

public DependencyInjectionTests(ITestOutputHelper output)
: base(output: output, dependencyInjectionRegistration: Configure) { }

private static IServiceCollection Configure(IServiceCollection services)
{
return services.AddDiscord(TestConfig);
}

[Fact]
public void DiscordBotMustBeRegisteredAsIDiscordBot()
{
this.RequireService<IDiscordBot>();
}

[Fact]
public void DiscordBotMustBeRegisteredAsIComponentStatus()
{
this.RequireService<IComponentStatus>();
}

[Fact]
public void BotMessageChannelMustBeRegistered()
{
this.RequireService<IMessageChannel<BotMessage>>();
}

[Fact]
public void BotReleaseMessageChannelMustBeRegistered()
{
this.RequireService<IMessageChannel<BotReleaseMessage>>();
}

[Fact]
public void BotServiceMustBeRegisteredAsIHostedService()
{
this.RequireService<IHostedService>();
}

[Fact]
public void DiscordRawClientMustBeRegistered()
{
this.RequireService<IDiscordRawClient>();
}

[Fact]
public void DiscordRawClient_WhenDisconnected_LoginStateIsLoggedOut()
{
IDiscordRawClient client = this.GetService<IDiscordRawClient>();
Assert.Equal(expected: LoginState.LoggedOut, actual: client.LoginState);
}

[Fact]
public void FindChannel_WhenClientIsDisconnected_ReturnsNull()
{
IDiscordRawClient client = this.GetService<IDiscordRawClient>();
IDiscordChannel? channel = client.FindChannel(
serverName: "nonexistent-server",
channelName: "nonexistent-channel"
);
Assert.Null(channel);
}
}
37 changes: 37 additions & 0 deletions src/BuildBot.Discord.Tests/Models/BotMessageTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using BuildBot.Discord.Models;
using Discord;
using FunFair.Test.Common;
using Xunit;

namespace BuildBot.Discord.Tests.Models;

public sealed class BotMessageTests : TestBase
{
[Fact]
public void MessageProperty_ReturnsSuppliedBuilder()
{
EmbedBuilder builder = new();
BotMessage msg = new(builder);

Assert.Same(expected: builder, actual: msg.Message);
}

[Fact]
public void TwoInstances_WithSameBuilder_AreEqual()
{
EmbedBuilder builder = new();
BotMessage msg1 = new(builder);
BotMessage msg2 = new(builder);

Assert.Equal(expected: msg1, actual: msg2);
}

[Fact]
public void TwoInstances_WithDifferentBuilders_AreNotEqual()
{
BotMessage msg1 = new(new EmbedBuilder().WithTitle("First Message"));
BotMessage msg2 = new(new EmbedBuilder().WithTitle("Second Message"));

Assert.NotEqual(expected: msg1, actual: msg2);
}
}
37 changes: 37 additions & 0 deletions src/BuildBot.Discord.Tests/Models/BotReleaseMessageTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using BuildBot.Discord.Models;
using Discord;
using FunFair.Test.Common;
using Xunit;

namespace BuildBot.Discord.Tests.Models;

public sealed class BotReleaseMessageTests : TestBase
{
[Fact]
public void MessageProperty_ReturnsSuppliedBuilder()
{
EmbedBuilder builder = new();
BotReleaseMessage msg = new(builder);

Assert.Same(expected: builder, actual: msg.Message);
}

[Fact]
public void TwoInstances_WithSameBuilder_AreEqual()
{
EmbedBuilder builder = new();
BotReleaseMessage msg1 = new(builder);
BotReleaseMessage msg2 = new(builder);

Assert.Equal(expected: msg1, actual: msg2);
}

[Fact]
public void TwoInstances_WithDifferentBuilders_AreNotEqual()
{
BotReleaseMessage msg1 = new(new EmbedBuilder().WithTitle("First Release"));
BotReleaseMessage msg2 = new(new EmbedBuilder().WithTitle("Second Release"));

Assert.NotEqual(expected: msg1, actual: msg2);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Threading;
using System.Threading.Tasks;
using BuildBot.Discord.Models;
using BuildBot.Discord.Publishers;
using Discord;
using FunFair.Test.Common;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;

namespace BuildBot.Discord.Tests.Publishers;

public sealed class DiscordBotMessageNotificationHandlerTests : TestBase
{
[Fact]
public async Task Handle_LogsAndPublishesToChannel()
{
ILogger<DiscordBotMessageNotificationHandler> logger =
this.GetTypedLogger<DiscordBotMessageNotificationHandler>();
logger.IsEnabled(Arg.Any<LogLevel>()).Returns(true);

IMessageChannel<BotMessage> channel = GetSubstitute<IMessageChannel<BotMessage>>();

DiscordBotMessageNotificationHandler handler = new(messageChannel: channel, logger: logger);

EmbedBuilder builder = new EmbedBuilder().WithTitle("Test Notification");
BotMessage notification = new(builder);

await handler.Handle(notification: notification, cancellationToken: this.CancellationToken());

logger.Received(1).IsEnabled(LogLevel.Information);
await channel
.Received(1)
.PublishAsync(message: Arg.Any<BotMessage>(), cancellationToken: Arg.Any<CancellationToken>());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Threading;
using System.Threading.Tasks;
using BuildBot.Discord.Models;
using BuildBot.Discord.Publishers;
using Discord;
using FunFair.Test.Common;
using NSubstitute;
using Xunit;

namespace BuildBot.Discord.Tests.Publishers;

public sealed class DiscordBotReleaseMessageNotificationHandlerTests : TestBase
{
[Fact]
public async Task Handle_PublishesToChannel()
{
IMessageChannel<BotReleaseMessage> channel = GetSubstitute<IMessageChannel<BotReleaseMessage>>();

DiscordBotReleaseMessageNotificationHandler handler = new(channel);

EmbedBuilder builder = new EmbedBuilder().WithTitle("Release Notification");
BotReleaseMessage notification = new(builder);

await handler.Handle(notification: notification, cancellationToken: this.CancellationToken());

await channel
.Received(1)
.PublishAsync(message: Arg.Any<BotReleaseMessage>(), cancellationToken: Arg.Any<CancellationToken>());
}
}
Loading
Loading