Skip to content

Commit

Permalink
Add interaction router with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
zobweyt committed Jun 10, 2024
1 parent 733a657 commit b3187fb
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 0 deletions.
66 changes: 66 additions & 0 deletions src/Giveaways/Services/InteractionRouter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using System.Linq;
using System.Reflection;
using System.Text;
using Discord.Interactions;
using Microsoft.Extensions.Options;

namespace Giveaways.Services;

/// <summary>
/// Represents a service for binding custom interactions to their corresponding custom IDs.
/// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="InteractionRouter"/> class.
/// </remarks>
/// <param name="options">The options containing the wild card expression.</param>
public class InteractionRouter(IOptions<InteractionServiceConfig> options)
{
private readonly string _wildCardExpression = options.Value.WildCardExpression;

/// <summary>
/// Binds a delegate to its corresponding custom ID by applying any provided arguments.
/// </summary>
/// <param name="func">The delegate to bind.</param>
/// <param name="args">The arguments to apply.</param>
/// <returns>The custom ID bound with the delegate and arguments applied.</returns>
public string Bind(Delegate func, params object[] args)
{
var pattern = GetCustomIdPattern(func);
var sb = new StringBuilder(pattern);

for (int argPos = 0; argPos < args.Length; argPos++)
{
int startIndex = sb.ToString().IndexOf(_wildCardExpression, StringComparison.Ordinal);
sb.Replace(_wildCardExpression, args[argPos].ToString(), startIndex, _wildCardExpression.Length);
}

return sb.ToString();
}

/// <summary>
/// Retrieves the custom ID pattern associated with a delegate.
/// </summary>
/// <param name="func">The delegate.</param>
/// <returns>The custom ID pattern associated with the delegate.</returns>
/// <exception cref="ArgumentException">
/// Thrown when the provided delegate doesn't have an interaction attribute.
/// </exception>
public static string GetCustomIdPattern(Delegate func)
{
foreach (Attribute attribute in func.Method.GetCustomAttributes())
{
switch (attribute)
{
case ModalInteractionAttribute modal:
return modal.CustomId;
case ComponentInteractionAttribute component:
return component.CustomId;
}
}

throw new ArgumentException("The provided delegate doesn't have an interaction attribute.", nameof(func));
}

public static string GetCustomIdPrefix(Delegate func) => GetCustomIdPattern(func).Split(":").First();
}
14 changes: 14 additions & 0 deletions test/Giveaways.Tests/MockedModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Threading.Tasks;
using Discord.Interactions;

namespace Giveaways.Tests;

/// <summary>
/// Represents a module for unit testing.
/// </summary>
internal sealed class MockedModule : ModuleBase
{
[ComponentInteraction("interaction:*:*")]
public Task InteractionAsync(string arg1, string arg2) => throw new NotImplementedException();
}
34 changes: 34 additions & 0 deletions test/Giveaways.Tests/Services/InteractionRouterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Bogus;
using Discord.Interactions;
using Giveaways.Services;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;

namespace Giveaways.Tests;

/// <summary>
/// Provides unit test cases for <see cref="InteractionRouter"/>.
/// </summary>
public class InteractionRouterTests : TestsBase
{
[Fact]
public void InteractionRouter_Binds_CustomID()
{
var optionsMock = new Mock<IOptions<InteractionServiceConfig>>();
optionsMock.Setup(x => x.Value).Returns(new InteractionServiceConfig());

var moduleMock = new MockedModule();
var func = moduleMock.InteractionAsync;

var arg1 = Faker.Lorem.Word();
var arg2 = Faker.Lorem.Word();

var routerMock = new Mock<InteractionRouter>(optionsMock.Object);
var id = routerMock.Object.Bind(func, arg1, arg2);

var prefix = InteractionRouter.GetCustomIdPrefix(func);

Assert.Equal($"{prefix}:{arg1}:{arg2}", id);
}
}

0 comments on commit b3187fb

Please sign in to comment.