Files
ersatztv/ErsatzTV.Tests/Controllers/AuthControllerTests.cs
T

118 lines
4.3 KiB
C#

using System.Collections.Generic;
using System.Security.Claims;
using ErsatzTV.Application.Auth;
using ErsatzTV.Controllers.Api;
using ErsatzTV.Controllers.Api.Requests;
using ErsatzTV.Services;
using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using NSubstitute;
using NUnit.Framework;
using Shouldly;
namespace ErsatzTV.Tests.Controllers;
[TestFixture]
public class AuthControllerTests
{
private static IConfiguration Config(bool envSeed) =>
new ConfigurationBuilder()
.AddInMemoryCollection(
envSeed
? new Dictionary<string, string?> { ["Auth:LocalAdmin:Password"] = "seed-password" }
: new Dictionary<string, string?>())
.Build();
private static IApiKeyProvider ApiKeyProvider(string key = "the-machine-key")
{
var provider = Substitute.For<IApiKeyProvider>();
provider.ApiKey.Returns(key);
return provider;
}
[Test]
public async Task Config_Reports_Setup_Not_Required_When_Env_Seed_Configured()
{
var mediator = Substitute.For<IMediator>();
mediator.Send(Arg.Any<IsLocalAdminConfigured>(), Arg.Any<CancellationToken>()).Returns(false);
var controller = new AuthController(mediator, Config(envSeed: true), ApiKeyProvider());
var result = await controller.Config(CancellationToken.None) as OkObjectResult;
var body = result!.Value.ShouldBeOfType<AuthConfigResponse>();
// Env seed owns the credential → the SPA must not offer the browser setup-claim.
body.SetupRequired.ShouldBeFalse();
}
[Test]
public async Task Config_Reports_Setup_Required_When_Unconfigured_And_No_Env_Seed()
{
var mediator = Substitute.For<IMediator>();
mediator.Send(Arg.Any<IsLocalAdminConfigured>(), Arg.Any<CancellationToken>()).Returns(false);
var controller = new AuthController(mediator, Config(envSeed: false), ApiKeyProvider());
var result = await controller.Config(CancellationToken.None) as OkObjectResult;
var body = result!.Value.ShouldBeOfType<AuthConfigResponse>();
body.SetupRequired.ShouldBeTrue();
}
[Test]
public async Task Setup_Is_Closed_With_409_When_Env_Seed_Configured()
{
var mediator = Substitute.For<IMediator>();
var controller = new AuthController(mediator, Config(envSeed: true), ApiKeyProvider())
{
ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() }
};
var result = await controller.Setup(new SetupRequest("admin", "hunter2pw"), CancellationToken.None);
var problem = result.ShouldBeOfType<ConflictObjectResult>();
problem.StatusCode.ShouldBe(StatusCodes.Status409Conflict);
// The claim must never be attempted while the env seed owns the credential.
await mediator.DidNotReceive().Send(Arg.Any<ClaimLocalAdmin>(), Arg.Any<CancellationToken>());
}
[Test]
public void MachineKey_Returns_401_When_Anonymous()
{
var mediator = Substitute.For<IMediator>();
var controller = new AuthController(mediator, Config(envSeed: false), ApiKeyProvider())
{
ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() }
};
IActionResult result = controller.MachineKey();
var unauthorized = result.ShouldBeOfType<UnauthorizedObjectResult>();
unauthorized.Value.ShouldBeOfType<ProblemDetails>().Status.ShouldBe(401);
}
[Test]
public void MachineKey_Returns_The_Key_For_An_Authenticated_Session()
{
var mediator = Substitute.For<IMediator>();
var controller = new AuthController(mediator, Config(envSeed: false), ApiKeyProvider("the-machine-key"))
{
ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext
{
User = new ClaimsPrincipal(
new ClaimsIdentity([new Claim(ClaimTypes.Name, "admin")], AuthConstants.CookieScheme))
}
}
};
IActionResult result = controller.MachineKey();
var ok = result.ShouldBeOfType<OkObjectResult>();
ok.Value.ShouldBeOfType<MachineKeyResponse>().ApiKey.ShouldBe("the-machine-key");
}
}