Files
TinfoilVibeServer/TinfoilVibeServerTest/Tests/BasicAuthMiddlewareTests.cs
T
ecenshu a1ea34bc01
Build & Push Docker image / build-and-push (push) Has been cancelled
ci / build_linux (push) Has been cancelled
feature/ci (#1)
Consolidate data and config into separate folders that will be expected to be mapped in the container

Reviewed-on: #1
Co-authored-by: Huy Nguyen <ecenshu@gmail.com>
Co-committed-by: Huy Nguyen <ecenshu@gmail.com>
2025-11-13 09:11:21 +00:00

117 lines
3.7 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Moq;
using NUnit.Framework;
using TinfoilVibeServer.Authentication;
using TinfoilVibeServer.Middleware;
namespace TinfoilVibeServerTest.Tests
{
[TestFixture]
public class BasicAuthMiddlewareTests
{
private Mock<ILogger<BasicAuthMiddleware>> _loggerMock;
private Mock<IAuthStore> _authMock;
private BasicAuthMiddleware _middleware;
private RequestDelegate _next;
[SetUp]
public void SetUp()
{
_loggerMock = new Mock<ILogger<BasicAuthMiddleware>>();
_authMock = new Mock<IAuthStore>();
_next = (HttpContext ctx) => Task.CompletedTask;
_middleware = new BasicAuthMiddleware(_next);
}
private HttpContext CreateContext(string authHeader = "", string ip = "127.0.0.1", string uid = "")
{
var ctx = new DefaultHttpContext();
ctx.Connection.RemoteIpAddress = IPAddress.Parse(ip);
if (!string.IsNullOrEmpty(authHeader))
{
ctx.Request.Headers["Authorization"] = authHeader;
}
if (uid!= null)
{
ctx.Request.Headers["UID"] = uid.ToString();
}
return ctx;
}
[Test]
public async Task InvokeAsync_NoAuthHeader_ShouldReturn401()
{
// Arrange
var ctx = CreateContext();
ctx.Request.Path = new PathString("/");
// Act
await _middleware.InvokeAsync(ctx, _authMock.Object, _loggerMock.Object);
// Assert
Assert.That(ctx.Response.StatusCode, Is.EqualTo(StatusCodes.Status401Unauthorized));
_loggerMock.Verify(l => l.Log(
LogLevel.Warning,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString().Contains("Missing Authorization header")),
null,
It.IsAny<Func<It.IsAnyType, Exception, string>>()), Times.Once);
}
[Test]
public async Task InvokeAsync_BlacklistedIP_ShouldReturn403()
{
// Arrange
var ctx = CreateContext("Basic dXNlcjpwYXNz");
ctx.Request.Path = new PathString("/");
_authMock.Setup(a => a.IsIPBlacklisted("127.0.0.1")).Returns(true);
// Act
await _middleware.InvokeAsync(ctx, _authMock.Object, _loggerMock.Object);
// Assert
Assert.That(ctx.Response.StatusCode, Is.EqualTo(StatusCodes.Status403Forbidden));
}
[Test]
public async Task InvokeAsync_ValidCredentials_ShouldCallNext()
{
// Arrange
var user = "alice";
var pw = "secret";
var uid = "1234";
var header = $"Basic {Convert.ToBase64String(Encoding.ASCII.GetBytes($"{user}:{pw}"))}";
var ip = "127.0.0.1";
var ctx = CreateContext(header,ip, uid);
string? error;
_authMock.Setup(a =>
a.TryValidate(user, pw, uid, ip, out error))
.Returns(true);
bool nextCalled = false;
_next = (HttpContext _) => { nextCalled = true; return Task.CompletedTask; };
_middleware = new BasicAuthMiddleware(_next);
// Act
await _middleware.InvokeAsync(ctx, _authMock.Object, _loggerMock.Object);
// Assert
Assert.That(nextCalled, Is.True);
Assert.That(ctx.Response.StatusCode, Is.EqualTo(StatusCodes.Status200OK));
}
}
}