using Duempelkas.App.Services; 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 DashboardPdfStatementServiceTests : IDisposable { private readonly FinanceDbContext _db; private readonly PdfStatementService _pdfService; private readonly string _connectionString = $"Data Source=duempelkas-dashboard-pdf-tests-{Guid.NewGuid():N};Mode=Memory;Cache=Shared"; public DashboardPdfStatementServiceTests() { var options = new DbContextOptionsBuilder() .UseSqlite(_connectionString) .Options; _db = new FinanceDbContext(options); _db.Database.OpenConnection(); _db.Database.EnsureCreated(); var dbFactory = new TestDbContextFactory(options); var entryService = new EntryService(dbFactory); var balanceQueryService = new BalanceQueryService(dbFactory); var settingsService = new FixedSettingsService("Testverein"); _pdfService = new PdfStatementService(dbFactory, entryService, balanceQueryService, settingsService); } [Fact] public async Task GenerateDashboardStatementAsync_WithBookingsAndTransfer_ReturnsPdf() { var barkasse = new Account { Name = "Barkasse" }; var girokonto = new Account { Name = "Girokonto" }; _db.Accounts.AddRange(barkasse, girokonto); await _db.SaveChangesAsync(); _db.Entries.Add(new Entry { AccountId = barkasse.Id, DisplayId = "2026-001", Type = EntryType.Income, Date = new DateTime(2026, 1, 12), Title = "Einkuenfte Sommerfest", Amount = 604.60m }); var transferExpense = new Entry { AccountId = barkasse.Id, DisplayId = "2026-002", Type = EntryType.Expense, Date = new DateTime(2026, 2, 16), Title = "Einzahlung", Amount = 600.00m }; var transferIncome = new Entry { AccountId = girokonto.Id, DisplayId = "2026-002", Type = EntryType.Income, Date = new DateTime(2026, 2, 16), Title = "Einzahlung", Amount = 600.00m }; _db.Entries.AddRange(transferExpense, transferIncome); await _db.SaveChangesAsync(); var transferLink = new TransferLink { SourceEntryId = transferExpense.Id, TargetEntryId = transferIncome.Id, Note = "Umbuchung" }; _db.TransferLinks.Add(transferLink); await _db.SaveChangesAsync(); transferExpense.TransferLinkId = transferLink.Id; transferIncome.TransferLinkId = transferLink.Id; await _db.SaveChangesAsync(); var pdf = await _pdfService.GenerateDashboardStatementAsync(); pdf.Should().NotBeNull(); pdf.Length.Should().BeGreaterThan(1000); System.Text.Encoding.ASCII.GetString(pdf.Take(4).ToArray()).Should().Be("%PDF"); } public void Dispose() { _db.Database.CloseConnection(); _db.Dispose(); } private sealed class TestDbContextFactory : IDbContextFactory { private readonly DbContextOptions _options; public TestDbContextFactory(DbContextOptions options) { _options = options; } public FinanceDbContext CreateDbContext() => new(_options); public Task CreateDbContextAsync(CancellationToken cancellationToken = default) => Task.FromResult(new FinanceDbContext(_options)); } private sealed class FixedSettingsService : ISettingsService { private readonly string? _clubName; public FixedSettingsService(string? clubName) { _clubName = clubName; } public Task GetClubNameAsync() => Task.FromResult(_clubName); public Task SetClubNameAsync(string? clubName) => Task.CompletedTask; } }