using System.Net; using System.Net.Http.Json; using System.Text.Json; using FluentAssertions; using LittleShop.DTOs; using LittleShop.Models; using LittleShop.Tests.Infrastructure; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; using Xunit; namespace LittleShop.Tests.Integration; public class OrdersWithVariantsTests : IClassFixture { private readonly HttpClient _client; private readonly TestWebApplicationFactory _factory; public OrdersWithVariantsTests(TestWebApplicationFactory factory) { _factory = factory; _client = _factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); } [Fact] public async Task CreateOrder_WithVariants_ShouldStoreVariantInfo() { // Arrange var product = await CreateProductWithVariants(); var orderRequest = new CreateOrderDto { IdentityReference = "test_user_" + Guid.NewGuid(), ShippingName = "John Doe", ShippingAddress = "123 Test St", ShippingCity = "Test City", ShippingPostCode = "TE1 1ST", ShippingCountry = "United Kingdom", Items = new List { new() { ProductId = product.Id, Quantity = 1, SelectedVariant = "Red" }, new() { ProductId = product.Id, Quantity = 2, SelectedVariant = "Blue" } } }; // Act var response = await _client.PostAsJsonAsync("/api/orders", orderRequest); // Assert response.StatusCode.Should().Be(HttpStatusCode.Created); var order = await response.Content.ReadFromJsonAsync(); order.Should().NotBeNull(); order!.Items.Should().HaveCount(2); order.Items.Should().Contain(i => i.SelectedVariant == "Red" && i.Quantity == 1); order.Items.Should().Contain(i => i.SelectedVariant == "Blue" && i.Quantity == 2); } [Fact] public async Task CreateOrder_WithMultiBuy_ShouldApplyMultiBuyPricing() { // Arrange var product = await CreateProductWithMultiBuys(); var multiBuy = product.MultiBuys.First(mb => mb.Quantity == 3); var orderRequest = new CreateOrderDto { IdentityReference = "test_user_" + Guid.NewGuid(), ShippingName = "Jane Doe", ShippingAddress = "456 Test Ave", ShippingCity = "Test Town", ShippingPostCode = "TT2 2ST", ShippingCountry = "United Kingdom", Items = new List { new() { ProductId = product.Id, ProductMultiBuyId = multiBuy.Id, Quantity = 3 } } }; // Act var response = await _client.PostAsJsonAsync("/api/orders", orderRequest); // Assert response.StatusCode.Should().Be(HttpStatusCode.Created); var order = await response.Content.ReadFromJsonAsync(); order.Should().NotBeNull(); order!.Items.Should().HaveCount(1); var item = order.Items.First(); item.Quantity.Should().Be(3); item.ProductMultiBuyId.Should().Be(multiBuy.Id); item.UnitPrice.Should().Be(multiBuy.PricePerUnit); item.TotalPrice.Should().Be(multiBuy.Price); } [Fact] public async Task CreateOrder_WithMultiBuyAndVariant_ShouldStoreBoth() { // Arrange var product = await CreateProductWithMultiBuysAndVariants(); var multiBuy = product.MultiBuys.First(mb => mb.Quantity == 2); var orderRequest = new CreateOrderDto { IdentityReference = "test_user_" + Guid.NewGuid(), ShippingName = "Bob Smith", ShippingAddress = "789 Test Rd", ShippingCity = "Testville", ShippingPostCode = "TV3 3ST", ShippingCountry = "United Kingdom", Items = new List { new() { ProductId = product.Id, ProductMultiBuyId = multiBuy.Id, Quantity = 2, SelectedVariant = "Vanilla" } } }; // Act var response = await _client.PostAsJsonAsync("/api/orders", orderRequest); // Assert response.StatusCode.Should().Be(HttpStatusCode.Created); var order = await response.Content.ReadFromJsonAsync(); order.Should().NotBeNull(); var item = order.Items.First(); item.ProductMultiBuyId.Should().Be(multiBuy.Id); item.SelectedVariant.Should().Be("Vanilla"); item.Quantity.Should().Be(2); } [Fact] public async Task CreateOrder_MixedItems_ShouldCalculateTotalCorrectly() { // Arrange var regularProduct = await CreateRegularProduct(); var variantProduct = await CreateProductWithVariants(); var multiBuyProduct = await CreateProductWithMultiBuys(); var multiBuy = multiBuyProduct.MultiBuys.First(mb => mb.Quantity == 3); var orderRequest = new CreateOrderDto { IdentityReference = "test_user_" + Guid.NewGuid(), ShippingName = "Alice Johnson", ShippingAddress = "321 Mixed St", ShippingCity = "Complex City", ShippingPostCode = "CC4 4ST", ShippingCountry = "United Kingdom", Items = new List { // Regular product new() { ProductId = regularProduct.Id, Quantity = 2 }, // Product with variant new() { ProductId = variantProduct.Id, Quantity = 1, SelectedVariant = "Blue" }, // Product with multi-buy new() { ProductId = multiBuyProduct.Id, ProductMultiBuyId = multiBuy.Id, Quantity = 3 } } }; // Act var response = await _client.PostAsJsonAsync("/api/orders", orderRequest); // Assert response.StatusCode.Should().Be(HttpStatusCode.Created); var order = await response.Content.ReadFromJsonAsync(); order.Should().NotBeNull(); order!.Items.Should().HaveCount(3); // Verify total calculation var expectedTotal = (regularProduct.Price * 2) + variantProduct.Price + multiBuy.Price; order.TotalAmount.Should().Be(expectedTotal); } [Fact] public async Task GetOrder_ShouldIncludeVariantAndMultiBuyInfo() { // Arrange var product = await CreateProductWithMultiBuysAndVariants(); var multiBuy = product.MultiBuys.First(); var orderRequest = new CreateOrderDto { IdentityReference = "test_user_" + Guid.NewGuid(), ShippingName = "Test User", ShippingAddress = "Test Address", ShippingCity = "Test City", ShippingPostCode = "TE1 1ST", ShippingCountry = "United Kingdom", Items = new List { new() { ProductId = product.Id, ProductMultiBuyId = multiBuy.Id, Quantity = multiBuy.Quantity, SelectedVariant = "Chocolate" } } }; var createResponse = await _client.PostAsJsonAsync("/api/orders", orderRequest); var createdOrder = await createResponse.Content.ReadFromJsonAsync(); // Act var getResponse = await _client.GetAsync($"/api/orders/by-identity/{orderRequest.IdentityReference}/{createdOrder!.Id}"); // Assert getResponse.StatusCode.Should().Be(HttpStatusCode.OK); var retrievedOrder = await getResponse.Content.ReadFromJsonAsync(); retrievedOrder.Should().NotBeNull(); var item = retrievedOrder!.Items.First(); item.ProductMultiBuyId.Should().Be(multiBuy.Id); item.SelectedVariant.Should().Be("Chocolate"); } private async Task CreateRegularProduct() { var category = await CreateTestCategory(); var productDto = new CreateProductDto { Name = "Regular Product", Description = "A regular product without variants", Price = 15.00m, CategoryId = category.Id, Weight = 1.0m, WeightUnit = LittleShop.Enums.ProductWeightUnit.Kilograms }; var response = await _client.PostAsJsonAsync("/api/admin/products", productDto); response.EnsureSuccessStatusCode(); return (await response.Content.ReadFromJsonAsync())!; } private async Task CreateProductWithVariants() { var product = await CreateRegularProduct(); // Add variants var variants = new[] { "Red", "Blue", "Green" }; foreach (var variantName in variants) { var variantDto = new CreateProductVariantDto { ProductId = product.Id, Name = variantName, VariantType = "Color", StockLevel = 100 }; await _client.PostAsJsonAsync($"/api/admin/products/{product.Id}/variants", variantDto); } // Refresh product to get variants var response = await _client.GetAsync($"/api/catalog/products/{product.Id}"); return (await response.Content.ReadFromJsonAsync())!; } private async Task CreateProductWithMultiBuys() { var product = await CreateRegularProduct(); // Add multi-buys var multiBuys = new[] { new CreateProductMultiBuyDto { ProductId = product.Id, Name = "Twin Pack", Quantity = 2, Price = 25.00m }, new CreateProductMultiBuyDto { ProductId = product.Id, Name = "Triple Pack", Quantity = 3, Price = 35.00m } }; foreach (var multiBuyDto in multiBuys) { await _client.PostAsJsonAsync($"/api/admin/products/{product.Id}/multibuys", multiBuyDto); } // Refresh product to get multi-buys var response = await _client.GetAsync($"/api/catalog/products/{product.Id}"); return (await response.Content.ReadFromJsonAsync())!; } private async Task CreateProductWithMultiBuysAndVariants() { var product = await CreateProductWithMultiBuys(); // Add variants var variants = new[] { "Vanilla", "Chocolate", "Strawberry" }; foreach (var variantName in variants) { var variantDto = new CreateProductVariantDto { ProductId = product.Id, Name = variantName, VariantType = "Flavor", StockLevel = 50 }; await _client.PostAsJsonAsync($"/api/admin/products/{product.Id}/variants", variantDto); } // Refresh product var response = await _client.GetAsync($"/api/catalog/products/{product.Id}"); return (await response.Content.ReadFromJsonAsync())!; } private async Task CreateTestCategory() { var categoryDto = new CreateCategoryDto { Name = "Test Category " + Guid.NewGuid(), Description = "Test category for integration tests" }; var response = await _client.PostAsJsonAsync("/api/admin/categories", categoryDto); response.EnsureSuccessStatusCode(); return (await response.Content.ReadFromJsonAsync())!; } }