package business import ( "context" "testing" ) type alertRuleRepoFake struct { AlertRuleRepo rule *AlertRule created *AlertRule updated *AlertRule scope LevelOneScope } func (r *alertRuleRepoFake) Find(context.Context, uint64) (*AlertRule, error) { if r.rule == nil { return nil, ErrAlertRuleNotFound } copy := *r.rule return ©, nil } func (r *alertRuleRepoFake) List(_ context.Context, s LevelOneScope, _ AlertRuleFilter, _ ...ListOption) ([]*AlertRule, int64, error) { r.scope = s return []*AlertRule{}, 0, nil } func (r *alertRuleRepoFake) Create(_ context.Context, rule *AlertRule) error { copy := *rule r.created = © rule.ID = 5 return nil } func (r *alertRuleRepoFake) Update(_ context.Context, rule *AlertRule) error { r.updated = rule return nil } func intPointer(n int) *int { return &n } func textPointer(s string) *string { return &s } func alertRuleFixture() (*AlertRuleUsecase, *alertRuleRepoFake) { a := &accountRepoFake{accounts: map[uint]*Account{1: {ID: 1, Level: 0, IDPath: "0"}, 7: {ID: 7, Level: 1, IDPath: "0"}, 8: {ID: 8, Level: 2, IDPath: "0,7"}, 9: {ID: 9, Level: 3, IDPath: "0,7,8"}, 10: {ID: 10, Level: 2, IDPath: "broken"}}} r := &alertRuleRepoFake{} return NewAlertRuleUsecase(r, NewAccountUsecase(a)), r } func validAlertRule() *AlertRule { return &AlertRule{Name: "注册异常", Type: " 自定义标签 ", Metric: 1, StatPeriodDays: intPointer(7), MomDirection: intPointer(2), MomThreshold: textPointer("20.50"), WarningColor: "#f56c6c"} } func TestAlertRuleReadScopeUsesLevelOneAncestor(t *testing.T) { uc, r := alertRuleFixture() for _, id := range []uint{7, 8, 9} { if _, _, err := uc.List(accountContext(id, 901), AlertRuleFilter{}); err != nil || r.scope.UserID != 7 || r.scope.All || r.scope.Empty { t.Fatalf("所属一级范围错误: %+v %v", r.scope, err) } } uc.List(accountContext(10, 901), AlertRuleFilter{}) if !r.scope.Empty { t.Fatal("损坏祖先链不应放行规则") } uc.List(accountContext(1, 888), AlertRuleFilter{}) if !r.scope.All { t.Fatal("超管读取应不限创建人") } } func TestAlertRuleOwnershipDefaultsAndInactiveFields(t *testing.T) { uc, r := alertRuleFixture() input := validAlertRule() input.CreatorID = 99 input.MonitorScope = 99 input.Enabled = 0 if _, err := uc.Save(accountContext(7, 901), input); err != nil { t.Fatal(err) } if r.created.CreatorID != 7 || r.created.MonitorScope != 1 || r.created.Enabled != 1 || r.created.Type != "自定义标签" { t.Fatalf("服务器归属或默认值错误: %+v", r.created) } if _, err := uc.Save(accountContext(8, 902), validAlertRule()); err != ErrAlertRuleLevel { t.Fatal("二级不能创建规则") } r.rule = &AlertRule{ID: 5, CreatorID: 99} input.ID = 5 if _, err := uc.Save(accountContext(7, 901), input); err != ErrAlertRuleScope { t.Fatal("不能编辑其他一级商务的规则") } r.rule = &AlertRule{ID: 5, CreatorID: 7, StatPeriodDays: intPointer(7), MomThreshold: textPointer("20.50"), MomDirection: intPointer(2)} if _, err := uc.Save(accountContext(7, 901), &AlertRule{ID: 5, Name: "登录", Type: "登录异常", Metric: 3, UnloginDays: intPointer(10), WarningColor: "red"}); err != nil { t.Fatal(err) } if r.updated.StatPeriodDays == nil || *r.updated.StatPeriodDays != 7 || r.updated.MomThreshold == nil { t.Fatal("切换指标丢失了未传入的历史配置") } } func TestAlertRuleValidationAndLegacyDescription(t *testing.T) { r := validAlertRule() if err := validateAlertRule(r); err != nil { t.Fatal(err) } if r.Description() != "渠道近 7 天注册量较上一同期环比下降 20.50% 时触发" { t.Fatal(r.Description()) } r.MomDirection = intPointer(99) if err := validateAlertRule(r); err != ErrAlertRuleDirection { t.Fatal("无效方向被接受") } r = validAlertRule() r.MomThreshold = textPointer("9999") if err := validateAlertRule(r); err != nil { t.Fatal("原页面上限应可保存") } r.MomThreshold = textPointer("10000") if err := validateAlertRule(r); err != ErrAlertRuleThreshold { t.Fatal("阈值上限未校验") } }