Files
EmailBill/WebApi.Test/Application/TransactionPeriodicApplicationTest.cs

351 lines
11 KiB
C#
Raw Permalink Normal View History

2026-02-10 17:49:19 +08:00
using Application.Dto.Periodic;
using Service.Transaction;
namespace WebApi.Test.Application;
/// <summary>
/// TransactionPeriodicApplication 单元测试
/// </summary>
public class TransactionPeriodicApplicationTest : BaseApplicationTest
{
private readonly ITransactionPeriodicRepository _periodicRepository;
private readonly ITransactionPeriodicService _periodicService;
private readonly TransactionPeriodicApplication _application;
public TransactionPeriodicApplicationTest()
{
_periodicRepository = Substitute.For<ITransactionPeriodicRepository>();
_periodicService = Substitute.For<ITransactionPeriodicService>();
2026-02-11 13:00:01 +08:00
_application = new TransactionPeriodicApplication(_periodicRepository, _periodicService);
2026-02-10 17:49:19 +08:00
}
#region GetListAsync Tests
[Fact]
public async Task GetListAsync_有数据_应返回分页结果()
{
// Arrange
var pageIndex = 1;
var pageSize = 10;
var periodics = new List<TransactionPeriodic>
{
new() { Id = 1, Amount = 100, Type = TransactionType.Expense, Classify = "房租", IsEnabled = true },
new() { Id = 2, Amount = 5000, Type = TransactionType.Income, Classify = "工资", IsEnabled = true }
};
_periodicRepository.GetPagedListAsync(pageIndex, pageSize, null).Returns(periodics);
_periodicRepository.GetTotalCountAsync(null).Returns(2L);
// Act
var result = await _application.GetListAsync(pageIndex, pageSize);
// Assert
result.Data.Should().HaveCount(2);
result.Total.Should().Be(2);
result.Data[0].Amount.Should().Be(100);
}
[Fact]
public async Task GetListAsync_带搜索关键词_应过滤结果()
{
// Arrange
var keyword = "房租";
var periodics = new List<TransactionPeriodic>
{
new() { Id = 1, Amount = 1000, Type = TransactionType.Expense, Classify = "房租", IsEnabled = true }
};
_periodicRepository.GetPagedListAsync(1, 20, keyword).Returns(periodics);
_periodicRepository.GetTotalCountAsync(keyword).Returns(1L);
// Act
var result = await _application.GetListAsync(1, 20, keyword);
// Assert
result.Data.Should().HaveCount(1);
result.Total.Should().Be(1);
}
#endregion
#region GetByIdAsync Tests
[Fact]
public async Task GetByIdAsync_存在的记录_应返回详情()
{
// Arrange
var periodic = new TransactionPeriodic
{
Id = 1,
Amount = 1000,
Type = TransactionType.Expense,
Classify = "房租",
Reason = "每月房租",
IsEnabled = true
};
_periodicRepository.GetByIdAsync(1).Returns(periodic);
// Act
var result = await _application.GetByIdAsync(1);
// Assert
result.Should().NotBeNull();
result.Id.Should().Be(1);
result.Amount.Should().Be(1000);
result.Classify.Should().Be("房租");
}
[Fact]
public async Task GetByIdAsync_不存在的记录_应抛出NotFoundException()
{
// Arrange
_periodicRepository.GetByIdAsync(999).Returns((TransactionPeriodic?)null);
// Act & Assert
await Assert.ThrowsAsync<NotFoundException>(() => _application.GetByIdAsync(999));
}
#endregion
#region CreateAsync Tests
[Fact]
public async Task CreateAsync_有效请求_应成功创建()
{
// Arrange
var request = new CreatePeriodicRequest
{
PeriodicType = PeriodicType.Monthly,
PeriodicConfig = "1",
Amount = 1000,
Type = TransactionType.Expense,
Classify = "房租",
Reason = "每月房租"
};
var nextExecuteTime = DateTime.Now.AddDays(1);
_periodicService.CalculateNextExecuteTime(Arg.Any<TransactionPeriodic>(), Arg.Any<DateTime>())
.Returns(nextExecuteTime);
_periodicRepository.AddAsync(Arg.Any<TransactionPeriodic>()).Returns(true);
// Act
var result = await _application.CreateAsync(request);
// Assert
result.Should().NotBeNull();
result.Amount.Should().Be(1000);
result.IsEnabled.Should().BeTrue();
await _periodicRepository.Received(1).AddAsync(Arg.Is<TransactionPeriodic>(
p => p.Amount == 1000 && p.Classify == "房租"
));
}
[Fact]
public async Task CreateAsync_Repository添加失败_应抛出BusinessException()
{
// Arrange
var request = new CreatePeriodicRequest
{
PeriodicType = PeriodicType.Monthly,
Amount = 1000,
Type = TransactionType.Expense
};
_periodicService.CalculateNextExecuteTime(Arg.Any<TransactionPeriodic>(), Arg.Any<DateTime>())
.Returns(DateTime.Now.AddDays(1));
_periodicRepository.AddAsync(Arg.Any<TransactionPeriodic>()).Returns(false);
// Act & Assert
await Assert.ThrowsAsync<BusinessException>(() => _application.CreateAsync(request));
}
#endregion
#region UpdateAsync Tests
[Fact]
public async Task UpdateAsync_有效请求_应成功更新()
{
// Arrange
var existingPeriodic = new TransactionPeriodic
{
Id = 1,
Amount = 1000,
Type = TransactionType.Expense,
Classify = "房租",
IsEnabled = true
};
var request = new UpdatePeriodicRequest
{
Id = 1,
PeriodicType = PeriodicType.Monthly,
PeriodicConfig = "1",
Amount = 1200,
Type = TransactionType.Expense,
Classify = "房租",
Reason = "房租涨价",
IsEnabled = true
};
_periodicRepository.GetByIdAsync(1).Returns(existingPeriodic);
_periodicService.CalculateNextExecuteTime(Arg.Any<TransactionPeriodic>(), Arg.Any<DateTime>())
.Returns(DateTime.Now.AddMonths(1));
_periodicRepository.UpdateAsync(Arg.Any<TransactionPeriodic>()).Returns(true);
// Act
await _application.UpdateAsync(request);
// Assert
await _periodicRepository.Received(1).UpdateAsync(Arg.Is<TransactionPeriodic>(
p => p.Id == 1 && p.Amount == 1200 && p.Reason == "房租涨价"
));
}
[Fact]
public async Task UpdateAsync_记录不存在_应抛出NotFoundException()
{
// Arrange
var request = new UpdatePeriodicRequest
{
Id = 999,
PeriodicType = PeriodicType.Monthly,
Amount = 1000,
Type = TransactionType.Expense,
IsEnabled = true
};
_periodicRepository.GetByIdAsync(999).Returns((TransactionPeriodic?)null);
// Act & Assert
await Assert.ThrowsAsync<NotFoundException>(() => _application.UpdateAsync(request));
}
[Fact]
public async Task UpdateAsync_Repository更新失败_应抛出BusinessException()
{
// Arrange
var existingPeriodic = new TransactionPeriodic
{
Id = 1,
Amount = 1000,
Type = TransactionType.Expense
};
var request = new UpdatePeriodicRequest
{
Id = 1,
PeriodicType = PeriodicType.Monthly,
Amount = 1200,
Type = TransactionType.Expense,
IsEnabled = true
};
_periodicRepository.GetByIdAsync(1).Returns(existingPeriodic);
_periodicService.CalculateNextExecuteTime(Arg.Any<TransactionPeriodic>(), Arg.Any<DateTime>())
.Returns(DateTime.Now.AddMonths(1));
_periodicRepository.UpdateAsync(Arg.Any<TransactionPeriodic>()).Returns(false);
// Act & Assert
await Assert.ThrowsAsync<BusinessException>(() => _application.UpdateAsync(request));
}
#endregion
#region DeleteByIdAsync Tests
[Fact]
public async Task DeleteByIdAsync_成功删除_不应抛出异常()
{
// Arrange
_periodicRepository.DeleteAsync(1).Returns(true);
// Act
await _application.DeleteByIdAsync(1);
// Assert
await _periodicRepository.Received(1).DeleteAsync(1);
}
[Fact]
public async Task DeleteByIdAsync_删除失败_应抛出BusinessException()
{
// Arrange
_periodicRepository.DeleteAsync(999).Returns(false);
// Act & Assert
await Assert.ThrowsAsync<BusinessException>(() => _application.DeleteByIdAsync(999));
}
#endregion
#region ToggleEnabledAsync Tests
[Fact]
public async Task ToggleEnabledAsync_禁用账单_应成功更新状态()
{
// Arrange
var periodic = new TransactionPeriodic
{
Id = 1,
Amount = 1000,
Type = TransactionType.Expense,
IsEnabled = true
};
_periodicRepository.GetByIdAsync(1).Returns(periodic);
_periodicRepository.UpdateAsync(Arg.Any<TransactionPeriodic>()).Returns(true);
// Act
await _application.ToggleEnabledAsync(1, false);
// Assert
await _periodicRepository.Received(1).UpdateAsync(Arg.Is<TransactionPeriodic>(
p => p.Id == 1 && p.IsEnabled == false
));
}
[Fact]
public async Task ToggleEnabledAsync_启用账单_应成功更新状态()
{
// Arrange
var periodic = new TransactionPeriodic
{
Id = 1,
Amount = 1000,
Type = TransactionType.Expense,
IsEnabled = false
};
_periodicRepository.GetByIdAsync(1).Returns(periodic);
_periodicRepository.UpdateAsync(Arg.Any<TransactionPeriodic>()).Returns(true);
// Act
await _application.ToggleEnabledAsync(1, true);
// Assert
await _periodicRepository.Received(1).UpdateAsync(Arg.Is<TransactionPeriodic>(
p => p.Id == 1 && p.IsEnabled == true
));
}
[Fact]
public async Task ToggleEnabledAsync_记录不存在_应抛出NotFoundException()
{
// Arrange
_periodicRepository.GetByIdAsync(999).Returns((TransactionPeriodic?)null);
// Act & Assert
await Assert.ThrowsAsync<NotFoundException>(() => _application.ToggleEnabledAsync(999, false));
}
[Fact]
public async Task ToggleEnabledAsync_更新失败_应抛出BusinessException()
{
// Arrange
var periodic = new TransactionPeriodic
{
Id = 1,
Amount = 1000,
Type = TransactionType.Expense,
IsEnabled = true
};
_periodicRepository.GetByIdAsync(1).Returns(periodic);
_periodicRepository.UpdateAsync(Arg.Any<TransactionPeriodic>()).Returns(false);
// Act & Assert
await Assert.ThrowsAsync<BusinessException>(() => _application.ToggleEnabledAsync(1, false));
}
#endregion
}