package app import ( "encoding/json" "strings" "testing" ) func TestSanitizeAIReplyFlattensAssistantFormatting(t *testing.T) { tests := []struct { name, input, want string }{ {name: "trim", input: " 在的 ", want: "在的"}, {name: "markdown", input: "**在的**,__刚看到__", want: "在的,刚看到"}, {name: "bullets", input: "- 第一点\n- 第二点", want: "第一点 第二点"}, {name: "numbered", input: "1. 先这样\n2. 再那样", want: "先这样 再那样"}, {name: "quote", input: "> 引用\n正文", want: "引用 正文"}, {name: "code fence", input: "好的\n```go\nfmt.Println()\n```", want: "好的"}, {name: "blank", input: "\n\n \n", want: ""}, } for _, test := range tests { if got := sanitizeAIReply(test.input); got != test.want { t.Errorf("sanitizeAIReply(%s) = %q, want %q", test.name, got, test.want) } } long := strings.Repeat("字", aiReplyMaxRunes+40) if got := []rune(sanitizeAIReply(long)); len(got) != aiReplyMaxRunes { t.Errorf("a long reply must be cut to %d runes, got %d", aiReplyMaxRunes, len(got)) } } func TestAISystemPromptFallsBackToProfile(t *testing.T) { binding := aiAgentBinding{Nickname: "季若宁", Age: 27, City: "青岛", Bio: "喜欢电影与绘画"} prompt := aiSystemPrompt(binding, true) for _, fragment := range []string{"季若宁", "27 岁", "青岛", "喜欢电影与绘画"} { if !strings.Contains(prompt, fragment) { t.Errorf("profile-derived persona must mention %q: %s", fragment, prompt) } } // Disclosure is a monitored requirement, so it has to be switchable and // present by default. if !strings.Contains(prompt, "AI 助手") { t.Error("disclosure must add the honesty rule") } if strings.Contains(aiSystemPrompt(binding, false), "如果对方询问") { t.Error("disclosure off must drop the rule") } custom := aiSystemPrompt(aiAgentBinding{Persona: "你是一个健身教练", Nickname: "忽略我"}, false) if !strings.Contains(custom, "健身教练") || strings.Contains(custom, "忽略我") { t.Errorf("an explicit persona must replace the profile text: %s", custom) } if !strings.Contains(custom, "不索取或提供手机号") { t.Error("safety rules must survive a custom persona") } } func TestAIMessageTextDegradesMedia(t *testing.T) { text, _ := json.Marshal(map[string]any{"text": " 你好 "}) if got := aiMessageText(1, text); got != "你好" { t.Errorf("text message = %q", got) } if got := aiMessageText(2, []byte(`{"url":"https://example.com/a.jpg"}`)); got != "[图片]" { t.Errorf("image placeholder = %q", got) } if got := aiMessageText(3, []byte(`{"url":"https://example.com/a.m4a","duration":3}`)); got != "[语音]" { t.Errorf("audio placeholder = %q", got) } if got := aiMessageText(1, []byte(`not json`)); got != "" { t.Errorf("a broken body must be skipped, got %q", got) } } func TestAIBatchAllowList(t *testing.T) { if !aiBatchAllowed("", "any-batch") || !aiBatchAllowed("", "") { t.Error("an empty allow list must accept every test account") } if !aiBatchAllowed("batch-a, batch-b", "batch-b") { t.Error("a listed batch must be accepted") } if aiBatchAllowed("batch-a", "batch-c") { t.Error("an unlisted batch must be refused") } // An account with no batch must not slip through a non-empty allow list. if aiBatchAllowed("batch-a", "") { t.Error("an empty batch must not match a configured allow list") } }