128 lines
5.1 KiB
C#
128 lines
5.1 KiB
C#
using Duempelkas.Domain.Entities;
|
|
using Duempelkas.Domain.Enums;
|
|
using Duempelkas.Infrastructure.Persistence;
|
|
using Duempelkas.Infrastructure.Services;
|
|
using FluentAssertions;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Xunit;
|
|
|
|
namespace Duempelkas.Tests;
|
|
|
|
public class BalanceCalculationTests : IDisposable
|
|
{
|
|
private readonly FinanceDbContext _db;
|
|
private readonly BalanceQueryService _balanceQueryService;
|
|
private readonly EntryService _entryService;
|
|
private readonly string _connectionString = $"Data Source=duempelkas-balance-tests-{Guid.NewGuid():N};Mode=Memory;Cache=Shared";
|
|
|
|
public BalanceCalculationTests()
|
|
{
|
|
var options = new DbContextOptionsBuilder<FinanceDbContext>()
|
|
.UseSqlite(_connectionString)
|
|
.Options;
|
|
|
|
_db = new FinanceDbContext(options);
|
|
_db.Database.OpenConnection();
|
|
_db.Database.EnsureCreated();
|
|
|
|
var dbFactory = new TestDbContextFactory(options);
|
|
|
|
_balanceQueryService = new BalanceQueryService(dbFactory);
|
|
_entryService = new EntryService(dbFactory);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AccountBalance_CalculatesCorrectTotals()
|
|
{
|
|
var account = new Account { Name = "Test Account", CarryoverBalance = 500.00m };
|
|
_db.Accounts.Add(account);
|
|
await _db.SaveChangesAsync();
|
|
|
|
_db.Entries.AddRange(
|
|
new Entry { AccountId = account.Id, DisplayId = "2026-001", Type = EntryType.Income, Date = new DateTime(2026, 1, 15), Title = "Gehalt", Amount = 800.00m },
|
|
new Entry { AccountId = account.Id, DisplayId = "2026-002", Type = EntryType.Income, Date = new DateTime(2026, 2, 15), Title = "Bonus", Amount = 400.00m },
|
|
new Entry { AccountId = account.Id, DisplayId = "2026-003", Type = EntryType.Expense, Date = new DateTime(2026, 1, 20), Title = "Miete", Amount = 200.00m },
|
|
new Entry { AccountId = account.Id, DisplayId = "2026-004", Type = EntryType.Expense, Date = new DateTime(2026, 2, 20), Title = "Lebensmittel", Amount = 250.00m }
|
|
);
|
|
await _db.SaveChangesAsync();
|
|
|
|
var balance = await _balanceQueryService.GetAccountBalanceAsync(account.Id);
|
|
|
|
balance.CarryoverBalance.Should().Be(500.00m);
|
|
balance.TotalIncome.Should().Be(1200.00m);
|
|
balance.TotalExpense.Should().Be(450.00m);
|
|
balance.TotalBalance.Should().Be(1250.00m);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AccountBalance_IncludesTransfersCorrectly()
|
|
{
|
|
var accountA = new Account { Name = "Account A", CarryoverBalance = 500.00m };
|
|
var accountB = new Account { Name = "Account B" };
|
|
_db.Accounts.AddRange(accountA, accountB);
|
|
await _db.SaveChangesAsync();
|
|
|
|
_db.Entries.Add(new Entry
|
|
{
|
|
AccountId = accountA.Id,
|
|
DisplayId = "2026-001",
|
|
Type = EntryType.Income,
|
|
Date = new DateTime(2026, 1, 10),
|
|
Title = "Gehalt",
|
|
Amount = 1000.00m
|
|
});
|
|
await _db.SaveChangesAsync();
|
|
|
|
await _entryService.CreateTransferAsync(accountA.Id, accountB.Id, new DateTime(2026, 2, 1), "Umbuchung nach B", 300.00m);
|
|
|
|
var balanceA = await _balanceQueryService.GetAccountBalanceAsync(accountA.Id);
|
|
|
|
balanceA.CarryoverBalance.Should().Be(500.00m);
|
|
balanceA.TotalIncome.Should().Be(1000.00m);
|
|
balanceA.TotalExpense.Should().Be(300.00m);
|
|
balanceA.TotalBalance.Should().Be(1200.00m);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AccountBalance_WithCarryover()
|
|
{
|
|
var account = new Account { Name = "Konto mit Übertrag", CarryoverBalance = 1000.00m };
|
|
_db.Accounts.Add(account);
|
|
await _db.SaveChangesAsync();
|
|
|
|
_db.Entries.AddRange(
|
|
new Entry { AccountId = account.Id, DisplayId = "2025-001", Type = EntryType.Income, Date = new DateTime(2025, 6, 1), Title = "Einnahme 2025", Amount = 500.00m },
|
|
new Entry { AccountId = account.Id, DisplayId = "2026-001", Type = EntryType.Income, Date = new DateTime(2026, 6, 1), Title = "Einnahme 2026", Amount = 300.00m },
|
|
new Entry { AccountId = account.Id, DisplayId = "2026-002", Type = EntryType.Expense, Date = new DateTime(2026, 7, 1), Title = "Ausgabe 2026", Amount = 150.00m }
|
|
);
|
|
await _db.SaveChangesAsync();
|
|
|
|
var balance = await _balanceQueryService.GetAccountBalanceAsync(account.Id);
|
|
|
|
// 1000 + 500 + 300 - 150 = 1650
|
|
balance.TotalBalance.Should().Be(1650.00m);
|
|
balance.CarryoverBalance.Should().Be(1000.00m);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_db.Database.CloseConnection();
|
|
_db.Dispose();
|
|
}
|
|
|
|
private sealed class TestDbContextFactory : IDbContextFactory<FinanceDbContext>
|
|
{
|
|
private readonly DbContextOptions<FinanceDbContext> _options;
|
|
|
|
public TestDbContextFactory(DbContextOptions<FinanceDbContext> options)
|
|
{
|
|
_options = options;
|
|
}
|
|
|
|
public FinanceDbContext CreateDbContext() => new(_options);
|
|
|
|
public Task<FinanceDbContext> CreateDbContextAsync(CancellationToken cancellationToken = default)
|
|
=> Task.FromResult(new FinanceDbContext(_options));
|
|
}
|
|
}
|