using System.Net; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Mvc.Testing; namespace CartWise.Web.Tests; public class HouseholdAccessControlTests : IClassFixture { private readonly CartWiseWebApplicationFactory _factory; public HouseholdAccessControlTests(CartWiseWebApplicationFactory factory) { _factory = factory; } [Fact] public async Task AnonymousUser_IsRedirectedToLoginWhenRequestingHousehold() { var client = _factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); var response = await client.GetAsync("/household"); Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); Assert.Contains("/Account/Login", response.Headers.Location?.ToString()); } [Fact] public async Task EachUser_OnlySeesTheirOwnHousehold() { var clientA = _factory.CreateClient(); var clientB = _factory.CreateClient(); await RegisterAsync(clientA, "alice2@example.com", "Alice"); await RegisterAsync(clientB, "bob2@example.com", "Bob"); await CreateHouseholdAsync(clientA, "Alice's Household"); await CreateHouseholdAsync(clientB, "Bob's Household"); var householdPageA = await clientA.GetStringAsync("/household"); var householdPageB = await clientB.GetStringAsync("/household"); HtmlAssert.Contains("Alice's Household", householdPageA); HtmlAssert.DoesNotContain("Bob's Household", householdPageA); HtmlAssert.Contains("Bob's Household", householdPageB); HtmlAssert.DoesNotContain("Alice's Household", householdPageB); } private static async Task RegisterAsync(HttpClient client, string email, string displayName) { var html = await client.GetStringAsync("/Account/Register"); var token = ExtractAntiForgeryToken(html); var form = new Dictionary { ["__RequestVerificationToken"] = token, ["DisplayName"] = displayName, ["Email"] = email, ["Password"] = "Sup3rSecret!23", ["ConfirmPassword"] = "Sup3rSecret!23" }; var response = await client.PostAsync("/Account/Register", new FormUrlEncodedContent(form)); response.EnsureSuccessStatusCode(); } private static async Task CreateHouseholdAsync(HttpClient client, string name) { var html = await client.GetStringAsync("/household/create"); var token = ExtractAntiForgeryToken(html); var form = new Dictionary { ["__RequestVerificationToken"] = token, ["Name"] = name }; var response = await client.PostAsync("/household/create", new FormUrlEncodedContent(form)); response.EnsureSuccessStatusCode(); } private static string ExtractAntiForgeryToken(string html) { var match = Regex.Match(html, "name=\"__RequestVerificationToken\"[^>]*value=\"([^\"]+)\""); return match.Success ? match.Groups[1].Value : throw new InvalidOperationException("Anti-forgery token not found."); } }