refactor: separate interfaces from project ready for unit testing

This commit is contained in:
Oliver Booth 2023-08-26 17:06:12 +01:00
parent ade948ccab
commit 073d704ce6
Signed by: oliverbooth
GPG Key ID: B89D139977693FED
25 changed files with 202 additions and 104 deletions

View File

@ -0,0 +1,33 @@
namespace VPLink.Common.Configuration;
/// <summary>
/// Represents the bot configuration.
/// </summary>
public interface IBotConfiguration
{
/// <summary>
/// Gets a value indicating whether the bot should announce avatar events.
/// </summary>
/// <value>
/// <see langword="true" /> if the bot should announce avatar events; otherwise, <see langword="false" />.
/// </value>
bool AnnounceAvatarEvents { get; }
/// <summary>
/// Gets a value indicating whether the bot should announce avatar events for bots.
/// </summary>
/// <value>
/// <see langword="true" /> if the bot should announce avatar events for bots; otherwise,
/// <see langword="false" />.
/// </value>
bool AnnounceBots { get; }
/// <summary>
/// Gets a value indicating whether the bot should relay messages from other bots.
/// </summary>
/// <value>
/// <see langword="true" /> if the bot should relay messages from other bots; otherwise,
/// <see langword="false" />.
/// </value>
bool RelayBotMessages { get; }
}

View File

@ -0,0 +1,21 @@
using VpSharp;
namespace VPLink.Common.Configuration;
/// <summary>
/// Represents the chat configuration.
/// </summary>
public interface IChatConfiguration
{
/// <summary>
/// Gets or sets the color of the message.
/// </summary>
/// <value>The message color.</value>
uint Color { get; set; }
/// <summary>
/// Gets or sets the font style of the message.
/// </summary>
/// <value>The font style.</value>
FontStyle Style { get; set; }
}

View File

@ -0,0 +1,19 @@
namespace VPLink.Common.Configuration;
/// <summary>
/// Represents the Discord configuration.
/// </summary>
public interface IDiscordConfiguration
{
/// <summary>
/// Gets the channel ID to which the bot should relay messages.
/// </summary>
/// <value>The channel ID.</value>
ulong ChannelId { get; }
/// <summary>
/// Gets the Discord token.
/// </summary>
/// <value>The Discord token.</value>
string Token { get; }
}

View File

@ -0,0 +1,37 @@
namespace VPLink.Common.Configuration;
/// <summary>
/// Represents the Virtual Paradise configuration.
/// </summary>
public interface IVirtualParadiseConfiguration
{
/// <summary>
/// Gets or sets the display name of the bot.
/// </summary>
/// <value>The display name.</value>
string BotName { get; set; }
/// <summary>
/// Gets or sets the chat configuration.
/// </summary>
/// <value>The chat configuration.</value>
IChatConfiguration Chat { get; }
/// <summary>
/// Gets or sets the password with which to log in to Virtual Paradise.
/// </summary>
/// <value>The login password.</value>
string Password { get; set; }
/// <summary>
/// Gets or sets the username with which to log in to Virtual Paradise.
/// </summary>
/// <value>The login username.</value>
string Username { get; set; }
/// <summary>
/// Gets or sets the world into which the bot should enter.
/// </summary>
/// <value>The world to enter.</value>
string World { get; set; }
}

View File

@ -1,4 +1,4 @@
namespace VPLink.Data;
namespace VPLink.Common.Data;
/// <summary>
/// Represents a message that is relayed between Discord and Virtual Paradise.

View File

@ -1,6 +1,6 @@
using VpSharp.Entities;
namespace VPLink.Services;
namespace VPLink.Common.Services;
/// <summary>
/// Represents a service that listens for, and triggers, avatar events.

View File

@ -1,6 +1,6 @@
using VPLink.Configuration;
using VPLink.Common.Configuration;
namespace VPLink.Services;
namespace VPLink.Common.Services;
/// <summary>
/// Represents the configuration service.
@ -11,17 +11,17 @@ public interface IConfigurationService
/// Gets the bot configuration.
/// </summary>
/// <value>The bot configuration.</value>
BotConfiguration BotConfiguration { get; }
IBotConfiguration BotConfiguration { get; }
/// <summary>
/// Gets the Discord configuration.
/// </summary>
/// <value>The Discord configuration.</value>
DiscordConfiguration DiscordConfiguration { get; }
IDiscordConfiguration DiscordConfiguration { get; }
/// <summary>
/// Gets the Virtual Paradise configuration.
/// </summary>
/// <value>The Virtual Paradise configuration.</value>
VirtualParadiseConfiguration VirtualParadiseConfiguration { get; }
IVirtualParadiseConfiguration VirtualParadiseConfiguration { get; }
}

