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>
295 lines
9.1 KiB
C#
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;
|
|
}
|
|
}
|
|
} |