Skip to content

Commit

Permalink
Refactor options validators and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
austins committed Sep 8, 2023
1 parent 04074cf commit bf47241
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using DiscordTranslationBot.Configuration;
using FluentValidation.TestHelper;

namespace DiscordTranslationBot.Tests.Configuration;

public sealed class DiscordOptionsValidatorTests
{
private readonly DiscordOptionsValidator _sut = new();

[Fact]
public void Valid_Options_ValidatesWithoutErrors()
{
// Arrange
var options = new DiscordOptions { BotToken = "token" };

// Act
var result = _sut.TestValidate(options);

// Assert
result.ShouldNotHaveAnyValidationErrors();
}

[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void Invalid_BotToken_HasValidationErrors(string? botToken)
{
// Arrange
var options = new DiscordOptions { BotToken = botToken! };

// Act
var result = _sut.TestValidate(options);

// Assert
result.ShouldHaveValidationErrorFor(x => x.BotToken).Only();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using DiscordTranslationBot.Configuration.TranslationProviders;
using FluentValidation.TestHelper;

namespace DiscordTranslationBot.Tests.Configuration.TranslationProviders;

public sealed class TranslationProvidersOptionsValidatorTests
{
private readonly TranslationProvidersOptionsValidator _sut = new();

[Fact]
public void Valid_Options_ValidatesWithoutErrors()
{
// Arrange
var options = new TranslationProvidersOptions
{
AzureTranslator = new AzureTranslatorOptions
{
Enabled = true,
ApiUrl = new Uri("http://localhost", UriKind.Absolute),
Region = "westus",
SecretKey = "secret"
},
LibreTranslate = new LibreTranslateOptions { Enabled = false }
};

// Act
var result = _sut.TestValidate(options);

// Assert
result.ShouldNotHaveAnyValidationErrors();
}

[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void Invalid_ProviderOptions_HasValidationErrors(string? stringValue)
{
// Arrange
var options = new TranslationProvidersOptions
{
AzureTranslator = new AzureTranslatorOptions
{
Enabled = true,
ApiUrl = null,
Region = stringValue,
SecretKey = stringValue
},
LibreTranslate = new LibreTranslateOptions { Enabled = true, ApiUrl = null }
};

// Act
var result = _sut.TestValidate(options);

// Assert
result.ShouldHaveValidationErrorFor(x => x.AzureTranslator.ApiUrl);
result.ShouldHaveValidationErrorFor(x => x.AzureTranslator.Region);
result.ShouldHaveValidationErrorFor(x => x.AzureTranslator.SecretKey);
result.ShouldHaveValidationErrorFor(x => x.LibreTranslate.ApiUrl);
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
namespace DiscordTranslationBot.Configuration.TranslationProviders;
using FluentValidation;

namespace DiscordTranslationBot.Configuration.TranslationProviders;

/// <summary>
/// Options for the Azure Translator provider.
/// </summary>
public sealed class AzureTranslatorOptions : TranslationProviderOptionsBase
{
/// <summary>
/// The API URL for Azure Translator.
/// </summary>
public Uri? ApiUrl { get; init; }

/// <summary>
/// The secret key for the Azure Translator API.
/// </summary>
Expand All @@ -20,3 +17,26 @@ public sealed class AzureTranslatorOptions : TranslationProviderOptionsBase
/// </summary>
public string? Region { get; init; }
}

/// <summary>
/// Validator for <see cref="AzureTranslatorOptions" />.
/// </summary>
public sealed class AzureTranslatorOptionsValidator : AbstractValidator<AzureTranslatorOptions>
{
/// <summary>
/// Initializes validation rules.
/// </summary>
public AzureTranslatorOptionsValidator()
{
Include(new TranslationProviderOptionsBaseValidator());

When(
x => x.Enabled,
() =>
{
RuleFor(x => x.SecretKey).NotEmpty();
RuleFor(x => x.Region).NotEmpty();
}
);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
namespace DiscordTranslationBot.Configuration.TranslationProviders;
using FluentValidation;

namespace DiscordTranslationBot.Configuration.TranslationProviders;

/// <summary>
/// Options for the LibreTranslate provider.
/// </summary>
public sealed class LibreTranslateOptions : TranslationProviderOptionsBase
public sealed class LibreTranslateOptions : TranslationProviderOptionsBase { }

/// <summary>
/// Validator for <see cref="LibreTranslateOptions" />.
/// </summary>
public sealed class LibreTranslateOptionsValidator : AbstractValidator<LibreTranslateOptions>
{
/// <summary>
/// The API URL for LibreTranslate.
/// Initializes validation rules.
/// </summary>
public Uri? ApiUrl { get; init; }
public LibreTranslateOptionsValidator()
{
Include(new TranslationProviderOptionsBaseValidator());
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace DiscordTranslationBot.Configuration.TranslationProviders;
using FluentValidation;

namespace DiscordTranslationBot.Configuration.TranslationProviders;

/// <summary>
/// Base class for a translation provider's options.
Expand All @@ -9,4 +11,23 @@ public abstract class TranslationProviderOptionsBase
/// Flag indicating whether this provider is enabled.
/// </summary>
public bool Enabled { get; init; }

/// <summary>
/// The API URL for Azure Translator.
/// </summary>
public Uri? ApiUrl { get; init; }
}

/// <summary>
/// Validator for <see cref="TranslationProviderOptionsBase" />.
/// </summary>
public sealed class TranslationProviderOptionsBaseValidator : AbstractValidator<TranslationProviderOptionsBase>
{
/// <summary>
/// Initializes validation rules.
/// </summary>
public TranslationProviderOptionsBaseValidator()
{
When(x => x.Enabled, () => RuleFor(x => x.ApiUrl).NotNull());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,11 @@ public sealed class TranslationProvidersOptions
public sealed class TranslationProvidersOptionsValidator : AbstractValidator<TranslationProvidersOptions>
{
/// <summary>
/// Initializes a new instance of the <see cref="TranslationProvidersOptionsValidator" /> class.
/// Initializes validation rules.
/// </summary>
public TranslationProvidersOptionsValidator()
{
// Validate Azure Translator options.
When(
x => x.AzureTranslator.Enabled,
() =>
{
RuleFor(x => x.AzureTranslator.ApiUrl).NotNull();
RuleFor(x => x.AzureTranslator.SecretKey).NotEmpty();
RuleFor(x => x.AzureTranslator.Region).NotEmpty();
}
);

// Validate Libre Translate options.
When(x => x.LibreTranslate.Enabled, () => RuleFor(x => x.LibreTranslate.ApiUrl).NotNull());
RuleFor(x => x.AzureTranslator).SetValidator(new AzureTranslatorOptionsValidator());
RuleFor(x => x.LibreTranslate).SetValidator(new LibreTranslateOptionsValidator());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ IConfigurationSection configurationSection
where TValidator : class, IValidator<TOptions>
{
services.AddTransient<IValidator<TOptions>, TValidator>();

services.AddOptions<TOptions>().Bind(configurationSection).ValidateWithFluentValidation().ValidateOnStart();
}

Expand Down

0 comments on commit bf47241

Please sign in to comment.