using System.Net; using Microsoft.AspNetCore.Mvc.Testing; namespace CartWise.Web.Tests; public class PurchaseAccessControlTests : IClassFixture { private readonly CartWiseWebApplicationFactory _factory; public PurchaseAccessControlTests(CartWiseWebApplicationFactory factory) { _factory = factory; } [Fact] public async Task AnonymousUser_IsRedirectedToLoginWhenRequestingPurchases() { var client = _factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); var response = await client.GetAsync("/purchases"); Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); Assert.Contains("/Account/Login", response.Headers.Location?.ToString()); } [Fact] public async Task RecordPurchase_FullFlow_AddsItemAndCompletes() { var client = _factory.CreateClient(); await WebTestHelpers.RegisterAsync(client, "purchaseflow1@example.com", "Purchase Flow User"); await WebTestHelpers.CreateHouseholdAsync(client, "Purchase Flow Household"); var indexHtml = await client.GetStringAsync("/purchases"); var startToken = WebTestHelpers.ExtractAntiForgeryToken(indexHtml); var startResponse = await client.PostAsync("/purchases/start", new FormUrlEncodedContent(new Dictionary { ["__RequestVerificationToken"] = startToken })); startResponse.EnsureSuccessStatusCode(); var purchaseUrl = startResponse.RequestMessage!.RequestUri!.AbsolutePath; var detailHtml = await client.GetStringAsync(purchaseUrl); HtmlAssert.Contains("In progress", detailHtml); var itemToken = WebTestHelpers.ExtractAntiForgeryToken(detailHtml); var addItemResponse = await client.PostAsync($"{purchaseUrl}/items", new FormUrlEncodedContent(new Dictionary { ["__RequestVerificationToken"] = itemToken, ["NewItem.Description"] = "Milk", ["NewItem.Quantity"] = "2", ["NewItem.Unit"] = "gal", ["NewItem.LinePrice"] = "7.98" })); addItemResponse.EnsureSuccessStatusCode(); var afterAddHtml = await addItemResponse.Content.ReadAsStringAsync(); HtmlAssert.Contains("Milk", afterAddHtml); var completeToken = WebTestHelpers.ExtractAntiForgeryToken(afterAddHtml); var completeResponse = await client.PostAsync($"{purchaseUrl}/complete", new FormUrlEncodedContent(new Dictionary { ["__RequestVerificationToken"] = completeToken })); completeResponse.EnsureSuccessStatusCode(); var finalHtml = await completeResponse.Content.ReadAsStringAsync(); HtmlAssert.Contains("Completed", finalHtml); HtmlAssert.Contains("$7.98", finalHtml); } [Fact] public async Task EachHousehold_OnlySeesItsOwnPurchases() { var clientA = _factory.CreateClient(); var clientB = _factory.CreateClient(); await WebTestHelpers.RegisterAsync(clientA, "purchasealice@example.com", "Purchase Alice"); await WebTestHelpers.RegisterAsync(clientB, "purchasebob@example.com", "Purchase Bob"); await WebTestHelpers.CreateHouseholdAsync(clientA, "Purchase Household A"); await WebTestHelpers.CreateHouseholdAsync(clientB, "Purchase Household B"); var indexA = await clientA.GetStringAsync("/purchases"); var tokenA = WebTestHelpers.ExtractAntiForgeryToken(indexA); var startResponseA = await clientA.PostAsync("/purchases/start", new FormUrlEncodedContent(new Dictionary { ["__RequestVerificationToken"] = tokenA })); var purchaseUrlA = startResponseA.RequestMessage!.RequestUri!.AbsolutePath; var purchaseIdA = purchaseUrlA.Split('/').Last(); var response = await clientB.GetAsync($"/purchases/{purchaseIdA}"); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } }