using D3ROVoice.Api.Controllers; using D3ROVoice.Api.Dtos; using D3ROVoice.Api.Services; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Xunit; namespace D3ROVoice.Api.Tests; public sealed class AuthBootstrapControllerTests { private static readonly RegisterDto Request = new( "operator@example.test", "test-only-password-1234"); [Theory] [InlineData(null)] [InlineData("")] [InlineData("too-short")] public async Task RegisterIsDisabledWithoutAStrongConfiguredBootstrapToken(string? configuredToken) { var service = new RecordingAuthService(); var configuration = Configuration(configuredToken); var controller = new AuthController(service, configuration); var result = await controller.Register(Request, configuredToken); var unavailable = Assert.IsType(result); Assert.Equal(503, unavailable.StatusCode); Assert.Equal(0, service.RegisterCalls); } [Fact] public async Task RegisterRejectsAnIncorrectBootstrapTokenWithoutCreatingAUser() { var service = new RecordingAuthService(); var configuration = Configuration("test-bootstrap-token-0123456789-abcdef"); var controller = new AuthController(service, configuration); var result = await controller.Register( Request, "different-test-token-0123456789-abcdef"); Assert.IsType(result); Assert.Equal(0, service.RegisterCalls); } [Fact] public async Task RegisterAcceptsTheExactStrongBootstrapTokenOnce() { const string bootstrapToken = "test-bootstrap-token-0123456789-abcdef"; var service = new RecordingAuthService(); var controller = new AuthController(service, Configuration(bootstrapToken)); var result = await controller.Register(Request, bootstrapToken); Assert.IsType(result); Assert.Equal(1, service.RegisterCalls); } private static IConfiguration Configuration(string? bootstrapToken) { var values = new Dictionary(); if (bootstrapToken is not null) { values["ADMIN_BOOTSTRAP_TOKEN"] = bootstrapToken; } return new ConfigurationBuilder().AddInMemoryCollection(values).Build(); } private sealed class RecordingAuthService : IAuthService { public int RegisterCalls { get; private set; } public Task RegisterAsync(RegisterDto dto) { RegisterCalls += 1; return Task.FromResult(new AuthResponseDto( "test-token", dto.Email, "SuperAdmin", DateTime.UtcNow.AddMinutes(5))); } public Task LoginAsync(LoginDto dto) => throw new NotSupportedException(); public Task GetUserByEmailAsync(string email) => throw new NotSupportedException(); } }