using FluentAssertions; using Marathon.Application.Abstractions; using Marathon.Application.Storage; using Marathon.Application.UseCases; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using NSubstitute; using NSubstitute.ExceptionExtensions; namespace Marathon.Application.Tests.UseCases; public sealed class ExportToExcelUseCaseTests { private readonly IExcelExporter _exporter = Substitute.For(); private ExportToExcelUseCase CreateSut(string exportDir = "./exports") { var opts = Options.Create(new StorageOptions { ExportDirectory = exportDir, DatabasePath = "./data/marathon.db", }); return new ExportToExcelUseCase(_exporter, opts, NullLogger.Instance); } [Fact] public async Task Should_InvokeExporterWithCorrectArguments_When_Executed() { // Arrange var range = new DateRange( new DateTimeOffset(2026, 5, 1, 0, 0, 0, TimeSpan.Zero), new DateTimeOffset(2026, 5, 7, 23, 59, 59, TimeSpan.Zero)); const ExportKind kind = ExportKind.Combined; const string expectedOutputPath = "./exports/Marathon_2026-05-01_to_2026-05-07.xlsx"; _exporter .ExportAsync(range, kind, Arg.Any(), Arg.Any()) .Returns(expectedOutputPath); var sut = CreateSut(); // Act var outputPath = await sut.ExecuteAsync(range, kind, CancellationToken.None); // Assert outputPath.Should().Be(expectedOutputPath); await _exporter.Received(1).ExportAsync( range, kind, Arg.Is(dir => dir.Contains("exports")), Arg.Any()); } [Fact] public async Task Should_ReturnAbsoluteOutputPath_When_ExporterSucceeds() { // Arrange var range = new DateRange(DateTimeOffset.UtcNow.AddDays(-7), DateTimeOffset.UtcNow); const string absolutePath = @"C:\exports\Marathon_2026-05-01_to_2026-05-07.xlsx"; _exporter .ExportAsync(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(absolutePath); var sut = CreateSut(); // Act var result = await sut.ExecuteAsync(range, ExportKind.PreMatch, CancellationToken.None); // Assert result.Should().Be(absolutePath); } [Fact] public async Task Should_PropagateExporterException_When_ExportFails() { // Arrange var range = new DateRange(DateTimeOffset.UtcNow.AddDays(-1), DateTimeOffset.UtcNow); _exporter .ExportAsync(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .ThrowsAsync(new InvalidOperationException("disk full")); var sut = CreateSut(); // Act var act = async () => await sut.ExecuteAsync(range, ExportKind.Live, CancellationToken.None); // Assert — ExportToExcelUseCase does not swallow exporter exceptions; callers decide how to handle await act.Should().ThrowAsync().WithMessage("disk full"); } }