Files
EmailBill/WebApi.Test/Transaction/TransactionStatisticsServiceTest.cs
SunCheng e93c3d6bae
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 27s
Docker Build & Deploy / Deploy to Production (push) Successful in 9s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 2s
Docker Build & Deploy / WeChat Notification (push) Successful in 2s
测试覆盖率
2026-01-28 17:00:58 +08:00

973 lines
30 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Service.Transaction;
namespace WebApi.Test.Transaction;
public class TransactionStatisticsServiceTest : BaseTest
{
private readonly ITransactionRecordRepository _transactionRepository = Substitute.For<ITransactionRecordRepository>();
private readonly ITransactionStatisticsService _service;
public TransactionStatisticsServiceTest()
{
// 默认配置 QueryAsync 返回空列表
_transactionRepository.QueryAsync(
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<DateTime>(),
Arg.Any<DateTime>(),
Arg.Any<TransactionType>(),
Arg.Any<string[]>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<bool>())
.ReturnsForAnyArgs(new List<TransactionRecord>());
_service = new TransactionStatisticsService(
_transactionRepository
);
}
private void ConfigureQueryAsync(List<TransactionRecord> data)
{
_transactionRepository.QueryAsync(
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<DateTime>(),
Arg.Any<DateTime>(),
Arg.Any<TransactionType>(),
Arg.Any<string[]>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<bool>())
.ReturnsForAnyArgs(data);
}
[Fact]
public async Task GetDailyStatisticsAsync_基本测试()
{
// Arrange
var year = 2024;
var month = 1;
var testData = new List<TransactionRecord>
{
new()
{
Id = 1,
OccurredAt = new DateTime(2024, 1, 1, 10, 0, 0),
Amount = -100m,
Type = TransactionType.Expense,
Classify = "餐饮",
Reason = "午餐"
},
new()
{
Id = 2,
OccurredAt = new DateTime(2024, 1, 1, 15, 0, 0),
Amount = -50m,
Type = TransactionType.Expense,
Classify = "交通",
Reason = "地铁"
},
new()
{
Id = 3,
OccurredAt = new DateTime(2024, 1, 2, 9, 0, 0),
Amount = 5000m,
Type = TransactionType.Income,
Classify = "工资",
Reason = "工资收入"
}
};
ConfigureQueryAsync(testData);
// Act
var result = await _service.GetDailyStatisticsAsync(year, month);
// Assert
result.Should().HaveCount(2);
result.Should().ContainKey("2024-01-01");
result.Should().ContainKey("2024-01-02");
result["2024-01-01"].count.Should().Be(2);
result["2024-01-01"].expense.Should().Be(150m);
result["2024-01-01"].income.Should().Be(0m);
result["2024-01-02"].count.Should().Be(1);
result["2024-01-02"].expense.Should().Be(0m);
result["2024-01-02"].income.Should().Be(5000m);
}
[Fact]
public async Task GetDailyStatisticsAsync_带储蓄分类()
{
// Arrange
var year = 2024;
var month = 1;
var savingClassify = "投资,存款";
var testData = new List<TransactionRecord>
{
new()
{
Id = 1,
OccurredAt = new DateTime(2024, 1, 1, 10, 0, 0),
Amount = -100m,
Type = TransactionType.Expense,
Classify = "餐饮",
Reason = "午餐"
},
new()
{
Id = 2,
OccurredAt = new DateTime(2024, 1, 1, 15, 0, 0),
Amount = -1000m,
Type = TransactionType.Expense,
Classify = "投资",
Reason = "基金定投"
},
new()
{
Id = 3,
OccurredAt = new DateTime(2024, 1, 2, 9, 0, 0),
Amount = -500m,
Type = TransactionType.Expense,
Classify = "存款",
Reason = "银行存款"
}
};
_transactionRepository.QueryAsync(
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<DateTime>(),
Arg.Any<DateTime>(),
Arg.Any<TransactionType>(),
Arg.Any<string[]>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<bool>())
.ReturnsForAnyArgs(testData);
// Act
var result = await _service.GetDailyStatisticsAsync(year, month, savingClassify);
// Assert
result.Should().HaveCount(2);
result["2024-01-01"].count.Should().Be(2);
result["2024-01-01"].expense.Should().Be(1100m);
result["2024-01-01"].income.Should().Be(0m);
result["2024-01-01"].saving.Should().Be(1000m);
result["2024-01-02"].count.Should().Be(1);
result["2024-01-02"].expense.Should().Be(500m);
result["2024-01-02"].income.Should().Be(0m);
result["2024-01-02"].saving.Should().Be(500m);
}
[Fact]
public async Task GetDailyStatisticsByRangeAsync_基本测试()
{
// Arrange
var startDate = new DateTime(2024, 1, 1);
var endDate = new DateTime(2024, 1, 5);
var testData = new List<TransactionRecord>
{
new()
{
Id = 1,
OccurredAt = new DateTime(2024, 1, 3, 10, 0, 0),
Amount = -100m,
Type = TransactionType.Expense,
Classify = "餐饮",
Reason = "午餐"
}
};
_transactionRepository.QueryAsync(
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<DateTime>(),
Arg.Any<DateTime>(),
Arg.Any<TransactionType>(),
Arg.Any<string[]>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<bool>())
.ReturnsForAnyArgs(testData);
// Act
var result = await _service.GetDailyStatisticsByRangeAsync(startDate, endDate);
// Assert
result.Should().HaveCount(1);
result.Should().ContainKey("2024-01-03");
result["2024-01-03"].count.Should().Be(1);
result["2024-01-03"].expense.Should().Be(100m);
}
[Fact]
public async Task GetMonthlyStatisticsAsync_基本测试()
{
// Arrange
var year = 2024;
var month = 1;
var testData = new List<TransactionRecord>
{
new()
{
Id = 1,
OccurredAt = new DateTime(2024, 1, 1, 10, 0, 0),
Amount = -100m,
Type = TransactionType.Expense,
Classify = "餐饮",
Reason = "午餐"
},
new()
{
Id = 2,
OccurredAt = new DateTime(2024, 1, 2, 15, 0, 0),
Amount = -50m,
Type = TransactionType.Expense,
Classify = "交通",
Reason = "地铁"
},
new()
{
Id = 3,
OccurredAt = new DateTime(2024, 1, 5, 9, 0, 0),
Amount = 5000m,
Type = TransactionType.Income,
Classify = "工资",
Reason = "工资收入"
},
new()
{
Id = 4,
OccurredAt = new DateTime(2024, 1, 10, 9, 0, 0),
Amount = 2000m,
Type = TransactionType.Income,
Classify = "奖金",
Reason = "奖金收入"
}
};
_transactionRepository.QueryAsync(
year,
month,
Arg.Any<DateTime>(),
Arg.Any<DateTime>(),
Arg.Any<TransactionType>(),
Arg.Any<string[]>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<bool>())
.ReturnsForAnyArgs(testData);
// Act
var result = await _service.GetMonthlyStatisticsAsync(year, month);
// Assert
result.Year.Should().Be(year);
result.Month.Should().Be(month);
result.TotalExpense.Should().Be(150m);
result.TotalIncome.Should().Be(7000m);
result.Balance.Should().Be(6850m);
result.ExpenseCount.Should().Be(2);
result.IncomeCount.Should().Be(2);
result.TotalCount.Should().Be(4);
}
[Fact]
public async Task GetMonthlyStatisticsAsync_无数据()
{
// Arrange
var year = 2024;
var month = 2;
_transactionRepository.QueryAsync(
year,
month,
Arg.Any<DateTime>(),
Arg.Any<DateTime>(),
Arg.Any<TransactionType>(),
Arg.Any<string[]>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<bool>())
.Returns(new List<TransactionRecord>());
// Act
var result = await _service.GetMonthlyStatisticsAsync(year, month);
// Assert
result.Year.Should().Be(year);
result.Month.Should().Be(month);
result.TotalExpense.Should().Be(0m);
result.TotalIncome.Should().Be(0m);
result.Balance.Should().Be(0m);
result.ExpenseCount.Should().Be(0);
result.IncomeCount.Should().Be(0);
result.TotalCount.Should().Be(0);
}
[Fact]
public async Task GetCategoryStatisticsAsync_支出分类()
{
// Arrange
var year = 2024;
var month = 1;
var type = TransactionType.Expense;
var testData = new List<TransactionRecord>
{
new()
{
Id = 1,
OccurredAt = new DateTime(2024, 1, 1, 10, 0, 0),
Amount = -100m,
Type = TransactionType.Expense,
Classify = "餐饮",
Reason = "午餐"
},
new()
{
Id = 2,
OccurredAt = new DateTime(2024, 1, 2, 15, 0, 0),
Amount = -50m,
Type = TransactionType.Expense,
Classify = "餐饮",
Reason = "晚餐"
},
new()
{
Id = 3,
OccurredAt = new DateTime(2024, 1, 3, 9, 0, 0),
Amount = -200m,
Type = TransactionType.Expense,
Classify = "交通",
Reason = "打车"
},
new()
{
Id = 4,
OccurredAt = new DateTime(2024, 1, 5, 9, 0, 0),
Amount = 5000m,
Type = TransactionType.Income,
Classify = "工资",
Reason = "工资收入"
}
};
_transactionRepository.QueryAsync(
year,
month,
Arg.Any<DateTime>(),
Arg.Any<DateTime>(),
type,
Arg.Any<string[]>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<bool>())
.ReturnsForAnyArgs(testData);
// Act
var result = await _service.GetCategoryStatisticsAsync(year, month, type);
// Assert
result.Should().HaveCount(2);
var dining = result.First(c => c.Classify == "餐饮");
dining.Amount.Should().Be(150m);
dining.Count.Should().Be(2);
dining.Percent.Should().Be(42.9m);
var transport = result.First(c => c.Classify == "交通");
transport.Amount.Should().Be(200m);
transport.Count.Should().Be(1);
transport.Percent.Should().Be(57.1m);
}
[Fact]
public async Task GetCategoryStatisticsAsync_收入分类()
{
// Arrange
var year = 2024;
var month = 1;
var type = TransactionType.Income;
var testData = new List<TransactionRecord>
{
new()
{
Id = 1,
OccurredAt = new DateTime(2024, 1, 1, 10, 0, 0),
Amount = 5000m,
Type = TransactionType.Income,
Classify = "工资",
Reason = "工资收入"
},
new()
{
Id = 2,
OccurredAt = new DateTime(2024, 1, 2, 15, 0, 0),
Amount = 1000m,
Type = TransactionType.Income,
Classify = "奖金",
Reason = "绩效奖金"
},
new()
{
Id = 3,
OccurredAt = new DateTime(2024, 1, 3, 9, 0, 0),
Amount = -100m,
Type = TransactionType.Expense,
Classify = "餐饮",
Reason = "午餐"
}
};
_transactionRepository.QueryAsync(
year,
month,
Arg.Any<DateTime>(),
Arg.Any<DateTime>(),
type,
Arg.Any<string[]>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<bool>())
.ReturnsForAnyArgs(testData);
// Act
var result = await _service.GetCategoryStatisticsAsync(year, month, type);
// Assert
result.Should().HaveCount(2);
var salary = result.First(c => c.Classify == "工资");
salary.Amount.Should().Be(5000m);
salary.Count.Should().Be(1);
salary.Percent.Should().Be(83.3m);
var bonus = result.First(c => c.Classify == "奖金");
bonus.Amount.Should().Be(1000m);
bonus.Count.Should().Be(1);
bonus.Percent.Should().Be(16.7m);
}
[Fact]
public async Task GetTrendStatisticsAsync_多个月份()
{
// Arrange
var startYear = 2024;
var startMonth = 1;
var monthCount = 3;
var mockData = new Dictionary<int, List<TransactionRecord>>
{
[1] = new List<TransactionRecord>
{
new() { Id = 1, OccurredAt = new DateTime(2024, 1, 1), Amount = -1000m, Type = TransactionType.Expense },
new() { Id = 2, OccurredAt = new DateTime(2024, 1, 5), Amount = 5000m, Type = TransactionType.Income }
},
[2] = new List<TransactionRecord>
{
new() { Id = 3, OccurredAt = new DateTime(2024, 2, 1), Amount = -1500m, Type = TransactionType.Expense },
new() { Id = 4, OccurredAt = new DateTime(2024, 2, 5), Amount = 5000m, Type = TransactionType.Income }
},
[3] = new List<TransactionRecord>
{
new() { Id = 5, OccurredAt = new DateTime(2024, 3, 1), Amount = -2000m, Type = TransactionType.Expense },
new() { Id = 6, OccurredAt = new DateTime(2024, 3, 5), Amount = 5000m, Type = TransactionType.Income }
}
};
_transactionRepository.QueryAsync(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<DateTime>(), Arg.Any<DateTime>(), Arg.Any<TransactionType>(), Arg.Any<string[]>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<bool>())
.Returns(args =>
{
var month = (int)args[1];
if (mockData.ContainsKey(month))
{
return mockData[month];
}
return new List<TransactionRecord>();
});
// Act
var result = await _service.GetTrendStatisticsAsync(startYear, startMonth, monthCount);
// Assert
result.Should().HaveCount(3);
result[0].Year.Should().Be(2024);
result[0].Month.Should().Be(1);
result[0].Expense.Should().Be(1000m);
result[0].Income.Should().Be(5000m);
result[0].Balance.Should().Be(4000m);
result[1].Year.Should().Be(2024);
result[1].Month.Should().Be(2);
result[1].Expense.Should().Be(1500m);
result[1].Income.Should().Be(5000m);
result[1].Balance.Should().Be(3500m);
result[2].Year.Should().Be(2024);
result[2].Month.Should().Be(3);
result[2].Expense.Should().Be(2000m);
result[2].Income.Should().Be(5000m);
result[2].Balance.Should().Be(3000m);
}
[Fact]
public async Task GetTrendStatisticsAsync_跨年()
{
// Arrange
var startYear = 2024;
var startMonth = 11;
var monthCount = 4;
_transactionRepository.QueryAsync(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<DateTime>(), Arg.Any<DateTime>(), Arg.Any<TransactionType>(), Arg.Any<string[]>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<bool>())
.Returns(new List<TransactionRecord>());
// Act
var result = await _service.GetTrendStatisticsAsync(startYear, startMonth, monthCount);
// Assert
result.Should().HaveCount(4);
result[0].Year.Should().Be(2024);
result[0].Month.Should().Be(11);
result[1].Year.Should().Be(2024);
result[1].Month.Should().Be(12);
result[2].Year.Should().Be(2025);
result[2].Month.Should().Be(1);
result[3].Year.Should().Be(2025);
result[3].Month.Should().Be(2);
}
[Fact]
public async Task GetReasonGroupsAsync_基本测试()
{
// Arrange
var testData = new List<TransactionRecord>
{
new()
{
Id = 1,
Reason = "麦当劳",
Classify = "",
Amount = -50m,
Type = TransactionType.Expense,
OccurredAt = new DateTime(2024, 1, 1)
},
new()
{
Id = 2,
Reason = "麦当劳",
Classify = "",
Amount = -80m,
Type = TransactionType.Expense,
OccurredAt = new DateTime(2024, 1, 2)
},
new()
{
Id = 3,
Reason = "肯德基",
Classify = "",
Amount = -60m,
Type = TransactionType.Expense,
OccurredAt = new DateTime(2024, 1, 3)
},
new()
{
Id = 4,
Reason = "麦当劳",
Classify = "快餐",
Amount = -45m,
Type = TransactionType.Expense,
OccurredAt = new DateTime(2024, 1, 4)
}
};
_transactionRepository.QueryAsync(
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<DateTime>(),
Arg.Any<DateTime>(),
Arg.Any<TransactionType>(),
Arg.Any<string[]>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<bool>())
.ReturnsForAnyArgs(testData);
// Act
var (list, total) = await _service.GetReasonGroupsAsync();
// Assert
total.Should().Be(2);
list.Should().HaveCount(2);
var mcdonalds = list.First(g => g.Reason == "麦当劳");
mcdonalds.Count.Should().Be(2);
mcdonalds.TotalAmount.Should().Be(130m);
mcdonalds.SampleType.Should().Be(TransactionType.Expense);
mcdonalds.SampleClassify.Should().Be("");
mcdonalds.TransactionIds.Should().Contain(1L);
mcdonalds.TransactionIds.Should().Contain(2L);
var kfc = list.First(g => g.Reason == "肯德基");
kfc.Count.Should().Be(1);
kfc.TotalAmount.Should().Be(60m);
}
[Fact]
public async Task GetClassifiedByKeywordsWithScoreAsync_基本匹配()
{
// Arrange
var keywords = new List<string> { "餐饮", "午餐" };
var testData = new List<TransactionRecord>
{
new()
{
Id = 1,
Reason = "今天午餐吃得很饱",
Classify = "餐饮",
OccurredAt = new DateTime(2024, 1, 1),
Amount = -50m
},
new()
{
Id = 2,
Reason = "餐饮支出",
Classify = "餐饮",
OccurredAt = new DateTime(2024, 1, 2),
Amount = -80m
},
new()
{
Id = 3,
Reason = "交通费",
Classify = "交通",
OccurredAt = new DateTime(2024, 1, 3),
Amount = -10m
}
};
_transactionRepository.GetClassifiedByKeywordsAsync(Arg.Any<List<string>>(), Arg.Any<int>())
.ReturnsForAnyArgs(testData);
// Act
var result = await _service.GetClassifiedByKeywordsWithScoreAsync(keywords, minMatchRate: 0.3, limit: 10);
// Assert
result.Should().HaveCount(2);
var first = result[0];
// 第一个结果应该是相关性分数最高的,可能是 Id=2"今天午餐吃得很饱"匹配两个关键词)
first.record.Id.Should().BeOneOf(1L, 2L);
first.relevanceScore.Should().BeGreaterThan(0.5);
var second = result[1];
second.record.Id.Should().BeOneOf(1L, 2L);
second.record.Id.Should().NotBe(first.record.Id);
second.relevanceScore.Should().BeGreaterThan(0.3);
}
[Fact]
public async Task GetClassifiedByKeywordsWithScoreAsync_精确匹配加分()
{
// Arrange
var keywords = new List<string> { "午餐" };
var testData = new List<TransactionRecord>
{
new()
{
Id = 1,
Reason = "午餐",
Classify = "餐饮",
OccurredAt = new DateTime(2024, 1, 1),
Amount = -50m
},
new()
{
Id = 2,
Reason = "今天中午吃了一顿午餐",
Classify = "餐饮",
OccurredAt = new DateTime(2024, 1, 2),
Amount = -80m
}
};
_transactionRepository.GetClassifiedByKeywordsAsync(Arg.Any<List<string>>(), Arg.Any<int>())
.ReturnsForAnyArgs(testData);
// Act
var result = await _service.GetClassifiedByKeywordsWithScoreAsync(keywords, minMatchRate: 0.3, limit: 10);
// Assert
result.Should().HaveCount(2);
// 精确匹配应该得分更高
result[0].record.Id.Should().Be(1);
result[0].relevanceScore.Should().BeGreaterThan(result[1].relevanceScore);
}
[Fact]
public async Task GetFilteredTrendStatisticsAsync_按日分组()
{
// Arrange
var startDate = new DateTime(2024, 1, 1);
var endDate = new DateTime(2024, 1, 5);
var type = TransactionType.Expense;
var classifies = new[] { "餐饮", "交通" };
var testData = new List<TransactionRecord>
{
new()
{
Id = 1,
OccurredAt = new DateTime(2024, 1, 1, 10, 0, 0),
Amount = -100m,
Type = TransactionType.Expense,
Classify = "餐饮"
},
new()
{
Id = 2,
OccurredAt = new DateTime(2024, 1, 1, 15, 0, 0),
Amount = -50m,
Type = TransactionType.Expense,
Classify = "交通"
},
new()
{
Id = 3,
OccurredAt = new DateTime(2024, 1, 2, 10, 0, 0),
Amount = -80m,
Type = TransactionType.Expense,
Classify = "餐饮"
}
};
_transactionRepository.QueryAsync(
Arg.Any<int>(),
Arg.Any<int>(),
startDate,
endDate,
type,
classifies,
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<bool>())
.ReturnsForAnyArgs(testData);
// Act
var result = await _service.GetFilteredTrendStatisticsAsync(startDate, endDate, type, classifies, groupByMonth: false);
// Assert
result.Should().HaveCount(2);
result.Should().ContainKey(new DateTime(2024, 1, 1));
result.Should().ContainKey(new DateTime(2024, 1, 2));
result[new DateTime(2024, 1, 1)].Should().Be(150m);
result[new DateTime(2024, 1, 2)].Should().Be(80m);
}
[Fact]
public async Task GetFilteredTrendStatisticsAsync_按月分组()
{
// Arrange
var startDate = new DateTime(2024, 1, 1);
var endDate = new DateTime(2024, 3, 31);
var type = TransactionType.Expense;
var classifies = new[] { "餐饮" };
var testData = new List<TransactionRecord>
{
new()
{
Id = 1,
OccurredAt = new DateTime(2024, 1, 15),
Amount = -1000m,
Type = TransactionType.Expense,
Classify = "餐饮"
},
new()
{
Id = 2,
OccurredAt = new DateTime(2024, 2, 15),
Amount = -1500m,
Type = TransactionType.Expense,
Classify = "餐饮"
},
new()
{
Id = 3,
OccurredAt = new DateTime(2024, 3, 15),
Amount = -2000m,
Type = TransactionType.Expense,
Classify = "餐饮"
}
};
_transactionRepository.QueryAsync(
Arg.Any<int>(),
Arg.Any<int>(),
startDate,
endDate,
type,
classifies,
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<bool>())
.ReturnsForAnyArgs(testData);
// Act
var result = await _service.GetFilteredTrendStatisticsAsync(startDate, endDate, type, classifies, groupByMonth: true);
// Assert
result.Should().HaveCount(3);
result.Should().ContainKey(new DateTime(2024, 1, 1));
result.Should().ContainKey(new DateTime(2024, 2, 1));
result.Should().ContainKey(new DateTime(2024, 3, 1));
result[new DateTime(2024, 1, 1)].Should().Be(1000m);
result[new DateTime(2024, 2, 1)].Should().Be(1500m);
result[new DateTime(2024, 3, 1)].Should().Be(2000m);
}
[Fact]
public async Task GetAmountGroupByClassifyAsync_基本测试()
{
// Arrange
var startTime = new DateTime(2024, 1, 1);
var endTime = new DateTime(2024, 1, 31);
var testData = new List<TransactionRecord>
{
new()
{
Id = 1,
Amount = -100m,
Type = TransactionType.Expense,
Classify = "餐饮"
},
new()
{
Id = 2,
Amount = -50m,
Type = TransactionType.Expense,
Classify = "餐饮"
},
new()
{
Id = 3,
Amount = 5000m,
Type = TransactionType.Income,
Classify = "工资"
},
new()
{
Id = 4,
Amount = -200m,
Type = TransactionType.Expense,
Classify = "交通"
}
};
_transactionRepository.QueryAsync(
Arg.Any<int>(),
Arg.Any<int>(),
startTime,
endTime,
Arg.Any<TransactionType>(),
Arg.Any<string[]>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<bool>())
.ReturnsForAnyArgs(testData);
// Act
var result = await _service.GetAmountGroupByClassifyAsync(startTime, endTime);
// Assert
result.Should().HaveCount(3);
result[("餐饮", TransactionType.Expense)].Should().Be(-150m);
result[("工资", TransactionType.Income)].Should().Be(5000m);
result[("交通", TransactionType.Expense)].Should().Be(-200m);
}
[Fact]
public async Task GetAmountGroupByClassifyAsync_相同分类不同类型()
{
// Arrange
var startTime = new DateTime(2024, 1, 1);
var endTime = new DateTime(2024, 1, 31);
var testData = new List<TransactionRecord>
{
new()
{
Id = 1,
Amount = -100m,
Type = TransactionType.Expense,
Classify = "兼职"
},
new()
{
Id = 2,
Amount = 500m,
Type = TransactionType.Income,
Classify = "兼职"
}
};
_transactionRepository.QueryAsync(
Arg.Any<int>(),
Arg.Any<int>(),
startTime,
endTime,
Arg.Any<TransactionType>(),
Arg.Any<string[]>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<bool>())
.ReturnsForAnyArgs(testData);
// Act
var result = await _service.GetAmountGroupByClassifyAsync(startTime, endTime);
// Assert
result.Should().HaveCount(2);
result[("兼职", TransactionType.Expense)].Should().Be(-100m);
result[("兼职", TransactionType.Income)].Should().Be(500m);
}
}