thread-master/apps/backend/internal/module/radar/domain/opportunity_test.go

91 lines
2.1 KiB
Go
Raw Normal View History

2026-08-03 05:52:02 +00:00
package domain
import (
"errors"
"testing"
)
func TestBandFromScore_LockedThresholds(t *testing.T) {
cases := []struct {
score int
want string
}{
{80, BandHigh},
{100, BandHigh},
{79, BandMid},
{50, BandMid},
{49, BandLow},
{0, BandLow},
{-1, BandLow},
}
for _, tc := range cases {
if got := BandFromScore(tc.score); got != tc.want {
t.Fatalf("BandFromScore(%d)=%q want %q", tc.score, got, tc.want)
}
}
}
func TestValidateReasons_RequiresAllFive(t *testing.T) {
full := fiveReasons()
if err := ValidateReasons(full); err != nil {
t.Fatalf("full reasons should pass: %v", err)
}
// Drop fit — OP-06.
missing := full[:4]
err := ValidateReasons(missing)
if err == nil {
t.Fatal("expected error when one dimension is missing")
}
if !errors.Is(err, ErrValidation) {
t.Fatalf("want ErrValidation, got %v", err)
}
// Blank reason text.
blank := fiveReasons()
blank[0].Reason = " "
if err := ValidateReasons(blank); err == nil {
t.Fatal("expected error for blank reason text")
}
if err := ValidateReasons(nil); err == nil {
t.Fatal("expected error for nil reasons")
}
}
func TestCanTransitionOpportunity(t *testing.T) {
allow := [][2]string{
{OppJudging, OppQualified},
{OppJudging, OppRejected},
{OppQualified, OppAccepted},
{OppQualified, OppDismissed},
{OppRejected, OppQualified},
}
for _, p := range allow {
if !CanTransitionOpportunity(p[0], p[1]) {
t.Fatalf("expected allow %s → %s", p[0], p[1])
}
}
deny := [][2]string{
{OppAccepted, OppQualified},
{OppDismissed, OppQualified},
{OppQualified, OppJudging},
{OppRejected, OppAccepted},
}
for _, p := range deny {
if CanTransitionOpportunity(p[0], p[1]) {
t.Fatalf("expected deny %s → %s", p[0], p[1])
}
}
}
func fiveReasons() []OpportunityReason {
return []OpportunityReason{
{Dimension: DimAuthenticity, Score: 25, Reason: "真的在求推薦"},
{Dimension: DimIntent, Score: 28, Reason: "有明確購買意圖"},
{Dimension: DimRegion, Score: 15, Reason: "地區相符"},
{Dimension: DimFreshness, Score: 12, Reason: "24 小時內"},
{Dimension: DimFit, Score: 8, Reason: "對上婚攝服務"},
}
}