littleshop/LittleShop.Tests/Integration/CatalogControllerTests.cs

340 lines
11 KiB
C#

using System.Net;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using FluentAssertions;
using LittleShop.Data;
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 CatalogControllerTests : IClassFixture<TestWebApplicationFactory>
{
private readonly HttpClient _client;
private readonly TestWebApplicationFactory _factory;
private readonly JsonSerializerOptions _jsonOptions;
public CatalogControllerTests(TestWebApplicationFactory factory)
{
_factory = factory;
_client = _factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
_jsonOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
// Add default JWT token for authenticated requests
var token = JwtTokenHelper.GenerateJwtToken();
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
[Fact]
public async Task GetCategories_WithAuthentication_ReturnsOnlyActiveCategories()
{
// Arrange
await SeedTestData();
// Act
var response = await _client.GetAsync("/api/catalog/categories");
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var categories = JsonSerializer.Deserialize<List<CategoryDto>>(content, _jsonOptions);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
categories.Should().NotBeNull();
categories.Should().HaveCountGreaterThan(0);
categories.Should().OnlyContain(c => c.IsActive);
}
[Fact]
public async Task GetCategoryById_WithValidId_ReturnsCategory()
{
// Arrange
var categoryId = await SeedCategoryAndGetId("Test Category");
// Act
var response = await _client.GetAsync($"/api/catalog/categories/{categoryId}");
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var category = JsonSerializer.Deserialize<CategoryDto>(content, _jsonOptions);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
category.Should().NotBeNull();
category!.Id.Should().Be(categoryId);
category.Name.Should().Be("Test Category");
}
[Fact]
public async Task GetCategoryById_WithInvalidId_Returns404()
{
// Arrange
var invalidId = Guid.NewGuid();
// Act
var response = await _client.GetAsync($"/api/catalog/categories/{invalidId}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
}
[Fact]
public async Task GetCategoryById_WithInactiveCategory_Returns404()
{
// Arrange
var categoryId = await SeedCategoryAndGetId("Inactive Category", isActive: false);
// Act
var response = await _client.GetAsync($"/api/catalog/categories/{categoryId}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
}
[Fact]
public async Task GetProducts_WithoutCategoryFilter_ReturnsAllActiveProducts()
{
// Arrange
await SeedTestData();
// Act
var response = await _client.GetAsync("/api/catalog/products");
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var pagedResult = JsonSerializer.Deserialize<PagedResult<ProductDto>>(content, _jsonOptions);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
pagedResult.Should().NotBeNull();
pagedResult!.Items.Should().HaveCountGreaterThan(0);
pagedResult.TotalCount.Should().BeGreaterThan(0);
}
[Fact]
public async Task GetProducts_WithCategoryFilter_ReturnsOnlyProductsInCategory()
{
// Arrange
var categoryId = await SeedCategoryWithProducts();
// Act
var response = await _client.GetAsync($"/api/catalog/products?categoryId={categoryId}");
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var pagedResult = JsonSerializer.Deserialize<PagedResult<ProductDto>>(content, _jsonOptions);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
pagedResult.Should().NotBeNull();
pagedResult!.Items.Should().HaveCountGreaterThan(0);
pagedResult.Items.Should().OnlyContain(p => p.CategoryId == categoryId);
}
[Fact]
public async Task GetProductById_WithValidId_ReturnsProduct()
{
// Arrange
var productId = await SeedProductAndGetId();
// Act
var response = await _client.GetAsync($"/api/catalog/products/{productId}");
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var product = JsonSerializer.Deserialize<ProductDto>(content, _jsonOptions);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
product.Should().NotBeNull();
product!.Id.Should().Be(productId);
}
[Fact]
public async Task GetProductById_WithInvalidId_Returns404()
{
// Arrange
var invalidId = Guid.NewGuid();
// Act
var response = await _client.GetAsync($"/api/catalog/products/{invalidId}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
}
[Fact]
public async Task GetProductById_WithInactiveProduct_Returns404()
{
// Arrange
var productId = await SeedProductAndGetId(isActive: false);
// Act
var response = await _client.GetAsync($"/api/catalog/products/{productId}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
}
private async Task SeedTestData()
{
using var scope = _factory.Services.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<LittleShopContext>();
// Clear existing data
context.Categories.RemoveRange(context.Categories);
context.Products.RemoveRange(context.Products);
await context.SaveChangesAsync();
// Add test categories
var categories = new[]
{
new Category { Id = Guid.NewGuid(), Name = "Electronics", Description = "Electronic devices", IsActive = true },
new Category { Id = Guid.NewGuid(), Name = "Books", Description = "Books and literature", IsActive = true },
new Category { Id = Guid.NewGuid(), Name = "Archived", Description = "Archived category", IsActive = false }
};
context.Categories.AddRange(categories);
// Add test products
var products = new[]
{
new Product
{
Id = Guid.NewGuid(),
Name = "Laptop",
Description = "High-performance laptop",
Price = 999.99m,
CategoryId = categories[0].Id,
IsActive = true,
Weight = 2.5m,
WeightUnit = Enums.ProductWeightUnit.Kilograms
},
new Product
{
Id = Guid.NewGuid(),
Name = "Programming Book",
Description = "Learn programming",
Price = 49.99m,
CategoryId = categories[1].Id,
IsActive = true,
Weight = 500,
WeightUnit = Enums.ProductWeightUnit.Grams
}
};
context.Products.AddRange(products);
await context.SaveChangesAsync();
}
private async Task<Guid> SeedCategoryAndGetId(string name, bool isActive = true)
{
using var scope = _factory.Services.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<LittleShopContext>();
var category = new Category
{
Id = Guid.NewGuid(),
Name = name,
Description = "Test category",
IsActive = isActive
};
context.Categories.Add(category);
await context.SaveChangesAsync();
return category.Id;
}
private async Task<Guid> SeedCategoryWithProducts()
{
using var scope = _factory.Services.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<LittleShopContext>();
var category = new Category
{
Id = Guid.NewGuid(),
Name = "Test Category",
Description = "Category with products",
IsActive = true
};
context.Categories.Add(category);
var products = new[]
{
new Product
{
Id = Guid.NewGuid(),
Name = "Product 1",
Description = "First product",
Price = 19.99m,
CategoryId = category.Id,
IsActive = true,
Weight = 100,
WeightUnit = Enums.ProductWeightUnit.Grams
},
new Product
{
Id = Guid.NewGuid(),
Name = "Product 2",
Description = "Second product",
Price = 29.99m,
CategoryId = category.Id,
IsActive = true,
Weight = 200,
WeightUnit = Enums.ProductWeightUnit.Grams
}
};
context.Products.AddRange(products);
await context.SaveChangesAsync();
return category.Id;
}
private async Task<Guid> SeedProductAndGetId(bool isActive = true)
{
using var scope = _factory.Services.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<LittleShopContext>();
var category = new Category
{
Id = Guid.NewGuid(),
Name = "Product Category",
Description = "Category for product",
IsActive = true
};
context.Categories.Add(category);
var product = new Product
{
Id = Guid.NewGuid(),
Name = "Test Product",
Description = "Test product description",
Price = 99.99m,
CategoryId = category.Id,
IsActive = isActive,
Weight = 1.5m,
WeightUnit = Enums.ProductWeightUnit.Kilograms
};
context.Products.Add(product);
await context.SaveChangesAsync();
return product.Id;
}
}