111 lines
5.1 KiB
Go
111 lines
5.1 KiB
Go
package business
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"kra/internal/biz/business"
|
|
)
|
|
|
|
func TestAlertTriggerStorageUsesRawMetricsAndPreservesWorkflow(t *testing.T) {
|
|
db := openTestDB(t)
|
|
if err := db.AutoMigrate(&accountPO{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
now := time.Date(2026, 11, 4, 12, 0, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600))
|
|
for _, sql := range []string{
|
|
"CREATE TABLE tab_promote(id INTEGER,source_db TEXT,busier_id INTEGER,account TEXT,promote_level INTEGER,status INTEGER,last_login_time INTEGER,create_time INTEGER)",
|
|
"CREATE TABLE tab_user(id INTEGER,source_db TEXT,promote_id INTEGER,puid INTEGER,register_time TEXT)",
|
|
"CREATE TABLE tab_spend(source_db TEXT,promote_id INTEGER,pay_time INTEGER,pay_status INTEGER,pay_amount DECIMAL(20,2))",
|
|
"INSERT INTO sys_users(id,username,level,parent_id,id_path,authority_id) VALUES(10,'一级',1,0,'0',901),(20,'二级',2,10,'0,10',902),(30,'三级',3,20,'0,10,20',903)",
|
|
"INSERT INTO sys_user_bind_platform(user_id,source_db,platform_busier_id,platform_busier_account,status) VALUES(30,'lx',7,'绑定','0')",
|
|
"INSERT INTO tab_promote VALUES(10,'lx',7,'正常渠道',1,1,0,1),(11,'lx',7,'停用渠道',1,0,0,1),(12,'lx',7,'下级渠道',2,1,0,1)",
|
|
} {
|
|
if err := db.Exec(sql).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
today := time.Date(2026, 11, 4, 0, 0, 0, 0, now.Location())
|
|
if err := db.Exec("INSERT INTO tab_user VALUES(1,'lx',10,0,?),(2,'lx',10,0,?),(3,'lx',10,NULL,?),(4,'lx',10,1,?),(5,'hw',10,0,?)", today.AddDate(0, 0, -2).Unix(), today.AddDate(0, 0, -1).Unix(), today.AddDate(0, 0, -1).Unix(), today.AddDate(0, 0, -1).Unix(), today.AddDate(0, 0, -1).Unix()).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.Exec("INSERT INTO tab_spend VALUES('lx',10,?,1,100),('lx',10,?,1,110),('lx',10,?,0,999),('hw',10,?,1,999)", today.AddDate(0, 0, -2).Unix(), today.AddDate(0, 0, -1).Unix(), today.AddDate(0, 0, -1).Unix(), today.AddDate(0, 0, -1).Unix()).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
day, direction := 1, 1
|
|
threshold := "10"
|
|
rule := alertRulePO{ID: 1, RuleName: "充值规则", RuleType: "充值", MonitorMetric: 2, CreatorID: 10, StatPeriodDays: &day, MomDirection: &direction, MomThreshold: &threshold, Enabled: 1}
|
|
if err := db.Create(&rule).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r := NewAlertTriggerRepo(testData{db})
|
|
input, err := r.Input(context.Background())
|
|
if err != nil || len(input.Channels) != 1 {
|
|
t.Fatalf("渠道查询错误: %+v %v", input, err)
|
|
}
|
|
metrics, err := r.Comparison(context.Background(), 1, []business.ChannelKey{{SourceDB: "lx", ID: 10}}, today, 1)
|
|
if err != nil || metrics[business.ChannelKey{SourceDB: "lx", ID: 10}].Current != "2" {
|
|
t.Fatalf("主账号、NULL父账号或跨平台口径错误: %+v %v", metrics, err)
|
|
}
|
|
uc := business.NewAlertTriggerUsecase(r)
|
|
result, err := uc.Detect(context.Background(), now)
|
|
if err != nil || result.Written != 1 {
|
|
t.Fatalf("首次命中: %+v %v", result, err)
|
|
}
|
|
var first alertRecordPO
|
|
if err := db.First(&first).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if first.BusinessID != 10 || first.TargetPromoteID != 10 || first.PlatformCode != "lx" {
|
|
t.Fatalf("归属错误: %+v", first)
|
|
}
|
|
result, err = uc.Detect(context.Background(), now.Add(time.Hour))
|
|
var count int64
|
|
db.Model(&alertRecordPO{}).Count(&count)
|
|
if err != nil || result.Written != 1 || count != 1 {
|
|
t.Fatalf("重复命中必须刷新同一未处理记录: %+v count=%d %v", result, count, err)
|
|
}
|
|
if err := db.Model(&alertRecordPO{}).Where("id=?", first.ID).Update("status", 2).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
result, err = uc.Detect(context.Background(), now.Add(2*time.Hour))
|
|
if err != nil || result.Written != 0 {
|
|
t.Fatalf("处理中预警被覆盖: %+v %v", result, err)
|
|
}
|
|
if err := db.Model(&alertRecordPO{}).Where("id=?", first.ID).Update("status", 4).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
result, err = uc.Detect(context.Background(), now.Add(3*time.Hour))
|
|
db.Model(&alertRecordPO{}).Count(&count)
|
|
if err != nil || result.Written != 0 || count != 1 {
|
|
t.Fatalf("驳回仍是未关闭记录,不应重复生成: %+v count=%d %v", result, count, err)
|
|
}
|
|
if err := db.Model(&alertRecordPO{}).Where("id=?", first.ID).Update("status", 5).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
result, err = uc.Detect(context.Background(), now.Add(3*time.Hour))
|
|
db.Model(&alertRecordPO{}).Count(&count)
|
|
if err != nil || result.Written != 1 || count != 2 {
|
|
t.Fatalf("已关闭后应允许新一轮预警: %+v count=%d %v", result, count, err)
|
|
}
|
|
}
|
|
|
|
func TestAlertTriggerWriteFailureRollsBack(t *testing.T) {
|
|
db := openTestDB(t)
|
|
if err := db.Exec("CREATE TRIGGER reject_alert BEFORE INSERT ON sys_alert_record BEGIN SELECT RAISE(ABORT,'test write failure'); END").Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r := NewAlertTriggerRepo(testData{db})
|
|
hit := &business.AlertRecord{PlatformCode: "lx", TargetPromoteID: 1, RuleType: "注册", Status: 1, CreatedAt: time.Now(), UpdatedAt: time.Now()}
|
|
_, err := r.StoreHit(context.Background(), hit, []int{1, 2, 3, 4}, func(*business.AlertRecord) bool { return true })
|
|
if err == nil {
|
|
t.Fatal("写入失败应返回错误")
|
|
}
|
|
var count int64
|
|
db.Model(&alertTriggerLockPO{}).Count(&count)
|
|
if count != 0 {
|
|
t.Fatalf("事务未回滚键锁: %d", count)
|
|
}
|
|
}
|