using System.Net; using System.Net.Http.Headers; using FluentAssertions; using LittleShop.Tests.Infrastructure; using Microsoft.AspNetCore.Mvc.Testing; using Xunit; namespace LittleShop.Tests.Security; public class AuthenticationEnforcementTests : IClassFixture { private readonly HttpClient _client; private readonly TestWebApplicationFactory _factory; public AuthenticationEnforcementTests(TestWebApplicationFactory factory) { _factory = factory; _client = _factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); } [Theory] [InlineData("/api/catalog/categories")] [InlineData("/api/catalog/categories/00000000-0000-0000-0000-000000000001")] [InlineData("/api/catalog/products")] [InlineData("/api/catalog/products/00000000-0000-0000-0000-000000000001")] public async Task CatalogEndpoints_WithoutAuthentication_ShouldReturn401(string url) { // Act var response = await _client.GetAsync(url); // Assert response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } [Theory] [InlineData("/api/orders")] [InlineData("/api/orders/00000000-0000-0000-0000-000000000001")] public async Task AdminOrderEndpoints_WithoutAuthentication_ShouldReturn401(string url) { // Act var response = await _client.GetAsync(url); // Assert response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } [Theory] [InlineData("/api/orders/by-identity/test-identity")] [InlineData("/api/orders/by-identity/test-identity/00000000-0000-0000-0000-000000000001")] [InlineData("/api/orders/00000000-0000-0000-0000-000000000001/payments")] [InlineData("/api/orders/payments/00000000-0000-0000-0000-000000000001/status")] public async Task PublicOrderEndpoints_WithoutAuthentication_ShouldReturn401(string url) { // Act var response = await _client.GetAsync(url); // Assert response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } [Fact] public async Task PostOrder_WithoutAuthentication_ShouldReturn401() { // Act var response = await _client.PostAsync("/api/orders", null); // Assert response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } [Fact] public async Task PostPayment_WithoutAuthentication_ShouldReturn401() { // Act var response = await _client.PostAsync("/api/orders/00000000-0000-0000-0000-000000000001/payments", null); // Assert response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } [Fact] public async Task PaymentWebhook_WithoutAuthentication_ShouldReturn401() { // Act var response = await _client.PostAsync("/api/orders/payments/webhook", null); // Assert response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } [Theory] [InlineData("/api/catalog/categories")] [InlineData("/api/catalog/products")] public async Task CatalogEndpoints_WithValidJwtToken_ShouldReturn200(string url) { // Arrange var token = JwtTokenHelper.GenerateJwtToken(); _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); // Act var response = await _client.GetAsync(url); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); } [Theory] [InlineData("/api/catalog/categories")] [InlineData("/api/catalog/products")] public async Task CatalogEndpoints_WithExpiredJwtToken_ShouldReturn401(string url) { // Arrange var token = JwtTokenHelper.GenerateExpiredJwtToken(); _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); // Act var response = await _client.GetAsync(url); // Assert response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } [Theory] [InlineData("/api/catalog/categories")] [InlineData("/api/catalog/products")] public async Task CatalogEndpoints_WithInvalidJwtToken_ShouldReturn401(string url) { // Arrange var token = JwtTokenHelper.GenerateInvalidJwtToken(); _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); // Act var response = await _client.GetAsync(url); // Assert response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } [Theory] [InlineData("/api/catalog/categories")] [InlineData("/api/catalog/products")] public async Task CatalogEndpoints_WithMalformedToken_ShouldReturn401(string url) { // Arrange _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "not-a-valid-jwt-token"); // Act var response = await _client.GetAsync(url); // Assert response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } [Fact] public async Task AdminEndpoint_WithUserToken_ShouldReturnForbiddenOrUnauthorized() { // Arrange var token = JwtTokenHelper.GenerateJwtToken(role: "User"); _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); // Act var response = await _client.GetAsync("/api/orders"); // Assert response.StatusCode.Should().BeOneOf(HttpStatusCode.Unauthorized, HttpStatusCode.Forbidden); } [Fact] public async Task AdminEndpoint_WithAdminToken_ShouldReturn200() { // Arrange var token = JwtTokenHelper.GenerateAdminJwtToken(); _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); // Act var response = await _client.GetAsync("/api/orders"); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); } }