package business import ( "context" "gorm.io/gorm" "kra/internal/biz/business" "testing" "time" ) func TestAlertWritesRecheckCurrentChannelOwnerAndRejectConflictingBindings(t *testing.T) { exerciseAlertOwnerRecheck(t, openTestDB(t)) } func TestMySQLAlertWritesRecheckCurrentChannelOwner(t *testing.T) { exerciseAlertOwnerRecheck(t, openRehearsalMySQL(t)) } func exerciseAlertOwnerRecheck(t *testing.T, db *gorm.DB) { t.Helper() now := prepareAlertV2(t, db) if err := db.AutoMigrate(&accountPO{}); err != nil { t.Fatal(err) } for _, sql := range []string{ "INSERT INTO sys_users(id,username,level,id_path) VALUES(7,'原商务',1,'0'),(8,'新商务',1,'0')", "INSERT INTO sys_user_bind_platform(user_id,source_db,platform_busier_id,status) VALUES(7,'lx',7,'0')", } { if err := db.Exec(sql).Error; err != nil { t.Fatal(err) } } ctx := context.Background() scope := business.UserScope{SelfID: 7, IncludeSelf: true, DescendantPath: "0,7"} hit := &business.AlertRecord{OwnerScope: &scope, PlatformCode: "lx", TargetPromoteID: 1, BusinessID: 7, RuleID: 1, RuleType: "充值", Status: 1, AlertContent: "换绑前", CreatedAt: now, UpdatedAt: now} trigger := NewAlertTriggerRepo(testData{db}) write := func(want bool) { t.Helper() got, err := trigger.StoreHit(ctx, hit, []int{1, 2, 3, 4}, func(*business.AlertRecord) bool { return true }) if err != nil || got != want { t.Fatalf("written=%v want=%v err=%v", got, want, err) } } write(true) oldExemption := &business.AlertExemption{ChannelKey: business.ChannelKey{SourceDB: "lx", ID: 1}, OwnerID: 7, AddedBy: 7, AddedName: "原商务", Reason: "原商务的豁免", AddedAt: now} if err := NewAlertGovernanceRepo(testData{db}).AddExemption(ctx, scope, oldExemption.ChannelKey, func(*business.AlertExemptionTarget) (*business.AlertExemption, error) { return oldExemption, nil }); err != nil { t.Fatal(err) } if err := db.Exec("UPDATE sys_user_bind_platform SET user_id=8 WHERE source_db='lx'").Error; err != nil { t.Fatal(err) } hit.AlertContent = "换绑后的私有数据" write(false) var old alertRecordPO if err := db.First(&old).Error; err != nil { t.Fatal(err) } if old.AlertContent != "换绑前" { t.Fatal("旧商务预警被新归属数据刷新") } newHit := *hit newScope := business.UserScope{SelfID: 8, IncludeSelf: true, DescendantPath: "0,8"} newHit.OwnerScope = &newScope newHit.BusinessID = 8 newHit.RuleID = 2 if ok, err := trigger.StoreHit(ctx, &newHit, []int{1, 2, 3, 4}, func(*business.AlertRecord) bool { return true }); err != nil || !ok { t.Fatalf("新商务应产生独立预警: %v %v", ok, err) } var histories []alertRecordPO if err := db.Order("id").Find(&histories).Error; err != nil { t.Fatal(err) } if len(histories) != 2 || histories[0].BusinessID != 7 || histories[0].AlertContent != "换绑前" || histories[1].BusinessID != 8 { t.Fatalf("旧商务预警不应被转移给新商务: %+v", histories) } governance := NewAlertGovernanceRepo(testData{db}) called := false build := func(*business.AlertExemptionTarget) (*business.AlertExemption, error) { called = true return nil, business.ErrAlertRecordInput } if err := governance.AddExemption(ctx, scope, business.ChannelKey{SourceDB: "lx", ID: 1}, build); err != business.ErrAlertRecordScope || called { t.Fatalf("换绑后仍允许免预警: %v called=%v", err, called) } if err := db.Exec("INSERT INTO sys_user_bind_platform(user_id,source_db,platform_busier_id,status) VALUES(7,'lx',7,'0')").Error; err != nil { t.Fatal(err) } if err := governance.AddExemption(ctx, scope, business.ChannelKey{SourceDB: "lx", ID: 1}, build); err != business.ErrAlertRecordScope || called { t.Fatal("不能忽略范围外的冲突绑定") } } func TestAlertLossMissingChannelRemainsUnknown(t *testing.T) { db := openTestDB(t) now := prepareAlertV2(t, db) if err := db.Create(&alertRecordPO{ID: 1, PlatformCode: "lx", TargetPromoteID: 999, Status: 1, CreatedAt: now}).Error; err != nil { t.Fatal(err) } scope := business.AlertRecordScope{Owner: business.LevelOneScope{All: true}, Viewer: business.UserScope{All: true}} r := NewAlertRecordRepo(testData{db}) page, err := r.List(context.Background(), scope, business.AlertRecordFilter{}) if err != nil || page.Total != 1 || page.Rows[0].Lost != nil { t.Fatalf("缺失渠道不应被展示为未流失: %+v %v", page, err) } lost := false page, err = r.List(context.Background(), scope, business.AlertRecordFilter{Lost: &lost}) if err != nil || page.Total != 0 { t.Fatalf("未流失筛选应排除未知渠道: %+v %v", page, err) } } func TestAlertComparisonIgnoresInvalidHistoricalTimestamps(t *testing.T) { db := openTestDB(t) now := prepareAlertV2(t, db) if err := db.Exec("INSERT INTO tab_user VALUES(99,'lx',1,0,'','unknown')").Error; err != nil { t.Fatal(err) } key := business.ChannelKey{SourceDB: "lx", ID: 1} values, err := NewAlertTriggerRepo(testData{db}).Comparison(context.Background(), 1, []business.ChannelKey{key}, now, 1) if err != nil || values[key].FirstUnix != now.Add(-48*time.Hour).Unix() { t.Fatalf("无效时间不应阻止正常渠道触发: %+v %v", values, err) } }