littleshop/LittleShop.Tests/TestUtilities/TestDataBuilder.cs
sysadmin a281bb2896 Implement complete e-commerce functionality with shipping and order management
Features Added:
- Standard e-commerce properties (Price, Weight, shipping fields)
- Order management with Create/Edit views and shipping information
- ShippingRates system for weight-based shipping calculations
- Comprehensive test coverage with JWT authentication tests
- Sample data seeder with 5 orders demonstrating full workflow
- Photo upload functionality for products
- Multi-cryptocurrency payment support (BTC, XMR, USDT, etc.)

Database Changes:
- Added ShippingRates table
- Added shipping fields to Orders (Name, Address, City, PostCode, Country)
- Renamed properties to standard names (BasePrice to Price, ProductWeight to Weight)
- Added UpdatedAt timestamps to models

UI Improvements:
- Added Create/Edit views for Orders
- Added ShippingRates management UI
- Updated navigation menu with Shipping option
- Enhanced Order Details view with shipping information

Sample Data:
- 3 Categories (Electronics, Clothing, Books)
- 5 Products with various prices
- 5 Shipping rates (Royal Mail options)
- 5 Orders in different statuses (Pending to Delivered)
- 3 Crypto payments demonstrating payment flow

Security:
- All API endpoints secured with JWT authentication
- No public endpoints - client apps must authenticate
- Privacy-focused design with minimal data collection

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-20 17:37:24 +01:00

295 lines
9.1 KiB
C#

