thread-master/backend/internal/library/placement/post_relevance_test.go

149 lines
5.3 KiB
Go
Raw Normal View History

package placement
import (
"testing"
"time"
libkg "haixun-backend/internal/library/knowledge"
)
func TestHasCategoryConflictLaundryVsDish(t *testing.T) {
ctx := PostScanContextFromPatrolInput(libkg.PatrolTagInput{
ProductName: "天然洗衣精",
MatchTags: []string{"洗衣精", "衣物清潔"},
}, nil)
text := "請問大家洗碗機清潔劑有推薦嗎?碗盤油污洗不掉"
if !HasCategoryConflict(text, ctx) {
t.Fatal("expected dishwasher post to conflict with laundry product")
}
if PassesPostScanFilters(text, "洗衣精 推薦", "", QueryRecency, 80, ctx) {
t.Fatal("expected conflicting post to be filtered out")
}
}
func TestPassesPostScanFiltersAcceptsMatchingLaundry(t *testing.T) {
ctx := PostScanContextFromPatrolInput(libkg.PatrolTagInput{
ProductName: "天然洗衣精",
MatchTags: []string{"洗衣精", "衣物"},
Pillars: []string{"衣物清潔", "敏感皮膚洗衣"},
}, []string{"洗碗精等其他品類"})
text := "最近衣服洗不乾淨,洗衣精有推薦嗎?污漬一直洗不掉"
if !PassesPostScanFilters(text, "洗衣精 推薦", time.Now().Format("2006-01-02"), QueryRecency, 70, ctx) {
t.Fatal("expected matching laundry post to pass")
}
}
func TestPassesPostAgeAllowsMissingDateOnRelevance(t *testing.T) {
if !PassesPostAge("", QueryRelevance, 0) {
t.Fatal("expected missing posted_at to pass age gate at ingest")
}
}
func TestPassesDiscoverFiltersAllowsWebSnippetWithoutDate(t *testing.T) {
ctx := PostScanContextFromPatrolInput(libkg.PatrolTagInput{
ProductName: "天然洗衣精",
MatchTags: []string{"洗衣精", "衣物"},
}, nil)
text := "最近衣服洗不乾淨,洗衣精有推薦嗎?污漬一直洗不掉"
if !PassesDiscoverFilters(text, "洗衣精 推薦", "", QueryRelevance, 70, 0, ctx) {
t.Fatal("expected web snippet without date to pass discover filters")
}
}
func TestPassesPostAgeRejectsOldRelevancePost(t *testing.T) {
old := time.Now().AddDate(0, 0, -20).Format("2006-01-02")
if PassesPostAge(old, QueryRelevance, 0) {
t.Fatal("expected old relevance post to fail age gate")
}
}
func TestHasPainPointDemandRejectsCasualChat(t *testing.T) {
if HasPainPointDemand("今天天氣真好,晒照給大家看") {
t.Fatal("expected casual chat without demand to fail")
}
}
func TestHasPainPointDemandAcceptsHelpSeeking(t *testing.T) {
text := "最近皮膚一直癢,沐浴乳有推薦嗎?"
if !HasPainPointDemand(text) {
t.Fatal("expected help-seeking post with pain signal to pass")
}
}
func TestPostSolvedByProductRejectsCategoryMismatch(t *testing.T) {
ctx := PostScanContextFromPatrolInput(libkg.PatrolTagInput{
ProductName: "天然洗衣精",
MatchTags: []string{"洗衣精"},
}, nil)
text := "洗碗精有推薦嗎?碗盤油污洗不掉"
if PostSolvedByProduct(text, 80, ctx) {
t.Fatal("expected dish post not to be solved by laundry product")
}
}
func TestHasTangentialTopicMismatchRejectsWashingMachine(t *testing.T) {
ctx := PostScanContextFromPatrolInput(libkg.PatrolTagInput{
ProductName: "抗敏無香洗衣精",
ProductFeatures: "完全無香、抗敏、適合敏感肌與化療族群",
MatchTags: []string{"無香", "抗敏", "洗衣精"},
Pillars: []string{"化療後衣物清潔", "無香洗衣"},
}, nil)
text := "請問大家有推薦的洗衣機嗎?想買洗脫烘"
if !HasTangentialTopicMismatch(text, ctx) {
t.Fatal("expected washing machine post to be tangential mismatch for hypoallergenic detergent")
}
if PassesPostScanFilters(text, "無香 洗衣精 推薦", time.Now().Format("2006-01-02"), QueryRecency, 80, ctx) {
t.Fatal("expected washing machine post to be filtered out")
}
}
func TestHasTangentialTopicMismatchAcceptsDetergentPain(t *testing.T) {
ctx := PostScanContextFromPatrolInput(libkg.PatrolTagInput{
ProductName: "抗敏無香洗衣精",
ProductFeatures: "完全無香、抗敏",
MatchTags: []string{"無香", "洗衣精"},
}, nil)
text := "化療後對香味很敏感,有無香洗衣精推薦嗎?"
if HasTangentialTopicMismatch(text, ctx) {
t.Fatal("expected detergent pain post to pass tangential check")
}
}
func TestHasTangentialTopicMismatchAcceptsMachineWithDetergentNeed(t *testing.T) {
ctx := PostScanContextFromPatrolInput(libkg.PatrolTagInput{
ProductName: "抗敏無香洗衣精",
ProductFeatures: "完全無香、抗敏",
MatchTags: []string{"無香", "洗衣精"},
}, nil)
text := "買了洗衣機,但想找不會讓皮膚癢的無香洗衣精"
if HasTangentialTopicMismatch(text, ctx) {
t.Fatal("expected combined machine + detergent pain post to pass")
}
}
func TestPostSolvedByProductAcceptsAlignedDemand(t *testing.T) {
ctx := PostScanContextFromPatrolInput(libkg.PatrolTagInput{
ProductName: "天然洗衣精",
MatchTags: []string{"洗衣精", "衣物"},
Pillars: []string{"衣物清潔"},
}, nil)
text := "衣服污漬洗不掉,洗衣精有推薦嗎?"
if !PostSolvedByProduct(text, 70, ctx) {
t.Fatal("expected aligned laundry demand to be solved by product")
}
}
func TestExpandExclusionTokensIncludesDishSynonyms(t *testing.T) {
tokens := ExpandExclusionTokens([]string{"洗碗精等其他品類"})
found := false
for _, token := range tokens {
if token == "洗碗機" || token == "洗碗精" {
found = true
break
}
}
if !found {
t.Fatalf("expected dish synonyms, got %v", tokens)
}
}