using FluentAssertions; using Marathon.Application.Abstractions; using Marathon.Application.UseCases; using Marathon.Domain.Backtesting; using Microsoft.Extensions.Logging.Abstractions; using NSubstitute; namespace Marathon.Application.Tests.UseCases; public sealed class SaveStrategyUseCaseTests { private readonly ISavedStrategyRepository _repo = Substitute.For(); private SaveStrategyUseCase CreateSut() => new(_repo, NullLogger.Instance); [Fact] public async Task Creates_New_When_NameUnused() { _repo.GetByNameAsync("Fresh", Arg.Any()).Returns((SavedStrategy?)null); var result = await CreateSut().ExecuteAsync("Fresh", BacktestStrategy.Default, CancellationToken.None); result.Name.Should().Be("Fresh"); await _repo.Received(1).AddAsync( Arg.Is(s => s.Name == "Fresh"), Arg.Any()); await _repo.DidNotReceive().UpdateAsync(Arg.Any(), Arg.Any()); await _repo.Received(1).SaveChangesAsync(Arg.Any()); } [Fact] public async Task Overwrites_Existing_KeepingIdentityAndCreatedAt() { var existing = SavedStrategy.Create("Reused", BacktestStrategy.Default); _repo.GetByNameAsync("Reused", Arg.Any()).Returns(existing); var newStrategy = new BacktestStrategy(1000m, 0.7m, StakeRule.Flat, 50m, 0.02m, 0.25m); var result = await CreateSut().ExecuteAsync("Reused", newStrategy, CancellationToken.None); result.Id.Should().Be(existing.Id); result.CreatedAt.Should().Be(existing.CreatedAt); result.Strategy.MinScore.Should().Be(0.7m); await _repo.Received(1).UpdateAsync( Arg.Is(s => s.Id == existing.Id && s.Strategy.MinScore == 0.7m), Arg.Any()); await _repo.DidNotReceive().AddAsync(Arg.Any(), Arg.Any()); } [Fact] public async Task UpsertLookup_UsesTrimmedName() { _repo.GetByNameAsync("Padded", Arg.Any()).Returns((SavedStrategy?)null); await CreateSut().ExecuteAsync(" Padded ", BacktestStrategy.Default, CancellationToken.None); await _repo.Received(1).GetByNameAsync("Padded", Arg.Any()); } [Theory] [InlineData("")] [InlineData(" ")] public async Task Throws_When_NameBlank(string blank) { var act = async () => await CreateSut().ExecuteAsync(blank, BacktestStrategy.Default, CancellationToken.None); await act.Should().ThrowAsync(); await _repo.DidNotReceive().AddAsync(Arg.Any(), Arg.Any()); } }