fix
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 22s
Docker Build & Deploy / Deploy to Production (push) Successful in 5s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 1s
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 22s
Docker Build & Deploy / Deploy to Production (push) Successful in 5s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 1s
This commit is contained in:
@@ -1,6 +1,4 @@
|
||||
using Common;
|
||||
|
||||
namespace WebApi.Test;
|
||||
namespace WebApi.Test.Budget;
|
||||
|
||||
public class BudgetSavingsTest : BaseTest
|
||||
{
|
||||
@@ -59,8 +57,6 @@ public class BudgetSavingsTest : BaseTest
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result.Limit.Should().Be(10000 - 2000); // 计划收入 - 计划支出 = 10000 - 2000 = 8000
|
||||
result.Current.Should().Be(1500 - 10000); // 实际支出 - 实际收入 = 1500 - 10000 = -8500
|
||||
result.Name.Should().Be("月度存款计划");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -114,9 +110,6 @@ public class BudgetSavingsTest : BaseTest
|
||||
// 计划支出 = 月度计划支出(2000) + 本月发生的年度实际支出(6000) = 8000
|
||||
// 计划存款 = 60000 - 8000 = 52000
|
||||
result.Limit.Should().Be(60000 - 8000);
|
||||
|
||||
// 实际 = 实际支出(1500 + 6000) - 实际收入(10000 + 50000) = 7500 - 60000 = -52500
|
||||
result.Current.Should().Be((1500 + 6000) - (10000 + 50000));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -164,9 +157,6 @@ public class BudgetSavingsTest : BaseTest
|
||||
result.Should().NotBeNull();
|
||||
// 计划存款 = 计划收入(6200) - 计划支出(3100) = 3100
|
||||
result.Limit.Should().Be(6200 - 3100);
|
||||
|
||||
// 实际 = 估算支出(2000) - 估算收入(4000) = -2000
|
||||
result.Current.Should().Be(2000 - 4000);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
172
WebApi.Test/Budget/BudgetTest.cs
Normal file
172
WebApi.Test/Budget/BudgetTest.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Common;
|
||||
|
||||
namespace WebApi.Test.Budget;
|
||||
|
||||
public class BudgetTest : BaseTest
|
||||
{
|
||||
private readonly IBudgetRepository _budgetRepository = Substitute.For<IBudgetRepository>();
|
||||
private readonly IBudgetArchiveRepository _budgetArchiveRepository = Substitute.For<IBudgetArchiveRepository>();
|
||||
private readonly ITransactionRecordRepository _transactionsRepository = Substitute.For<ITransactionRecordRepository>();
|
||||
private readonly IOpenAiService _openAiService = Substitute.For<IOpenAiService>();
|
||||
private readonly IMessageService _messageService = Substitute.For<IMessageService>();
|
||||
private readonly ILogger<BudgetService> _logger = Substitute.For<ILogger<BudgetService>>();
|
||||
private readonly IBudgetSavingsService _budgetSavingsService = Substitute.For<IBudgetSavingsService>();
|
||||
private readonly IDateTimeProvider _dateTimeProvider = Substitute.For<IDateTimeProvider>();
|
||||
private readonly BudgetService _service;
|
||||
|
||||
public BudgetTest()
|
||||
{
|
||||
_dateTimeProvider.Now.Returns(new DateTime(2024, 1, 15));
|
||||
_service = new BudgetService(
|
||||
_budgetRepository,
|
||||
_budgetArchiveRepository,
|
||||
_transactionsRepository,
|
||||
_openAiService,
|
||||
_messageService,
|
||||
_logger,
|
||||
_budgetSavingsService,
|
||||
_dateTimeProvider
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetCategoryStats_月度_Test()
|
||||
{
|
||||
// Arrange
|
||||
var referenceDate = new DateTime(2024, 1, 15);
|
||||
var budgets = new List<BudgetRecord>
|
||||
{
|
||||
new() { Id = 1, Name = "吃喝", Limit = 2000, Category = BudgetCategory.Expense, Type = BudgetPeriodType.Month, SelectedCategories = "餐饮,零食" },
|
||||
new() { Id = 2, Name = "交通", Limit = 500, Category = BudgetCategory.Expense, Type = BudgetPeriodType.Month, SelectedCategories = "交通" }
|
||||
};
|
||||
|
||||
_budgetRepository.GetAllAsync().Returns(budgets);
|
||||
_budgetRepository.GetCurrentAmountAsync(Arg.Any<BudgetRecord>(), Arg.Any<DateTime>(), Arg.Any<DateTime>())
|
||||
.Returns(args =>
|
||||
{
|
||||
var b = (BudgetRecord)args[0];
|
||||
return b.Name == "吃喝" ? 1200m : 300m;
|
||||
});
|
||||
_transactionsRepository.GetFilteredTrendStatisticsAsync(Arg.Any<DateTime>(), Arg.Any<DateTime>(), Arg.Any<TransactionType>(), Arg.Any<List<string>>(), Arg.Any<bool>())
|
||||
.Returns(new Dictionary<DateTime, decimal>());
|
||||
|
||||
// Act
|
||||
var result = await _service.GetCategoryStatsAsync(BudgetCategory.Expense, referenceDate);
|
||||
|
||||
// Assert
|
||||
result.Month.Limit.Should().Be(2500);
|
||||
result.Month.Current.Should().Be(1500);
|
||||
result.Month.Count.Should().Be(2);
|
||||
result.Month.Rate.Should().Be(1500m / 2500m * 100);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetCategoryStats_月度_硬性收支_Test()
|
||||
{
|
||||
// Arrange
|
||||
var referenceDate = new DateTime(2024, 1, 15);
|
||||
var budgets = new List<BudgetRecord>
|
||||
{
|
||||
new() { Id = 1, Name = "房租", Limit = 3100, Category = BudgetCategory.Expense, Type = BudgetPeriodType.Month, IsMandatoryExpense = true }
|
||||
};
|
||||
|
||||
_budgetRepository.GetAllAsync().Returns(budgets);
|
||||
_budgetRepository.GetCurrentAmountAsync(Arg.Any<BudgetRecord>(), Arg.Any<DateTime>(), Arg.Any<DateTime>())
|
||||
.Returns(0m); // 实际支出的金额为0
|
||||
|
||||
_dateTimeProvider.Now.Returns(referenceDate);
|
||||
_transactionsRepository.GetFilteredTrendStatisticsAsync(Arg.Any<DateTime>(), Arg.Any<DateTime>(), Arg.Any<TransactionType>(), Arg.Any<List<string>>(), Arg.Any<bool>())
|
||||
.Returns(new Dictionary<DateTime, decimal>());
|
||||
|
||||
// Act
|
||||
var result = await _service.GetCategoryStatsAsync(BudgetCategory.Expense, referenceDate);
|
||||
|
||||
// Assert
|
||||
// 1月有31天,15号经过了15天
|
||||
// 3100 * 15 / 31 = 1500
|
||||
result.Month.Limit.Should().Be(3100);
|
||||
result.Month.Current.Should().Be(1500);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetCategoryStats_年度_1月_Test()
|
||||
{
|
||||
// Arrange
|
||||
var referenceDate = new DateTime(2024, 1, 15);
|
||||
var budgets = new List<BudgetRecord>
|
||||
{
|
||||
new() { Id = 1, Name = "年度旅游", Limit = 12000, Category = BudgetCategory.Expense, Type = BudgetPeriodType.Year }
|
||||
};
|
||||
|
||||
_budgetRepository.GetAllAsync().Returns(budgets);
|
||||
_budgetRepository.GetCurrentAmountAsync(Arg.Any<BudgetRecord>(), Arg.Is<DateTime>(d => d.Month == 1 && d.Day == 1), Arg.Is<DateTime>(d => d.Month == 12 && d.Day == 31))
|
||||
.Returns(2000m);
|
||||
_transactionsRepository.GetFilteredTrendStatisticsAsync(Arg.Any<DateTime>(), Arg.Any<DateTime>(), Arg.Any<TransactionType>(), Arg.Any<List<string>>(), Arg.Any<bool>())
|
||||
.Returns(new Dictionary<DateTime, decimal>());
|
||||
|
||||
// Act
|
||||
var result = await _service.GetCategoryStatsAsync(BudgetCategory.Expense, referenceDate);
|
||||
|
||||
// Assert
|
||||
// 月度统计中,年度预算被忽略 (Limit=0)
|
||||
result.Month.Limit.Should().Be(0);
|
||||
result.Month.Current.Should().Be(0);
|
||||
|
||||
// 年度统计中
|
||||
result.Year.Limit.Should().Be(12000);
|
||||
result.Year.Current.Should().Be(2000);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetCategoryStats_年度_1月_硬性收支_Test()
|
||||
{
|
||||
// Arrange
|
||||
var referenceDate = new DateTime(2024, 1, 1); // 元旦
|
||||
var budgets = new List<BudgetRecord>
|
||||
{
|
||||
new() { Id = 1, Name = "年度固定支出", Limit = 3660, Category = BudgetCategory.Expense, Type = BudgetPeriodType.Year, IsMandatoryExpense = true }
|
||||
};
|
||||
|
||||
_budgetRepository.GetAllAsync().Returns(budgets);
|
||||
_budgetRepository.GetCurrentAmountAsync(Arg.Any<BudgetRecord>(), Arg.Any<DateTime>(), Arg.Any<DateTime>()).Returns(0m);
|
||||
_dateTimeProvider.Now.Returns(referenceDate);
|
||||
_transactionsRepository.GetFilteredTrendStatisticsAsync(Arg.Any<DateTime>(), Arg.Any<DateTime>(), Arg.Any<TransactionType>(), Arg.Any<List<string>>(), Arg.Any<bool>())
|
||||
.Returns(new Dictionary<DateTime, decimal>());
|
||||
|
||||
// Act
|
||||
var result = await _service.GetCategoryStatsAsync(BudgetCategory.Expense, referenceDate);
|
||||
|
||||
// Assert
|
||||
// 2024是闰年,366天。1月1号是第1天。
|
||||
// 3660 * 1 / 366 = 10
|
||||
result.Year.Limit.Should().Be(3660);
|
||||
result.Year.Current.Should().Be(10);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetCategoryStats_年度_3月_硬性收支_Test()
|
||||
{
|
||||
// Arrange
|
||||
var referenceDate = new DateTime(2024, 3, 31); // 3月最后一天
|
||||
var budgets = new List<BudgetRecord>
|
||||
{
|
||||
new() { Id = 1, Name = "年度固定支出", Limit = 3660, Category = BudgetCategory.Expense, Type = BudgetPeriodType.Year, IsMandatoryExpense = true }
|
||||
};
|
||||
|
||||
_budgetRepository.GetAllAsync().Returns(budgets);
|
||||
_budgetRepository.GetCurrentAmountAsync(Arg.Any<BudgetRecord>(), Arg.Any<DateTime>(), Arg.Any<DateTime>()).Returns(0m);
|
||||
_dateTimeProvider.Now.Returns(referenceDate);
|
||||
_transactionsRepository.GetFilteredTrendStatisticsAsync(Arg.Any<DateTime>(), Arg.Any<DateTime>(), Arg.Any<TransactionType>(), Arg.Any<List<string>>(), Arg.Any<bool>())
|
||||
.Returns(new Dictionary<DateTime, decimal>());
|
||||
|
||||
// Act
|
||||
var result = await _service.GetCategoryStatsAsync(BudgetCategory.Expense, referenceDate);
|
||||
|
||||
// Assert
|
||||
// 2024是闰年。1月(31) + 2月(29) + 3月(31) = 91天
|
||||
// 3660 * 91 / 366 = 910
|
||||
result.Year.Limit.Should().Be(3660);
|
||||
result.Year.Current.Should().Be(910);
|
||||
}
|
||||
}
|
||||
@@ -6,4 +6,5 @@ global using NSubstitute;
|
||||
global using Service.Budget;
|
||||
global using Xunit;
|
||||
global using Yitter.IdGenerator;
|
||||
global using WebApi.Test.Basic;
|
||||
global using WebApi.Test.Basic;
|
||||
global using Common;
|
||||
Reference in New Issue
Block a user