Files
duempelkas/tests/Duempelkas.Tests/BackupServiceTests.cs
T

85 lines
3.6 KiB
C#

using Duempelkas.Domain.Enums;
using Duempelkas.Infrastructure;
using Duempelkas.Infrastructure.Persistence;
using Duempelkas.Infrastructure.Services;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace Duempelkas.Tests;
public sealed class BackupServiceTests : IDisposable
{
private readonly string folder = Path.Combine(Path.GetTempPath(), "duempelkas-tests-" + Guid.NewGuid().ToString("N"));
private readonly ServiceProvider services;
private readonly BackupService backup;
private readonly EntryService entries;
private readonly int accountId;
public BackupServiceTests()
{
Directory.CreateDirectory(folder);
services = new ServiceCollection().AddInfrastructure($"Data Source={Path.Combine(folder, "active.db")};Pooling=False").BuildServiceProvider();
using var db = services.GetRequiredService<IDbContextFactory<FinanceDbContext>>().CreateDbContext();
db.Database.Migrate();
db.Database.ExecuteSqlRaw("PRAGMA journal_mode=WAL;");
db.Accounts.Add(new() { Name = "Isolierte Testkasse" });
db.SaveChanges();
accountId = db.Accounts.Single().Id;
backup = new BackupService(services.GetRequiredService<DatabaseLocation>());
entries = (EntryService)services.GetRequiredService<Duempelkas.App.Services.IEntryService>();
}
[Fact]
public async Task WalSnapshot_RestoresExactState_AndKeepsFallback()
{
var first = await entries.CreateEntryAsync(accountId, EntryType.Income, new(2026, 1, 1), "Gesichert", 12.50m);
var path = Path.Combine(folder, "snapshot.bak");
await backup.CreateBackupToAsync(path);
await entries.CreateEntryAsync(accountId, EntryType.Expense, new(2026, 1, 2), "Später", 4.20m);
var fallback = await backup.RestoreFromAsync(path);
var restored = await entries.GetEntriesAsync(accountId, (int?)null);
Assert.Single(restored);
Assert.Equal(first, restored[0]);
using var old = new SqliteConnection($"Data Source={fallback};Mode=ReadOnly;Pooling=False");
old.Open();
using var command = old.CreateCommand();
command.CommandText = "SELECT COUNT(*) FROM Entries";
Assert.Equal(2L, command.ExecuteScalar());
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task InvalidBackup_DoesNotChangeActiveData(bool sqlite)
{
await entries.CreateEntryAsync(accountId, EntryType.Income, new(2026, 1, 1), "Bleibt", 10m);
var path = Path.Combine(folder, "invalid.bak");
if (sqlite)
{
using var connection = new SqliteConnection($"Data Source={path};Pooling=False");
connection.Open();
using var command = connection.CreateCommand();
command.CommandText = "CREATE TABLE Unrelated (Id INTEGER)";
command.ExecuteNonQuery();
}
else await File.WriteAllTextAsync(path, "not a database");
await Assert.ThrowsAnyAsync<Exception>(() => backup.RestoreFromAsync(path));
Assert.Single(await entries.GetEntriesAsync(accountId, (int?)null));
}
[Fact]
public async Task MissingSource_AndActiveDestination_AreRejected()
{
await Assert.ThrowsAnyAsync<Exception>(() => backup.RestoreFromAsync(Path.Combine(folder, "missing.bak")));
await Assert.ThrowsAnyAsync<Exception>(() => backup.CreateBackupToAsync(Path.Combine(folder, "active.db")));
}
public void Dispose()
{
services.Dispose();
Directory.Delete(folder, true);
}
}