package main import ( "encoding/json" "fmt" "github.com/gin-gonic/gin" "github.com/glebarez/sqlite" "gorm.io/gorm" businessbiz "kra/internal/biz/business" "kra/internal/biz/system" businessdata "kra/internal/data/business" systemdata "kra/internal/data/system" "kra/internal/server/handler" "kra/internal/server/router" businessservice "kra/internal/service/business" "net/http" "net/http/httptest" "path/filepath" "strconv" "strings" "testing" ) func TestAlertRuleHTTPScopesAndLifecycle(t *testing.T) { db, err := gorm.Open(sqlite.Open(filepath.Join(t.TempDir(), "rules.db")), &gorm.Config{}) if err != nil { t.Fatal(err) } sqlDB, _ := db.DB() t.Cleanup(func() { _ = sqlDB.Close() }) for _, step := range append(systemdata.Migrations(), businessdata.Migrations()...) { if err := step.Migrate(db); err != nil { t.Fatal(err) } } if err := db.Exec("INSERT INTO sys_users(id,uuid,username,level,id_path,enable) VALUES(1,'root','admin',0,'0',1),(7,'owner','owner',1,'0',1),(8,'child','child',2,'0,7',1),(99,'other','other',1,'0',1)").Error; err != nil { t.Fatal(err) } provider := businessHTTPData{db} accounts := businessbiz.NewAccountUsecase(businessdata.NewAccountRepo(provider)) service := businessservice.NewAlertRuleService(businessbiz.NewAlertRuleUsecase(businessdata.NewAlertRuleRepo(provider), accounts)) engine := gin.New() engine.Use(func(c *gin.Context) { id, _ := strconv.ParseUint(c.GetHeader("X-Test-User"), 10, 64) aid := uint(901) if id == 1 { aid = 888 } c.Request = c.Request.WithContext(system.NewActorContext(c.Request.Context(), system.Actor{UserID: uint(id), AuthorityID: aid})) c.Next() }) router.RegisterAlertRule(engine.Group(""), handler.NewAlertRule(service)) server := httptest.NewServer(engine) defer server.Close() call := func(user int, method, path, body string, expected int) map[string]any { t.Helper() req, _ := http.NewRequest(method, server.URL+path, strings.NewReader(body)) req.Header.Set("Content-Type", "application/json") req.Header.Set("X-Test-User", strconv.Itoa(user)) response, err := server.Client().Do(req) if err != nil { t.Fatal(err) } defer response.Body.Close() var result map[string]any if err := json.NewDecoder(response.Body).Decode(&result); err != nil { t.Fatal(err) } if result["code"] != float64(expected) { t.Fatalf("%s %s 返回错误: %+v", method, path, result) } return result } if err := db.Exec("INSERT INTO sys_alert_rule(id,rule_name,rule_type,monitor_metric,warning_color,creator_id,is_deleted) VALUES(9007199254740992,'旧规则','旧分类',3,'red',7,1)").Error; err != nil { t.Fatal(err) } payload := `{"ruleName":"注册异常","ruleType":"自定义分类","monitorMetric":1,"statPeriodDays":7,"momDirection":2,"momThreshold":"9999","warningColor":"#f56c6c","creatorId":99,"monitorScope":99}` created := call(7, "POST", "/sw/alertRule/add", payload, 0)["data"].(map[string]any) id, err := strconv.ParseUint(created["id"].(string), 10, 64) if err != nil || created["id"] != "9007199254740993" { t.Fatalf("大 ID 丢失精度: %+v", created) } path := fmt.Sprintf("/sw/alertRule/%d", id) value := call(8, "GET", path, "", 0)["data"].(map[string]any) if value["id"] != "9007199254740993" || value["creatorId"] != float64(7) || value["monitorScope"] != float64(1) || value["momThreshold"] != float64(9999) { t.Fatalf("规则参数、归属或阈值不正确: %+v", value) } call(99, "GET", path, "", 7) call(8, "POST", "/sw/alertRule/add", payload, 7) call(7, "PUT", fmt.Sprintf("/sw/alertRule/toggle/%d?enabled=0", id), "", 0) call(7, "POST", "/sw/alertRule/add", fmt.Sprintf(`{"id":%d,"ruleName":"绕过编辑权限","ruleType":"分类","monitorMetric":3,"unloginDays":7,"warningColor":"red"}`, id), 7) call(7, "PUT", "/sw/alertRule/edit", `{"ruleName":"绕过新建权限","ruleType":"分类","monitorMetric":3,"unloginDays":7,"warningColor":"red"}`, 7) call(7, "PUT", "/sw/alertRule/edit", fmt.Sprintf(`{"id":"%d","ruleName":"登录规则","ruleType":"新分类","monitorMetric":3,"unloginDays":7,"warningColor":"red"}`, id), 0) value = call(7, "GET", path, "", 0)["data"].(map[string]any) if value["enabled"] != float64(0) || value["statPeriodDays"] != float64(7) || value["momThreshold"] != float64(9999) { t.Fatal("编辑覆盖启停状态或未传入的历史参数") } if err := db.Exec("INSERT INTO sys_alert_record(business_id,rule_type) VALUES(7,'历史分类')").Error; err != nil { t.Fatal(err) } call(99, "DELETE", path, "", 7) call(1, "DELETE", path, "", 0) list := call(7, "GET", "/sw/alertRule/list", "", 0)["data"].(map[string]any) if list["total"] != float64(0) { t.Fatal("逻辑删除未从规则列表移除") } types := call(8, "GET", "/sw/alertRule/ruleTypes", "", 0)["data"].([]any) if len(types) != 1 || types[0] != "历史分类" { t.Fatal("历史预警分类被删除或范围错误") } }