using LittleShop.DTOs;
using LittleShop.Enums;
using LittleShop.Models;
using System;
using System.Collections.Generic;
namespace LittleShop.Tests.TestUtilities;
public static class TestDataBuilder
{
public static Category CreateCategory(string? name = null, bool isActive = true)
{
return new Category
{
Id = Guid.NewGuid(),
Name = name ?? $"Category-{Guid.NewGuid()}",
Description = "Test category description",
IsActive = isActive,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
};
}
public static Product CreateProduct(Guid? categoryId = null, string? name = null, decimal? price = null, bool isActive = true)
{
return new Product
{
Id = Guid.NewGuid(),
Name = name ?? $"Product-{Guid.NewGuid()}",
Description = "Test product description",
Price = price ?? 99.99m,
CategoryId = categoryId ?? Guid.NewGuid(),
IsActive = isActive,
Weight = 1.5m,
WeightUnit = ProductWeightUnit.Kilograms,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
};
}
public static Order CreateOrder(string? identityReference = null, OrderStatus status = OrderStatus.PendingPayment)
{
var reference = identityReference ?? $"identity-{Guid.NewGuid()}";
return new Order
{
Id = Guid.NewGuid(),
IdentityReference = reference,
Status = status,
ShippingName = "Test Customer",
ShippingAddress = "123 Test Street",
ShippingCity = "Test City",
ShippingPostCode = "TE5 7CD",
ShippingCountry = "United Kingdom",
TotalAmount = 199.99m,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow,
Items = new List<OrderItem>()
};
}
public static OrderItem CreateOrderItem(Guid orderId, Guid productId, int quantity = 1, decimal price = 99.99m)
{
return new OrderItem
{
Id = Guid.NewGuid(),
OrderId = orderId,
ProductId = productId,
Quantity = quantity,
UnitPrice = price,
TotalPrice = price * quantity
};
}
public static User CreateUser(string? username = null, string role = "User")
{
var user = username ?? $"user-{Guid.NewGuid()}";
return new User
{
Id = Guid.NewGuid(),
Username = user,
Email = $"{user}@test.com",
PasswordHash = "hashed-password",
Role = role,
IsActive = true,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
};
}
public static CryptoPayment CreateCryptoPayment(Guid orderId, CryptoCurrency currency = CryptoCurrency.BTC, PaymentStatus status = PaymentStatus.Pending)
{
return new CryptoPayment
{
Id = Guid.NewGuid(),
OrderId = orderId,
Currency = currency,
Amount = 0.0025m,
CryptoAddress = $"bc1q{Guid.NewGuid().ToString().Replace("-", "").Substring(0, 39)}",
Status = status,
ExchangeRate = 40000.00m,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
};
}
public static CreateOrderDto CreateOrderDto(List<Guid>? productIds = null)
{
var items = new List<CreateOrderItemDto>();
if (productIds != null)
{
foreach (var productId in productIds)
{
items.Add(new CreateOrderItemDto
{
ProductId = productId,
Quantity = 1
});
}
}
else
{
items.Add(new CreateOrderItemDto
{
ProductId = Guid.NewGuid(),
Quantity = 1
});
}
return new CreateOrderDto
{
IdentityReference = $"test-identity-{Guid.NewGuid()}",
ShippingName = "Test Customer",
ShippingAddress = "123 Test Street",
ShippingCity = "Test City",
ShippingPostCode = "TE5 7CD",
ShippingCountry = "United Kingdom",
Items = items
};
}
public static CreateProductDto CreateProductDto(Guid? categoryId = null)
{
return new CreateProductDto
{
Name = $"Product-{Guid.NewGuid()}",
Description = "Test product description",
Price = 99.99m,
CategoryId = categoryId ?? Guid.NewGuid(),
Weight = 1.5m,
WeightUnit = ProductWeightUnit.Kilograms
};
}
public static CreateCategoryDto CreateCategoryDto()
{
return new CreateCategoryDto
{
Name = $"Category-{Guid.NewGuid()}",
Description = "Test category description"
};
}
public static ProductPhoto CreateProductPhoto(Guid productId, int displayOrder = 1)
{
return new ProductPhoto
{
Id = Guid.NewGuid(),
ProductId = productId,
PhotoUrl = $"/uploads/products/{Guid.NewGuid()}.jpg",
AltText = "Test product photo",
DisplayOrder = displayOrder,
CreatedAt = DateTime.UtcNow
};
}
// Builder class for complex test scenarios
public class OrderBuilder
{
private Order _order;
private List<Product> _products = new();
public OrderBuilder()
{
_order = CreateOrder();
}
public OrderBuilder WithStatus(OrderStatus status)
{
_order.Status = status;
return this;
}
public OrderBuilder WithIdentity(string identityReference)
{
_order.IdentityReference = identityReference;
return this;
}
public OrderBuilder WithShipping(string name, string address, string city, string postCode, string country)
{
_order.ShippingName = name;
_order.ShippingAddress = address;
_order.ShippingCity = city;
_order.ShippingPostCode = postCode;
_order.ShippingCountry = country;
return this;
}
public OrderBuilder AddItem(Product product, int quantity)
{
_products.Add(product);
var item = CreateOrderItem(_order.Id, product.Id, quantity, product.Price);
_order.Items.Add(item);
_order.TotalAmount = _order.Items.Sum(i => i.TotalPrice);
return this;
}
public OrderBuilder WithPayment(CryptoCurrency currency, PaymentStatus status)
{
var payment = CreateCryptoPayment(_order.Id, currency, status);
_order.Payments ??= new List<CryptoPayment>();
_order.Payments.Add(payment);
return this;
}
public Order Build()
{
return _order;
}
public (Order order, List<Product> products) BuildWithProducts()
{
return (_order, _products);
}
}
// Bulk data generation for stress testing
public static class BulkDataGenerator
{
public static List<Category> GenerateCategories(int count)
{
var categories = new List<Category>();
for (int i = 0; i < count; i++)
{
categories.Add(CreateCategory($"Category {i + 1}"));
}
return categories;
}
public static List<Product> GenerateProducts(List<Category> categories, int productsPerCategory)
{
var products = new List<Product>();
foreach (var category in categories)
{
for (int i = 0; i < productsPerCategory; i++)
{
products.Add(CreateProduct(
category.Id,
$"{category.Name} - Product {i + 1}",
Random.Shared.Next(10, 1000)));
}
}
return products;
}
public static List<Order> GenerateOrders(int count, List<Product> availableProducts)
{
var orders = new List<Order>();
var statuses = Enum.GetValues<OrderStatus>();
for (int i = 0; i < count; i++)
{
var builder = new OrderBuilder()
.WithIdentity($"customer-{i}")
.WithStatus(statuses[Random.Shared.Next(statuses.Length)]);
// Add random products to order
var productCount = Random.Shared.Next(1, Math.Min(5, availableProducts.Count));
var selectedProducts = availableProducts
.OrderBy(x => Random.Shared.Next())
.Take(productCount);
foreach (var product in selectedProducts)
{
builder.AddItem(product, Random.Shared.Next(1, 3));
}
orders.Add(builder.Build());
}
return orders;
}
}
}