124 lines
4.4 KiB
C#
124 lines
4.4 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 EntryServiceBookingTests : IDisposable
|
|
{
|
|
private readonly FinanceDbContext _db;
|
|
private readonly EntryService _entryService;
|
|
private readonly string _connectionString = $"Data Source=duempelkas-entry-tests-{Guid.NewGuid():N};Mode=Memory;Cache=Shared";
|
|
|
|
public EntryServiceBookingTests()
|
|
{
|
|
var options = new DbContextOptionsBuilder<FinanceDbContext>()
|
|
.UseSqlite(_connectionString)
|
|
.Options;
|
|
|
|
_db = new FinanceDbContext(options);
|
|
_db.Database.OpenConnection();
|
|
_db.Database.EnsureCreated();
|
|
|
|
var dbFactory = new TestDbContextFactory(options);
|
|
_entryService = new EntryService(dbFactory);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateEntry_AssignsSequentialDisplayIds_PerYear()
|
|
{
|
|
var accountA = new Account { Name = "Konto A" };
|
|
var accountB = new Account { Name = "Konto B" };
|
|
_db.Accounts.AddRange(accountA, accountB);
|
|
await _db.SaveChangesAsync();
|
|
|
|
var first = await _entryService.CreateEntryAsync(accountA.Id, EntryType.Income, new DateTime(2026, 1, 10), "Einnahme A", 100m);
|
|
var second = await _entryService.CreateEntryAsync(accountB.Id, EntryType.Expense, new DateTime(2026, 1, 11), "Ausgabe B", 20m);
|
|
var third = await _entryService.CreateEntryAsync(accountA.Id, EntryType.Income, new DateTime(2027, 1, 1), "Neues Jahr", 5m);
|
|
|
|
first.DisplayId.Should().Be("2026-001");
|
|
second.DisplayId.Should().Be("2026-002");
|
|
third.DisplayId.Should().Be("2027-001");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateEntry_AfterTransfer_UsesNextDisplayId()
|
|
{
|
|
var source = new Account { Name = "Barkasse" };
|
|
var target = new Account { Name = "Girokonto" };
|
|
_db.Accounts.AddRange(source, target);
|
|
await _db.SaveChangesAsync();
|
|
|
|
await _entryService.CreateTransferAsync(source.Id, target.Id, new DateTime(2026, 3, 15), "Umbuchung", 500m);
|
|
var booking = await _entryService.CreateEntryAsync(source.Id, EntryType.Income, new DateTime(2026, 3, 16), "Einzahlung", 50m);
|
|
|
|
booking.DisplayId.Should().Be("2026-002");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateEntry_UpdatesBookingFields()
|
|
{
|
|
var account = new Account { Name = "Konto" };
|
|
_db.Accounts.Add(account);
|
|
await _db.SaveChangesAsync();
|
|
|
|
var entry = await _entryService.CreateEntryAsync(account.Id, EntryType.Expense, new DateTime(2026, 2, 2), "Alt", 10m);
|
|
|
|
await _entryService.UpdateEntryAsync(entry.Id, new DateTime(2026, 2, 3), "Neu", 25m);
|
|
|
|
_db.ChangeTracker.Clear();
|
|
var updated = await _db.Entries.SingleAsync(e => e.Id == entry.Id);
|
|
|
|
updated.Date.Should().Be(new DateTime(2026, 2, 3));
|
|
updated.Title.Should().Be("Neu");
|
|
updated.Amount.Should().Be(25m);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteAndRestoreEntry_TogglesSoftDelete_ForBookingOnly()
|
|
{
|
|
var account = new Account { Name = "Konto" };
|
|
_db.Accounts.Add(account);
|
|
await _db.SaveChangesAsync();
|
|
|
|
var entry = await _entryService.CreateEntryAsync(account.Id, EntryType.Expense, new DateTime(2026, 2, 2), "Buchung", 10m);
|
|
|
|
await _entryService.DeleteEntryAsync(entry.Id);
|
|
|
|
_db.ChangeTracker.Clear();
|
|
var deleted = await _db.Entries.SingleAsync(e => e.Id == entry.Id);
|
|
deleted.IsDeleted.Should().BeTrue();
|
|
|
|
await _entryService.RestoreEntryAsync(entry.Id);
|
|
|
|
_db.ChangeTracker.Clear();
|
|
var restored = await _db.Entries.SingleAsync(e => e.Id == entry.Id);
|
|
restored.IsDeleted.Should().BeFalse();
|
|
}
|
|
|
|
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));
|
|
}
|
|
}
|