package usecase import ( "context" "fmt" "strings" "testing" "apps/backend/internal/module/radar/domain" ) func TestMergeFetchWatchKeepsSubscriberTerms(t *testing.T) { w := &domain.RadarWatch{Terms: []string{"求推薦", "泛紅"}, ExcludeTerms: []string{"抽獎"}} plan := &domain.QueryPlan{Groups: []domain.QueryPlanGroup{ {Query: "換季 不適", Exclude: []string{"業配"}}, {Query: "求推薦"}, }} got := mergeFetchWatch(w, plan) if len(got.Terms) != 3 || got.Terms[0] != "求推薦" || got.Terms[1] != "泛紅" || got.Terms[2] != "換季 不適" { t.Fatalf("terms=%v want subscriber keywords first, then new plan queries", got.Terms) } if len(got.ExcludeTerms) != 2 { t.Fatalf("exclude=%v", got.ExcludeTerms) } } func TestMergeFetchWatchExpandsLongSubscriberTerms(t *testing.T) { w := &domain.RadarWatch{Terms: []string{"晚上睡覺容易口乾舌燥怎麼辦"}} got := mergeFetchWatch(w, nil) if len(got.Terms) == 0 { t.Fatal("long subscriber term must expand before fetch") } for _, term := range got.Terms { if !domain.IsThreadsSearchable(term) { t.Fatalf("fetch term %q is not searchable", term) } } } func TestHumanFetchErrorCrawlerSessionExpired(t *testing.T) { got := humanFetchError(fmt.Errorf(`Chrome crawler status 422: {"error":"crawler session expired"}`)) if !strings.Contains(got, "Chrome 登入已過期") || !strings.Contains(got, "設定") { t.Fatalf("human message=%q", got) } } func TestFetchCandidatesKeepsTitleOnlyHits(t *testing.T) { svc := New(nil) svc.HitFetch = HitFetcherFunc(func(context.Context, int64, []string, int) ([]ThreadHit, string, error) { return []ThreadHit{{ URL: "https://www.threads.net/@a/post/xyz", Title: "皮膚泛紅怎麼辦", }}, "api", nil }) // bill path: HitFetch returns api so FetchCandidates will try to bill. // Avoid billing by setting path after... FetchCandidates bills API path. // Use empty Usage so bill is a no-op if implemented that way. cands, _, _, err := svc.FetchCandidates(context.Background(), 1, &domain.RadarWatch{Terms: []string{"泛紅"}}, 10) if err != nil { t.Fatalf("fetch: %v", err) } if len(cands) != 1 || cands[0].Text == "" { t.Fatalf("title-only hit dropped: %+v err=%v", cands, err) } }