package logic import ( "context" "errors" "testing" "time" modentity "spark-server/internal/module/moderation/entity" "spark-server/internal/pkg/errs" "spark-server/internal/pkg/sensitive" ) type fakeUsers struct { mutedUntil time.Time disabled bool } func (f *fakeUsers) AssertCanSend(_ context.Context, _ int64) error { return nil } func (f *fakeUsers) DisableAccount(_ context.Context, _ int64) error { f.disabled = true return nil } func (f *fakeUsers) ExtendSendMutedUntil(_ context.Context, _ int64, until time.Time) error { if until.After(f.mutedUntil) { f.mutedUntil = until } return nil } type fakeIDs struct{ n int64 } func (f *fakeIDs) NextID() (int64, error) { f.n++ return f.n, nil } func TestScanReplace(t *testing.T) { word := sensitive.FoldWord("赌博") repo := NewMemoryRepo(&modentity.Word{ ID: 1, WordRaw: "赌博", Word: word, Category: modentity.CategoryGambling, Action: modentity.ActionReplace, Enabled: true, }) svc := NewService(repo, nil, &fakeIDs{}) res, err := svc.Scan(context.Background(), "今晚赌博") if err != nil || res == nil || res.Action != modentity.ActionReplace || res.ReplacedBody != "今晚***" { t.Fatalf("scan %+v %v", res, err) } } func TestCreateWordAndReload(t *testing.T) { repo := NewMemoryRepo() svc := NewService(repo, nil, &fakeIDs{}) w, err := svc.CreateWord(context.Background(), &CreateWordInput{ WordRaw: "广告词", Category: int8(modentity.CategoryAd), Action: int8(modentity.ActionLog), }) if err != nil || w == nil || w.Word == "" { t.Fatalf("create %+v %v", w, err) } list, total, err := svc.ListWords(context.Background(), "", 0, nil, 0, 10) if err != nil || total != 1 || len(list) != 1 { t.Fatalf("list %d %+v %v", total, list, err) } } func TestEnforceReject(t *testing.T) { repo := NewMemoryRepo(&modentity.Word{ ID: 2, Word: sensitive.FoldWord("毒品"), WordRaw: "毒品", Category: modentity.CategoryDrug, Action: modentity.ActionReject, Enabled: true, }) ids := &fakeIDs{} svc := NewService(repo, &fakeUsers{}, ids) res, _ := svc.Scan(context.Background(), "涉毒品") if err := svc.Apply(context.Background(), 9, 1, &modentity.ApplyInput{Hits: res.Hits}); err != nil { t.Fatal(err) } if err := svc.Enforce(context.Background(), 9, res); !errors.Is(err, errs.ErrContentBlocked) { t.Fatalf("enforce %v", err) } if len(repo.hits) != 1 { t.Fatalf("hits %d", len(repo.hits)) } } func TestEnforceMute(t *testing.T) { repo := NewMemoryRepo(&modentity.Word{ ID: 3, Word: sensitive.FoldWord("骂人"), WordRaw: "骂人", Category: modentity.CategoryAbuse, Action: modentity.ActionMute, MuteSeconds: 60, Enabled: true, }) fu := &fakeUsers{} svc := NewService(repo, fu, &fakeIDs{}) res, _ := svc.Scan(context.Background(), "骂人啊") if err := svc.Apply(context.Background(), 1, 2, &modentity.ApplyInput{Hits: res.Hits}); err != nil { t.Fatal(err) } if err := svc.Enforce(context.Background(), 1, res); !errors.Is(err, errs.ErrContentBlocked) { t.Fatalf("%v", err) } if fu.mutedUntil.IsZero() { t.Fatal("expected mute") } } func TestPatchAndDeleteWord(t *testing.T) { repo := NewMemoryRepo() svc := NewService(repo, nil, &fakeIDs{}) w, err := svc.CreateWord(context.Background(), &CreateWordInput{ WordRaw: "临时", Category: int8(modentity.CategoryOther), Action: int8(modentity.ActionReject), }) if err != nil { t.Fatal(err) } act := int8(modentity.ActionLog) if err := svc.PatchWord(context.Background(), w.ID, &PatchWordInput{Action: &act}); err != nil { t.Fatal(err) } if err := svc.DeleteWord(context.Background(), w.ID); err != nil { t.Fatal(err) } }