Files
2026-03-06 11:05:01 +00:00

276 lines
9.5 KiB
C#

using BudgetApp.Data.Entities;
using BudgetApp.Data.Repositories;
using BudgetApp.Tests.Helpers;
using Microsoft.EntityFrameworkCore;
using Xunit;
namespace BudgetApp.Tests.Services;
public class CategoryServiceTests
{
[Fact]
public async Task GetCategoriesAsync_ReturnsOnlyUserCategories()
{
using var factory = new TestDbContextFactory();
using (var ctx = factory.CreateContext())
{
ctx.Categories.AddRange(
new Category { Name = "Mine", UserId = TestDbContextFactory.TestUserId },
new Category { Name = "Theirs", UserId = TestDbContextFactory.OtherUserId });
await ctx.SaveChangesAsync();
}
using var context = factory.CreateContext();
var service = new CategoryRepository(context);
var result = await service.GetCategoriesAsync(TestDbContextFactory.TestUserId);
Assert.Single(result);
Assert.Equal("Mine", result[0].Name);
}
[Fact]
public async Task GetCategoriesAsync_OrderedByName()
{
using var factory = new TestDbContextFactory();
using (var ctx = factory.CreateContext())
{
ctx.Categories.AddRange(
new Category { Name = "Zebra", UserId = TestDbContextFactory.TestUserId },
new Category { Name = "Alpha", UserId = TestDbContextFactory.TestUserId });
await ctx.SaveChangesAsync();
}
using var context = factory.CreateContext();
var service = new CategoryRepository(context);
var result = await service.GetCategoriesAsync(TestDbContextFactory.TestUserId);
Assert.Equal("Alpha", result[0].Name);
Assert.Equal("Zebra", result[1].Name);
}
[Fact]
public async Task EnsureDefaultCategoriesAsync_CreatesDefaults_WhenEmpty()
{
using var factory = new TestDbContextFactory();
using var context = factory.CreateContext();
var service = new CategoryRepository(context);
await service.EnsureDefaultCategoriesAsync(TestDbContextFactory.TestUserId);
using var verifyCtx = factory.CreateContext();
var categories = await verifyCtx.Categories
.Where(c => c.UserId == TestDbContextFactory.TestUserId)
.ToListAsync();
Assert.Equal(18, categories.Count);
Assert.Contains(categories, c => c.Name == "Groceries" && c.Icon == "🛒");
Assert.Contains(categories, c => c.Name == "Transport" && c.Icon == "🚗");
Assert.Contains(categories, c => c.Name == "Salary" && c.Icon == "💰");
}
[Fact]
public async Task EnsureDefaultCategoriesAsync_DoesNotDuplicate_WhenCalledAgain()
{
using var factory = new TestDbContextFactory();
using (var ctx = factory.CreateContext())
{
var service = new CategoryRepository(ctx);
await service.EnsureDefaultCategoriesAsync(TestDbContextFactory.TestUserId);
}
using (var ctx = factory.CreateContext())
{
var service = new CategoryRepository(ctx);
await service.EnsureDefaultCategoriesAsync(TestDbContextFactory.TestUserId);
}
using var verifyCtx = factory.CreateContext();
var count = await verifyCtx.Categories
.Where(c => c.UserId == TestDbContextFactory.TestUserId)
.CountAsync();
Assert.Equal(18, count);
}
[Fact]
public async Task EnsureDefaultCategoriesAsync_BackfillsIcons_WhenMissing()
{
using var factory = new TestDbContextFactory();
using (var ctx = factory.CreateContext())
{
ctx.Categories.Add(new Category
{
Name = "Groceries",
Icon = "",
UserId = TestDbContextFactory.TestUserId
});
await ctx.SaveChangesAsync();
}
using var context = factory.CreateContext();
var service = new CategoryRepository(context);
await service.EnsureDefaultCategoriesAsync(TestDbContextFactory.TestUserId);
using var verifyCtx = factory.CreateContext();
var category = await verifyCtx.Categories
.SingleAsync(c => c.Name == "Groceries" && c.UserId == TestDbContextFactory.TestUserId);
Assert.Equal("🛒", category.Icon);
}
[Fact]
public async Task EnsureDefaultCategoriesAsync_DoesNotOverwrite_ExistingIcons()
{
using var factory = new TestDbContextFactory();
using (var ctx = factory.CreateContext())
{
ctx.Categories.Add(new Category
{
Name = "Groceries",
Icon = "🥕",
UserId = TestDbContextFactory.TestUserId
});
await ctx.SaveChangesAsync();
}
using var context = factory.CreateContext();
var service = new CategoryRepository(context);
await service.EnsureDefaultCategoriesAsync(TestDbContextFactory.TestUserId);
using var verifyCtx = factory.CreateContext();
var category = await verifyCtx.Categories
.SingleAsync(c => c.Name == "Groceries" && c.UserId == TestDbContextFactory.TestUserId);
Assert.Equal("🥕", category.Icon);
}
[Fact]
public async Task EnsureDefaultCategoriesAsync_IsolatedPerUser()
{
using var factory = new TestDbContextFactory();
using (var ctx = factory.CreateContext())
{
var service = new CategoryRepository(ctx);
await service.EnsureDefaultCategoriesAsync(TestDbContextFactory.TestUserId);
}
using (var ctx = factory.CreateContext())
{
var service = new CategoryRepository(ctx);
await service.EnsureDefaultCategoriesAsync(TestDbContextFactory.OtherUserId);
}
using var verifyCtx = factory.CreateContext();
var totalCount = await verifyCtx.Categories.CountAsync();
Assert.Equal(36, totalCount);
}
[Fact]
public async Task CreateCategoryAsync_AddsToDatabase()
{
using var factory = new TestDbContextFactory();
using var context = factory.CreateContext();
var service = new CategoryRepository(context);
await service.CreateCategoryAsync(new Category
{
Name = "Custom",
Icon = "🎯",
UserId = TestDbContextFactory.TestUserId
});
using var verifyCtx = factory.CreateContext();
Assert.Single(verifyCtx.Categories);
}
[Fact]
public async Task UpdateCategoryAsync_UpdatesNameAndIcon()
{
using var factory = new TestDbContextFactory();
int categoryId;
using (var ctx = factory.CreateContext())
{
var category = new Category { Name = "Old", Icon = "📁", UserId = TestDbContextFactory.TestUserId };
ctx.Categories.Add(category);
await ctx.SaveChangesAsync();
categoryId = category.Id;
}
using var context = factory.CreateContext();
var service = new CategoryRepository(context);
await service.UpdateCategoryAsync(categoryId, TestDbContextFactory.TestUserId, "New", "🎯");
using var verifyCtx = factory.CreateContext();
var updated = verifyCtx.Categories.Single();
Assert.Equal("New", updated.Name);
Assert.Equal("🎯", updated.Icon);
}
[Fact]
public async Task UpdateCategoryAsync_WrongUser_DoesNothing()
{
using var factory = new TestDbContextFactory();
int categoryId;
using (var ctx = factory.CreateContext())
{
var category = new Category { Name = "Original", Icon = "📁", UserId = TestDbContextFactory.TestUserId };
ctx.Categories.Add(category);
await ctx.SaveChangesAsync();
categoryId = category.Id;
}
using var context = factory.CreateContext();
var service = new CategoryRepository(context);
await service.UpdateCategoryAsync(categoryId, TestDbContextFactory.OtherUserId, "Hacked", "💀");
using var verifyCtx = factory.CreateContext();
Assert.Equal("Original", verifyCtx.Categories.Single().Name);
}
[Fact]
public async Task DeleteCategoryAsync_RemovesCategory()
{
using var factory = new TestDbContextFactory();
int categoryId;
using (var ctx = factory.CreateContext())
{
var category = new Category { Name = "ToDelete", UserId = TestDbContextFactory.TestUserId };
ctx.Categories.Add(category);
await ctx.SaveChangesAsync();
categoryId = category.Id;
}
using var context = factory.CreateContext();
var service = new CategoryRepository(context);
await service.DeleteCategoryAsync(categoryId, TestDbContextFactory.TestUserId);
using var verifyCtx = factory.CreateContext();
Assert.Empty(verifyCtx.Categories);
}
[Fact]
public async Task DeleteCategoryAsync_WrongUser_DoesNothing()
{
using var factory = new TestDbContextFactory();
int categoryId;
using (var ctx = factory.CreateContext())
{
var category = new Category { Name = "Protected", UserId = TestDbContextFactory.TestUserId };
ctx.Categories.Add(category);
await ctx.SaveChangesAsync();
categoryId = category.Id;
}
using var context = factory.CreateContext();
var service = new CategoryRepository(context);
await service.DeleteCategoryAsync(categoryId, TestDbContextFactory.OtherUserId);
using var verifyCtx = factory.CreateContext();
Assert.Single(verifyCtx.Categories);
}
}