173 lines
5.3 KiB
Go
173 lines
5.3 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"math"
|
|
"testing"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"oci-portal/internal/model"
|
|
"oci-portal/internal/oci"
|
|
)
|
|
|
|
func costItem(day string, amount float32, currency string) oci.CostItem {
|
|
t, _ := time.Parse("2006-01-02", day)
|
|
return oci.CostItem{TimeStart: &t, ComputedAmount: amount, Currency: currency}
|
|
}
|
|
|
|
func TestRunCostTaskSkipsFreeAndUpserts(t *testing.T) {
|
|
// 日期取相对当前的近两天:任务查近 7 天,窗口外的行会被 Costs 过滤
|
|
day1 := time.Now().UTC().AddDate(0, 0, -2).Format("2006-01-02")
|
|
day2 := time.Now().UTC().AddDate(0, 0, -1).Format("2006-01-02")
|
|
client := &fakeClient{
|
|
tenancy: oci.TenancyInfo{Name: "acme", HomeRegionKey: "FRA"},
|
|
costItems: []oci.CostItem{
|
|
costItem(day1, 5.2, "USD"),
|
|
costItem(day1, 1.4, "USD"),
|
|
costItem(day2, 6.3, "USD"),
|
|
},
|
|
}
|
|
tasks, configs, db := newTaskEnv(t, client)
|
|
ctx := context.Background()
|
|
if _, err := configs.Import(ctx, trialImportInput()); err != nil {
|
|
t.Fatalf("import: %v", err)
|
|
}
|
|
free := trialImportInput()
|
|
free.Alias = "免费号"
|
|
if _, err := configs.Import(ctx, free); err != nil {
|
|
t.Fatalf("import free: %v", err)
|
|
}
|
|
if err := db.Model(&model.OciConfig{}).Where("id = ?", 2).
|
|
Update("account_type", model.AccountTypeFree).Error; err != nil {
|
|
t.Fatalf("mark free: %v", err)
|
|
}
|
|
|
|
task, err := tasks.CreateTask(ctx, CreateTaskInput{
|
|
Name: "成本", Type: model.TaskTypeCost, CronExpr: "30 3 * * *",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("CreateTask: %v", err)
|
|
}
|
|
entry, err := tasks.RunTaskNow(ctx, task.ID)
|
|
if err != nil {
|
|
t.Fatalf("RunTaskNow: %v", err)
|
|
}
|
|
if !entry.Success || entry.Message != "synced usage for 1 tenants, skipped 1 free" {
|
|
t.Errorf("log = %+v, want synced 1 skipped 1", entry)
|
|
}
|
|
|
|
assertCostSnapshots(t, db, map[string]float64{day1: 6.6, day2: 6.3})
|
|
|
|
// 再次执行为覆盖更新,不产生重复行
|
|
if _, err := tasks.RunTaskNow(ctx, task.ID); err != nil {
|
|
t.Fatalf("RunTaskNow again: %v", err)
|
|
}
|
|
assertCostSnapshots(t, db, map[string]float64{day1: 6.6, day2: 6.3})
|
|
}
|
|
|
|
// assertCostSnapshots 断言快照表恰好为 want 中的日期与金额(配置 #1)。
|
|
func assertCostSnapshots(t *testing.T, db *gorm.DB, want map[string]float64) {
|
|
t.Helper()
|
|
var snaps []model.CostSnapshot
|
|
if err := db.Where("oci_config_id = ?", 1).Find(&snaps).Error; err != nil {
|
|
t.Fatalf("load cost snapshots: %v", err)
|
|
}
|
|
if len(snaps) != len(want) {
|
|
t.Fatalf("snapshots = %d rows, want %d", len(snaps), len(want))
|
|
}
|
|
for _, snap := range snaps {
|
|
amount, ok := want[snap.Day]
|
|
// 金额自 float32 累加而来,按容差比较
|
|
if !ok || math.Abs(snap.Amount-amount) > 1e-4 {
|
|
t.Errorf("day %s amount = %v, want %v", snap.Day, snap.Amount, want[snap.Day])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRunHealthCheckWritesSnapshot(t *testing.T) {
|
|
client := &fakeClient{
|
|
tenancy: oci.TenancyInfo{Name: "acme", HomeRegionKey: "FRA"},
|
|
instances: []oci.Instance{{ID: "i1"}, {ID: "i2"}},
|
|
}
|
|
tasks, configs, db := newTaskEnv(t, client)
|
|
ctx := context.Background()
|
|
if _, err := configs.Import(ctx, trialImportInput()); err != nil {
|
|
t.Fatalf("import: %v", err)
|
|
}
|
|
task, err := tasks.CreateTask(ctx, CreateTaskInput{
|
|
Name: "测活", Type: model.TaskTypeHealthCheck, CronExpr: "*/5 * * * *",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("CreateTask: %v", err)
|
|
}
|
|
if _, err := tasks.RunTaskNow(ctx, task.ID); err != nil {
|
|
t.Fatalf("RunTaskNow: %v", err)
|
|
}
|
|
var snap model.CheckSnapshot
|
|
if err := db.Where("oci_config_id = ?", 1).First(&snap).Error; err != nil {
|
|
t.Fatalf("load snapshot: %v", err)
|
|
}
|
|
if snap.AliveStatus != model.AliveStatusAlive || snap.InstanceCount != 2 {
|
|
t.Errorf("snapshot = %+v, want alive with 2 instances", snap)
|
|
}
|
|
}
|
|
|
|
func TestOverviewAggregates(t *testing.T) {
|
|
client := &fakeClient{
|
|
tenancy: oci.TenancyInfo{Name: "acme", HomeRegionKey: "FRA"},
|
|
instances: []oci.Instance{{ID: "i1"}},
|
|
costItems: []oci.CostItem{costItem(time.Now().UTC().Format("2006-01-02"), 3.5, "USD")},
|
|
}
|
|
tasks, configs, _ := newTaskEnv(t, client)
|
|
ctx := context.Background()
|
|
if _, err := configs.Import(ctx, trialImportInput()); err != nil {
|
|
t.Fatalf("import: %v", err)
|
|
}
|
|
for _, in := range []CreateTaskInput{
|
|
{Name: "测活", Type: model.TaskTypeHealthCheck, CronExpr: "*/5 * * * *"},
|
|
{Name: "成本", Type: model.TaskTypeCost, CronExpr: "30 3 * * *"},
|
|
} {
|
|
task, err := tasks.CreateTask(ctx, in)
|
|
if err != nil {
|
|
t.Fatalf("CreateTask %s: %v", in.Name, err)
|
|
}
|
|
if _, err := tasks.RunTaskNow(ctx, task.ID); err != nil {
|
|
t.Fatalf("RunTaskNow %s: %v", in.Name, err)
|
|
}
|
|
}
|
|
|
|
out, err := configs.Overview(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Overview: %v", err)
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
got interface{}
|
|
want interface{}
|
|
}{
|
|
{"租户总数", out.Tenants.Total, 1},
|
|
{"存活数", out.Tenants.Alive, 1},
|
|
{"测活任务存在", out.Check.HasActiveTask, true},
|
|
{"测活覆盖", out.Check.CoveredConfigs, 1},
|
|
{"实例数", out.Check.InstanceCount, 1},
|
|
{"成本任务存在", out.Cost.HasActiveTask, true},
|
|
{"成本覆盖", out.Cost.CoveredConfigs, 1},
|
|
{"成本合计", out.Cost.Total, 3.5},
|
|
{"币种", out.Cost.Currency, "USD"},
|
|
{"任务总数", out.Tasks.Total, 2},
|
|
{"active 任务", out.Tasks.Active, 2},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.got != tt.want {
|
|
t.Errorf("got %v, want %v", tt.got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
if len(out.Cost.Days) != 1 {
|
|
t.Errorf("cost days = %d, want 1", len(out.Cost.Days))
|
|
}
|
|
}
|