feat: enforce tenant content moderation
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
package moderation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/tinode/chat/server/store"
|
||||
"github.com/tinode/chat/server/store/types"
|
||||
)
|
||||
|
||||
type testConfig struct {
|
||||
policy *types.ModerationPolicy
|
||||
words *types.SensitiveWordSet
|
||||
}
|
||||
|
||||
func (c testConfig) Provider() (*types.ModerationProviderConfig, error) {
|
||||
return &types.ModerationProviderConfig{}, nil
|
||||
}
|
||||
func (c testConfig) Policy(string) (*types.ModerationPolicy, error) { return c.policy, nil }
|
||||
func (c testConfig) Words(string) (*types.SensitiveWordSet, error) { return c.words, nil }
|
||||
|
||||
type testAudit struct{}
|
||||
|
||||
func (testAudit) RecordLogin(types.TenantID, *types.LoginAuditEvent) error { return nil }
|
||||
func (testAudit) RecordLoginFailure(types.TenantID, *types.LoginFailureEvent) error { return nil }
|
||||
func (testAudit) CloseLogin(types.TenantID, string, time.Time) error { return nil }
|
||||
func (testAudit) ApplyMessageAction(types.TenantID, *types.MessageAdminAction) error { return nil }
|
||||
func (testAudit) RecordContentModeration(types.TenantID, *types.ContentModerationLog) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestExtractText(t *testing.T) {
|
||||
content := map[string]any{"txt": "正文", "ent": []any{map[string]any{"data": map[string]any{"title": "查看详情", "url": "https://example.com"}}}, "meta": "ignore"}
|
||||
if got := ExtractText(content); got != "正文\n查看详情" && got != "查看详情\n正文" {
|
||||
t.Fatalf("ExtractText()=%q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReviewLocalBlock(t *testing.T) {
|
||||
oldConfig, oldAudit := store.ModerationConfig, store.SecurityAudit
|
||||
defer func() { store.ModerationConfig, store.SecurityAudit = oldConfig, oldAudit }()
|
||||
store.ModerationConfig = testConfig{policy: &types.ModerationPolicy{Enabled: true, LocalWordsEnabled: true, Scenes: map[string]bool{ScenePrivateMessage: true}, RiskActions: map[string]string{"high": ActionBlock}}, words: &types.SensitiveWordSet{Items: []*types.SensitiveWordRule{{Word: "违规词", MatchType: "contains", Scenes: []string{ScenePrivateMessage}, Action: ActionBlock, RiskLevel: "high"}}}}
|
||||
store.SecurityAudit = testAudit{}
|
||||
result, err := Review(context.Background(), Request{TenantID: 1, TenantCode: "default", Scene: ScenePrivateMessage, Text: "包含违规词的消息"})
|
||||
if err != ErrRejected || result.Action != ActionBlock || result.Provider != "local" {
|
||||
t.Fatalf("result=%+v err=%v", result, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReviewOtherTenantUsesOwnPolicy(t *testing.T) {
|
||||
oldConfig, oldAudit := store.ModerationConfig, store.SecurityAudit
|
||||
defer func() { store.ModerationConfig, store.SecurityAudit = oldConfig, oldAudit }()
|
||||
store.ModerationConfig = testConfig{policy: &types.ModerationPolicy{Enabled: true, LocalWordsEnabled: true, Scenes: map[string]bool{ScenePrivateMessage: true}, RiskActions: map[string]string{"high": ActionBlock}}, words: &types.SensitiveWordSet{Items: []*types.SensitiveWordRule{{Word: "违规词", MatchType: "contains", Scenes: []string{ScenePrivateMessage}, Action: ActionBlock, RiskLevel: "high"}}}}
|
||||
store.SecurityAudit = testAudit{}
|
||||
result, err := Review(context.Background(), Request{TenantID: 2, TenantCode: "team", Scene: ScenePrivateMessage, Text: "违规词"})
|
||||
if err != ErrRejected || result.Action != ActionBlock {
|
||||
t.Fatalf("result=%+v err=%v", result, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchLocalWordsPinyin(t *testing.T) {
|
||||
result := matchLocalWords("这里包含敏感内容", SceneGroupMessage, &types.SensitiveWordSet{Items: []*types.SensitiveWordRule{{Word: "mingan", MatchType: "pinyin", Scenes: []string{SceneGroupMessage}, RiskLevel: "medium"}}})
|
||||
if result == nil || result.RiskLevel != "medium" {
|
||||
t.Fatalf("result=%+v", result)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user