Files
2026-04-17 08:09:31 +02:00

140 lines
4.1 KiB
C#

using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using TrafagSalesExporter.Data;
using TrafagSalesExporter.Models;
using TrafagSalesExporter.Services;
namespace TrafagSalesExporter.Tests;
public class CurrencyExchangeRateServiceTests : IDisposable
{
private readonly SqliteConnection _connection;
private readonly TestDbContextFactory _dbFactory;
private readonly CurrencyExchangeRateService _service;
public CurrencyExchangeRateServiceTests()
{
_connection = new SqliteConnection("DataSource=:memory:");
_connection.Open();
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlite(_connection)
.Options;
using var db = new AppDbContext(options);
db.Database.EnsureCreated();
_dbFactory = new TestDbContextFactory(options);
_service = new CurrencyExchangeRateService(_dbFactory);
}
public void Dispose()
{
_connection.Dispose();
}
[Fact]
public async Task ResolveRate_Returns_Direct_Rate_For_Valid_Date()
{
await SeedRatesAsync(new CurrencyExchangeRate
{
FromCurrency = "USD",
ToCurrency = "EUR",
Rate = 0.92m,
ValidFrom = new DateTime(2026, 1, 1),
ValidTo = null,
IsActive = true
});
var rate = _service.ResolveRate("USD", "EUR", new DateTime(2026, 4, 1));
Assert.Equal(0.92m, rate);
}
[Fact]
public async Task ResolveRate_Uses_Inverse_Rate_When_Only_Reverse_Rate_Exists()
{
await SeedRatesAsync(new CurrencyExchangeRate
{
FromCurrency = "EUR",
ToCurrency = "CHF",
Rate = 1.10m,
ValidFrom = new DateTime(2026, 1, 1),
ValidTo = null,
IsActive = true
});
var rate = _service.ResolveRate("CHF", "EUR", new DateTime(2026, 4, 1));
Assert.NotNull(rate);
Assert.Equal(1m / 1.10m, rate!.Value, 6);
}
[Fact]
public async Task ResolveRate_Uses_Eur_Cross_Rate_When_No_Direct_Rate_Exists()
{
await SeedRatesAsync(
new CurrencyExchangeRate
{
FromCurrency = "CHF",
ToCurrency = "EUR",
Rate = 0.95m,
ValidFrom = new DateTime(2026, 1, 1),
ValidTo = null,
IsActive = true
},
new CurrencyExchangeRate
{
FromCurrency = "EUR",
ToCurrency = "USD",
Rate = 1.08m,
ValidFrom = new DateTime(2026, 1, 1),
ValidTo = null,
IsActive = true
});
var rate = _service.ResolveRate("CHF", "USD", new DateTime(2026, 4, 1));
Assert.NotNull(rate);
Assert.Equal(1.026m, rate!.Value, 6);
}
[Theory]
[InlineData("$", "USD")]
[InlineData("US$", "USD")]
[InlineData("EUR", "EUR")]
[InlineData("sfr", "CHF")]
[InlineData("cad", "CAD")]
[InlineData("xyz", "XYZ")]
public void NormalizeCurrencyCode_Normalizes_Known_And_Unknown_Codes(string input, string expected)
{
var normalized = _service.NormalizeCurrencyCode(input);
Assert.Equal(expected, normalized);
}
private async Task SeedRatesAsync(params CurrencyExchangeRate[] rates)
{
await using var db = await _dbFactory.CreateDbContextAsync();
db.CurrencyExchangeRates.RemoveRange(db.CurrencyExchangeRates);
await db.SaveChangesAsync();
db.CurrencyExchangeRates.AddRange(rates);
await db.SaveChangesAsync();
}
private sealed class TestDbContextFactory : IDbContextFactory<AppDbContext>
{
private readonly DbContextOptions<AppDbContext> _options;
public TestDbContextFactory(DbContextOptions<AppDbContext> options)
{
_options = options;
}
public AppDbContext CreateDbContext() => new(_options);
public Task<AppDbContext> CreateDbContextAsync(CancellationToken cancellationToken = default)
=> Task.FromResult(new AppDbContext(_options));
}
}