View File

@ -1,7 +1,7 @@
using VPLink.Data;
using VPLink.Common.Data;
using VpSharp.Entities;
namespace VPLink.Services;
namespace VPLink.Common.Services;
/// <summary>
/// Represents a service that listens for messages from the Discord bridge channel.

View File

@ -1,6 +1,6 @@
using VPLink.Data;
using VPLink.Common.Data;
namespace VPLink.Services;
namespace VPLink.Common.Services;
/// <summary>
/// Represents an object that can be used as a relay target.

View File

@ -1,6 +1,6 @@
using VPLink.Data;
using VPLink.Common.Data;
namespace VPLink.Services;
namespace VPLink.Common.Services;
/// <summary>
/// Represents a service that listens for messages from the Virtual Paradise world.

View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="VpSharp" Version="0.1.0-nightly.43"/>
</ItemGroup>
</Project>

View File

@ -1,4 +1,4 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VPLink", "VPLink\VPLink.csproj", "{CD488A1E-0232-4EB5-A381-38A42B267B11}"
EndProject
@ -19,6 +19,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workflows", "Workflows", "{
.github\workflows\release.yml = .github\workflows\release.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VPLink.Common", "VPLink.Common\VPLink.Common.csproj", "{F1CE3BA6-05AC-47F3-BB7E-C489DC132367}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -29,5 +31,9 @@ Global
{CD488A1E-0232-4EB5-A381-38A42B267B11}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CD488A1E-0232-4EB5-A381-38A42B267B11}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CD488A1E-0232-4EB5-A381-38A42B267B11}.Release|Any CPU.Build.0 = Release|Any CPU
{F1CE3BA6-05AC-47F3-BB7E-C489DC132367}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F1CE3BA6-05AC-47F3-BB7E-C489DC132367}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F1CE3BA6-05AC-47F3-BB7E-C489DC132367}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F1CE3BA6-05AC-47F3-BB7E-C489DC132367}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -1,33 +1,16 @@
using VPLink.Common.Configuration;
namespace VPLink.Configuration;
/// <summary>
/// Represents the bot configuration.
/// </summary>
public sealed class BotConfiguration
/// <inheritdoc />
internal sealed class BotConfiguration : IBotConfiguration
{
/// <summary>
/// Gets or sets a value indicating whether the bot should announce avatar events.
/// </summary>
/// <value>
/// <see langword="true" /> if the bot should announce avatar events; otherwise, <see langword="false" />.
/// </value>
/// <inheritdoc />
public bool AnnounceAvatarEvents { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether the bot should announce avatar events for bots.
/// </summary>
/// <value>
/// <see langword="true" /> if the bot should announce avatar events for bots; otherwise,
/// <see langword="false" />.
/// </value>
/// <inheritdoc />
public bool AnnounceBots { get; set; } = false;
/// <summary>
/// Gets or sets a value indicating whether the bot should relay messages from other bots.
/// </summary>
/// <value>
/// <see langword="true" /> if the bot should relay messages from other bots; otherwise,
/// <see langword="false" />.
/// </value>
/// <inheritdoc />
public bool RelayBotMessages { get; set; } = false;
}

View File

@ -1,21 +1,14 @@
using VPLink.Common.Configuration;
using VpSharp;
namespace VPLink.Configuration;
/// <summary>
/// Represents the chat configuration.
/// </summary>
public sealed class ChatConfiguration
/// <inheritdoc />
internal sealed class ChatConfiguration : IChatConfiguration
{
/// <summary>
/// Gets or sets the color of the message.
/// </summary>
/// <value>The message color.</value>
/// <inheritdoc />
public uint Color { get; set; } = 0x191970;
/// <summary>
/// Gets or sets the font style of the message.
/// </summary>
/// <value>The font style.</value>
/// <inheritdoc />
public FontStyle Style { get; set; } = FontStyle.Regular;
}

View File

@ -1,19 +1,13 @@
using VPLink.Common.Configuration;
namespace VPLink.Configuration;
/// <summary>
/// Represents the Discord configuration.
/// </summary>
public sealed class DiscordConfiguration
/// <inheritdoc />
internal sealed class DiscordConfiguration : IDiscordConfiguration
{
/// <summary>
/// Gets or sets the channel ID to which the bot should relay messages.
/// </summary>
/// <value>The channel ID.</value>
/// <inheritdoc />
public ulong ChannelId { get; set; }
/// <summary>
/// Gets or sets the Discord token.
/// </summary>
/// <value>The Discord token.</value>
/// <inheritdoc />
public string Token { get; set; } = string.Empty;
}

View File

@ -1,37 +1,22 @@
using VPLink.Common.Configuration;
namespace VPLink.Configuration;
/// <summary>
/// Represents the Virtual Paradise configuration.
/// </summary>
public sealed class VirtualParadiseConfiguration
/// <inheritdoc />
internal sealed class VirtualParadiseConfiguration : IVirtualParadiseConfiguration
{
/// <summary>
/// Gets or sets the display name of the bot.
/// </summary>
/// <value>The display name.</value>
/// <inheritdoc />
public string BotName { get; set; } = "VPLink";
/// <summary>
/// Gets or sets the chat configuration.
/// </summary>
/// <value>The chat configuration.</value>
public ChatConfiguration Chat { get; } = new();
/// <inheritdoc />
public IChatConfiguration Chat { get; } = new ChatConfiguration();
/// <summary>
/// Gets or sets the password with which to log in to Virtual Paradise.
/// </summary>
/// <value>The login password.</value>
/// <inheritdoc />
public string Password { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the username with which to log in to Virtual Paradise.
/// </summary>
/// <value>The login username.</value>
/// <inheritdoc />
public string Username { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the world into which the bot should enter.
/// </summary>
/// <value>The world to enter.</value>
/// <inheritdoc />
public string World { get; set; } = string.Empty;
}

View File

@ -6,6 +6,7 @@ using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using Tomlyn.Extensions.Configuration;
using VPLink.Common.Services;
using VPLink.Services;
using VpSharp;
using X10D.Hosting.DependencyInjection;

View File

@ -2,7 +2,8 @@ using System.Reactive.Linq;
using System.Reactive.Subjects;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using VPLink.Configuration;
using VPLink.Common.Configuration;
using VPLink.Common.Services;
using VpSharp;
using VpSharp.Entities;
@ -50,7 +51,7 @@ internal sealed class AvatarService : BackgroundService, IAvatarService
{
_logger.LogInformation("{Avatar} joined", avatar);
BotConfiguration configuration = _configurationService.BotConfiguration;
IBotConfiguration configuration = _configurationService.BotConfiguration;
if (!configuration.AnnounceAvatarEvents || avatar.IsBot && !configuration.AnnounceBots)
return;
@ -61,7 +62,7 @@ internal sealed class AvatarService : BackgroundService, IAvatarService
{
_logger.LogInformation("{Avatar} left", avatar);
BotConfiguration configuration = _configurationService.BotConfiguration;
IBotConfiguration configuration = _configurationService.BotConfiguration;
if (!configuration.AnnounceAvatarEvents || avatar.IsBot && !configuration.AnnounceBots)
return;

View File

@ -1,4 +1,6 @@
using Microsoft.Extensions.Configuration;
using VPLink.Common.Configuration;
using VPLink.Common.Services;
using VPLink.Configuration;
namespace VPLink.Services;
@ -18,19 +20,19 @@ internal sealed class ConfigurationService : IConfigurationService
}
/// <inheritdoc />
public BotConfiguration BotConfiguration
public IBotConfiguration BotConfiguration
{
get => _configuration.GetSection("Bot").Get<BotConfiguration>() ?? new BotConfiguration();
}
/// <inheritdoc />
public DiscordConfiguration DiscordConfiguration
public IDiscordConfiguration DiscordConfiguration
{
get => _configuration.GetSection("Discord").Get<DiscordConfiguration>() ?? new DiscordConfiguration();
}
/// <inheritdoc />
public VirtualParadiseConfiguration VirtualParadiseConfiguration
public IVirtualParadiseConfiguration VirtualParadiseConfiguration
{
get => _configuration.GetSection("VirtualParadise").Get<VirtualParadiseConfiguration>() ??
new VirtualParadiseConfiguration();

View File

@ -8,9 +8,11 @@ using Discord;
using Discord.WebSocket;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using VPLink.Configuration;
using VPLink.Data;
using VPLink.Common.Configuration;
using VPLink.Common.Data;
using VPLink.Common.Services;
using VpSharp.Entities;
using IUser = Discord.IUser;
namespace VPLink.Services;
@ -144,7 +146,7 @@ internal sealed partial class DiscordMessageService : BackgroundService, IDiscor
private bool TryGetRelayChannel([NotNullWhen(true)] out ITextChannel? channel)
{
DiscordConfiguration configuration = _configurationService.DiscordConfiguration;
IDiscordConfiguration configuration = _configurationService.DiscordConfiguration;
ulong channelId = configuration.ChannelId;
if (_discordClient.GetChannel(channelId) is ITextChannel textChannel)

View File

@ -4,7 +4,8 @@ using Discord.WebSocket;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using VPLink.Commands;
using VPLink.Configuration;
using VPLink.Common.Configuration;
using VPLink.Common.Services;
namespace VPLink.Services;
@ -48,7 +49,7 @@ internal sealed class DiscordService : BackgroundService
_discordClient.Ready += OnReady;
_discordClient.InteractionCreated += OnInteractionCreated;
DiscordConfiguration configuration = _configurationService.DiscordConfiguration;
IDiscordConfiguration configuration = _configurationService.DiscordConfiguration;
string token = configuration.Token ?? throw new InvalidOperationException("Token is not set.");
_logger.LogDebug("Connecting to Discord");

View File

@ -1,5 +1,6 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using VPLink.Common.Services;
using VpSharp.Extensions;
namespace VPLink.Services;

View File

@ -3,15 +3,16 @@ using System.Reactive.Linq;
using System.Reactive.Subjects;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using VPLink.Configuration;
using VPLink.Data;
using VPLink.Common.Configuration;
using VPLink.Common.Data;
using VPLink.Common.Services;
using VpSharp;
using VpSharp.Entities;
using FontStyle = VpSharp.FontStyle;
namespace VPLink.Services;
/// <inheritdoc cref="VPLink.Services.IVirtualParadiseMessageService" />
/// <inheritdoc cref="IVirtualParadiseMessageService" />
internal sealed class VirtualParadiseMessageService : BackgroundService, IVirtualParadiseMessageService
{
private readonly ILogger<VirtualParadiseMessageService> _logger;
@ -40,7 +41,7 @@ internal sealed class VirtualParadiseMessageService : BackgroundService, IVirtua
/// <inheritdoc />
public Task SendMessageAsync(RelayedMessage message)
{
ChatConfiguration configuration = _configurationService.VirtualParadiseConfiguration.Chat;
IChatConfiguration configuration = _configurationService.VirtualParadiseConfiguration.Chat;
Color color = Color.FromArgb((int)configuration.Color);
FontStyle style = configuration.Style;

View File

@ -1,9 +1,10 @@
using System.Reactive.Subjects;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using VPLink.Common.Configuration;
using VPLink.Common.Services;
using VpSharp;
using VpSharp.Entities;
using VirtualParadiseConfiguration = VPLink.Configuration.VirtualParadiseConfiguration;
namespace VPLink.Services;
@ -35,7 +36,7 @@ internal sealed class VirtualParadiseService : BackgroundService
_logger.LogInformation("Establishing relay");
_virtualParadiseClient.MessageReceived.Subscribe(_messageReceived);
VirtualParadiseConfiguration configuration = _configurationService.VirtualParadiseConfiguration;
IVirtualParadiseConfiguration configuration = _configurationService.VirtualParadiseConfiguration;
string username = configuration.Username ?? throw new InvalidOperationException("Username is not set.");
string password = configuration.Password ?? throw new InvalidOperationException("Password is not set.");

View File

@ -55,4 +55,8 @@
<PackageReference Include="ZString" Version="2.5.0"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VPLink.Common\VPLink.Common.csproj" />
</ItemGroup>
</Project>