Files
aiops/backend/controller/billing_analysis_test.go
T
Logan.Li fd60361d93 chore: 统一本地服务器与远程仓库源码
- 合并 main 与 master 历史并以服务器结构为基线
- 保留迁移、数据保留、AWS 账单与部署安全能力
- 建立 SDD 规格和 CI 验证闭环
2026-08-05 20:03:25 +08:00

103 lines
3.5 KiB
Go

package controller
import (
"encoding/json"
"net/http/httptest"
"testing"
"time"
"ezops/config"
"ezops/model"
"github.com/gin-gonic/gin"
"github.com/glebarez/sqlite"
"gorm.io/gorm"
)
// TestCurrencyExpression 验证 CNY 与 USD 报告币种使用同一组可逆汇率。 // Logan.Li
func TestCurrencyExpression(t *testing.T) {
handler := NewBillingHandler(nil, &config.BillingConfig{
BaseCurrency: "CNY",
ExchangeRates: map[string]float64{"cny": 1, "usd": 7.2},
})
_, cnyArgs, ok := handler.currencyExpression("CNY")
if !ok || len(cnyArgs) != 2 {
t.Fatalf("CNY 汇率表达式无效: ok=%v args=%v", ok, cnyArgs)
}
metadata := handler.reportCurrencyMetadata("USD")
rates := metadata["exchange_rates"].(map[string]float64)
if rates["USD"] != 1 || rates["CNY"] != 0.138889 {
t.Fatalf("USD 折算汇率错误: %+v", rates)
}
if _, _, supported := handler.currencyExpression("EUR"); supported {
t.Fatal("未配置币种不应被接受")
}
}
// TestBillingCurrencyConversionAPI 验证统计接口不会直接混加不同原币金额。 // Logan.Li
func TestBillingCurrencyConversionAPI(t *testing.T) {
gin.SetMode(gin.TestMode)
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
if err != nil {
t.Fatal(err)
}
if err := db.AutoMigrate(&model.CloudBilling{}, &model.BillingAlertRule{}); err != nil {
t.Fatal(err)
}
today := time.Now().Format("2006-01-02")
records := []model.CloudBilling{
{Provider: "tencent", BillingDate: today, Cost: 100, Currency: "CNY", Service: "CVM"},
{Provider: "aws", BillingDate: today, Cost: 10, Currency: "USD", Service: "EC2"},
}
if err := db.Create(&records).Error; err != nil {
t.Fatal(err)
}
handler := NewBillingHandler(db, &config.BillingConfig{BaseCurrency: "CNY", ExchangeRates: map[string]float64{"CNY": 1, "USD": 7.2}})
assertMonthCost := func(currency string, expected float64) {
t.Helper()
recorder := httptest.NewRecorder()
context, _ := gin.CreateTestContext(recorder)
context.Request = httptest.NewRequest("GET", "/?currency="+currency, nil)
handler.GetBillingStats(context)
var response struct {
Code int `json:"code"`
Data struct {
MonthCost float64 `json:"month_cost"`
} `json:"data"`
}
if err := json.Unmarshal(recorder.Body.Bytes(), &response); err != nil {
t.Fatal(err)
}
if response.Code != 200 || mathRound(response.Data.MonthCost, 2) != expected {
t.Fatalf("%s 月费用错误: code=%d cost=%f", currency, response.Code, response.Data.MonthCost)
}
}
assertMonthCost("CNY", 172)
assertMonthCost("USD", 23.89)
}
// TestBuildBillingAnalysisMonths 验证空月份补齐和非 Top 分类合并。 // Logan.Li
func TestBuildBillingAnalysisMonths(t *testing.T) {
start, _ := time.Parse("2006-01", "2026-03")
end, _ := time.Parse("2006-01", "2026-04")
costs := map[string]map[string]float64{
"2026-03": {"CVM": 100, "TCR": 20},
}
months := buildBillingAnalysisMonths(start, end, costs, []string{"CVM", "其他"})
if len(months) != 2 || months[0].Total != 120 || months[0].Items[1].Cost != 20 {
t.Fatalf("月度聚合错误: %+v", months)
}
if months[1].Month != "2026-04" || months[1].Total != 0 {
t.Fatalf("空月份未正确补齐: %+v", months[1])
}
}
// TestBuildBillingAttributions 验证归因变化额与贡献度。 // Logan.Li
func TestBuildBillingAttributions(t *testing.T) {
items := buildBillingAttributions(map[string]float64{"CVM": 100}, map[string]float64{"CVM": 180, "TCR": 20})
if len(items) != 2 || items[0].Name != "CVM" || items[0].Delta != 80 || items[0].Contribution != 80 {
t.Fatalf("费用归因错误: %+v", items)
}
}