using FluentAssertions; using LittleShop.Data; using LittleShop.DTOs; using LittleShop.Models; using LittleShop.Services; using Microsoft.EntityFrameworkCore; using Moq; using AutoMapper; using Xunit; using LittleShop.Mapping; namespace LittleShop.Tests.Unit; public class CategoryServiceTests : IDisposable { private readonly LittleShopContext _context; private readonly ICategoryService _categoryService; public CategoryServiceTests() { // Set up in-memory database var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .Options; _context = new LittleShopContext(options); // Create service _categoryService = new CategoryService(_context); } [Fact] public async Task GetAllCategoriesAsync_ReturnsAllCategories() { // Arrange var categories = new[] { new Category { Id = Guid.NewGuid(), Name = "Category 1", Description = "Desc 1", IsActive = true }, new Category { Id = Guid.NewGuid(), Name = "Category 2", Description = "Desc 2", IsActive = true }, new Category { Id = Guid.NewGuid(), Name = "Category 3", Description = "Desc 3", IsActive = false } }; _context.Categories.AddRange(categories); await _context.SaveChangesAsync(); // Act var result = await _categoryService.GetAllCategoriesAsync(); // Assert result.Should().HaveCount(3); result.Should().Contain(c => c.Name == "Category 1"); result.Should().Contain(c => c.Name == "Category 2"); result.Should().Contain(c => c.Name == "Category 3"); } [Fact] public async Task GetCategoryByIdAsync_WithValidId_ReturnsCategory() { // Arrange var categoryId = Guid.NewGuid(); var category = new Category { Id = categoryId, Name = "Test Category", Description = "Test Description", IsActive = true }; _context.Categories.Add(category); await _context.SaveChangesAsync(); // Act var result = await _categoryService.GetCategoryByIdAsync(categoryId); // Assert result.Should().NotBeNull(); result!.Id.Should().Be(categoryId); result.Name.Should().Be("Test Category"); result.Description.Should().Be("Test Description"); } [Fact] public async Task GetCategoryByIdAsync_WithInvalidId_ReturnsNull() { // Arrange var invalidId = Guid.NewGuid(); // Act var result = await _categoryService.GetCategoryByIdAsync(invalidId); // Assert result.Should().BeNull(); } [Fact] public async Task CreateCategoryAsync_WithValidData_CreatesCategory() { // Arrange var createDto = new CreateCategoryDto { Name = "New Category", Description = "New Description" }; // Act var result = await _categoryService.CreateCategoryAsync(createDto); // Assert result.Should().NotBeNull(); result.Name.Should().Be("New Category"); result.Description.Should().Be("New Description"); result.IsActive.Should().BeTrue(); // Verify in database var dbCategory = await _context.Categories.FindAsync(result.Id); dbCategory.Should().NotBeNull(); dbCategory!.Name.Should().Be("New Category"); } [Fact] public async Task UpdateCategoryAsync_WithValidData_UpdatesCategory() { // Arrange var categoryId = Guid.NewGuid(); var category = new Category { Id = categoryId, Name = "Original Name", Description = "Original Description", IsActive = true }; _context.Categories.Add(category); await _context.SaveChangesAsync(); var updateDto = new UpdateCategoryDto { Name = "Updated Name", Description = "Updated Description", IsActive = false }; // Act var result = await _categoryService.UpdateCategoryAsync(categoryId, updateDto); // Assert result.Should().BeTrue(); // Verify in database var dbCategory = await _context.Categories.FindAsync(categoryId); dbCategory!.Name.Should().Be("Updated Name"); dbCategory.Description.Should().Be("Updated Description"); dbCategory.IsActive.Should().BeFalse(); } [Fact] public async Task UpdateCategoryAsync_WithInvalidId_ReturnsFalse() { // Arrange var invalidId = Guid.NewGuid(); var updateDto = new UpdateCategoryDto { Name = "Updated Name", Description = "Updated Description", IsActive = true }; // Act var result = await _categoryService.UpdateCategoryAsync(invalidId, updateDto); // Assert result.Should().BeFalse(); } [Fact] public async Task DeleteCategoryAsync_WithValidId_DeletesCategory() { // Arrange var categoryId = Guid.NewGuid(); var category = new Category { Id = categoryId, Name = "To Delete", Description = "Will be deleted", IsActive = true }; _context.Categories.Add(category); await _context.SaveChangesAsync(); // Act var result = await _categoryService.DeleteCategoryAsync(categoryId); // Assert result.Should().BeTrue(); // Verify in database var dbCategory = await _context.Categories.FindAsync(categoryId); dbCategory.Should().BeNull(); } [Fact] public async Task DeleteCategoryAsync_WithInvalidId_ReturnsFalse() { // Arrange var invalidId = Guid.NewGuid(); // Act var result = await _categoryService.DeleteCategoryAsync(invalidId); // Assert result.Should().BeFalse(); } [Fact] public async Task DeleteCategoryAsync_WithProductsAttached_ThrowsException() { // Arrange var categoryId = Guid.NewGuid(); var category = new Category { Id = categoryId, Name = "Category with Products", Description = "Has products", IsActive = true }; var product = new Product { Id = Guid.NewGuid(), Name = "Product", Description = "Product in category", Price = 10.00m, CategoryId = categoryId, IsActive = true, Weight = 1, WeightUnit = Enums.ProductWeightUnit.Kilograms }; _context.Categories.Add(category); _context.Products.Add(product); await _context.SaveChangesAsync(); // Act & Assert await Assert.ThrowsAsync(async () => { await _categoryService.DeleteCategoryAsync(categoryId); }); } public void Dispose() { _context.Dispose(); } }