package dramaser import ( "fmt" "net/http/httptest" "reflect" "testing" "time" "91porn-server/models/v/mediamod" "github.com/gin-gonic/gin/binding" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" ) func TestDramaListSortMatchesMediaLibraryEnum(t *testing.T) { tests := []struct { name string sortType int want bson.D }{ {name: "热门推荐", sortType: 1, want: bson.D{{Key: "sortCode", Value: -1}, {Key: "createdAt", Value: -1}, {Key: "_id", Value: -1}}}, {name: "最新上架", sortType: 2, want: bson.D{{Key: "latestPublishedAt", Value: -1}, {Key: "createdAt", Value: -1}, {Key: "_id", Value: -1}}}, {name: "最热", sortType: 3, want: bson.D{{Key: "countLike", Value: -1}, {Key: "createdAt", Value: -1}, {Key: "_id", Value: -1}}}, {name: "最多收藏", sortType: 4, want: bson.D{{Key: "countCollect", Value: -1}, {Key: "createdAt", Value: -1}, {Key: "_id", Value: -1}}}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { request := httptest.NewRequest("GET", fmt.Sprintf("/?pageNumber=1&pageSize=20&sortType=%d", test.sortType), nil) var params ListRequest if err := binding.Query.Bind(request, ¶ms); err != nil { t.Fatalf("bind sortType=%d: %v", test.sortType, err) } if got := dramaListSort(params.SortType); !reflect.DeepEqual(got, test.want) { t.Fatalf("dramaListSort(%d) = %#v, want %#v", test.sortType, got, test.want) } }) } legacyRequest := httptest.NewRequest("GET", "/?pageNumber=1&pageSize=20&sortType=7", nil) if err := binding.Query.Bind(legacyRequest, &ListRequest{}); err == nil { t.Fatal("bind legacy sortType=7 succeeded, want validation error") } } func TestDramaFeedQueueVersionChangesWithCandidateSet(t *testing.T) { now := time.Date(2026, 8, 28, 10, 0, 0, 0, time.FixedZone("CST", 8*60*60)) first := dramaFeedQueueVersion(now, []string{"media-b", "media-a"}) sameSet := dramaFeedQueueVersion(now.Add(time.Hour), []string{"media-a", "media-b"}) withNewDrama := dramaFeedQueueVersion(now.Add(time.Hour), []string{"media-a", "media-b", "media-c"}) nextDay := dramaFeedQueueVersion(now.Add(24*time.Hour), []string{"media-a", "media-b"}) if first != sameSet { t.Fatalf("same-day candidate set produced different versions: %q != %q", first, sameSet) } if first == withNewDrama { t.Fatalf("new candidate did not change queue version: %q", first) } if first == nextDay { t.Fatalf("next day did not change queue version: %q", first) } } func TestOrderFeedCandidatesPrioritizesRecommendedAndIsStable(t *testing.T) { now := time.Date(2026, 8, 24, 12, 0, 0, 0, time.UTC) recommended := &mediamod.Media{ID: primitive.NewObjectID(), SortCode: 10, CreatedAt: now.Add(-time.Hour)} high := &mediamod.Media{ID: primitive.NewObjectID(), CountLike: 100, CreatedAt: now.Add(-48 * time.Hour)} recent := &mediamod.Media{ID: primitive.NewObjectID(), CreatedAt: now.Add(-time.Hour)} candidates := []*mediamod.Media{high, recent, recommended} ordered := orderFeedCandidates(candidates, now) again := orderFeedCandidates(candidates, now.Add(time.Minute)) if len(ordered) != 3 || len(again) != 3 { t.Fatalf("ordered lengths = %d/%d, want 3/3", len(ordered), len(again)) } if ordered[0].ID != recommended.ID { t.Fatalf("first item = %s, want recommended %s", ordered[0].ID, recommended.ID) } for i := range ordered { if ordered[i].ID != again[i].ID { t.Fatalf("same-day queue changed at %d: %s != %s", i, ordered[i].ID, again[i].ID) } } } func TestValidateAnalyticsEventRequiresCheckoutContextForPaywall(t *testing.T) { event := AnalyticsEvent{ EventID: "event-1", EventName: "DRAMA_PAYWALL_EXPOSURE", SessionID: "session-1", OccurredAt: time.Now(), MediaID: primitive.NewObjectID(), ContentID: primitive.NewObjectID(), Scene: "detail", } if err := validateAnalyticsEvent(event); err == nil { t.Fatal("validateAnalyticsEvent() should reject missing checkoutContextId") } event.CheckoutContextID = "drama-checkout-test" if err := validateAnalyticsEvent(event); err != nil { t.Fatalf("validateAnalyticsEvent() error = %v", err) } } func TestDramaInteractionScoreUses91PWeights(t *testing.T) { media := &mediamod.Media{ CountLike: 1, CountCollect: 2, CountComment: 3, CountShare: 4, CountBrowse: 999, } if got, want := dramaInteractionScore(media), int64(34); got != want { t.Fatalf("dramaInteractionScore() = %d, want %d", got, want) } } func TestObjectIDStringReturnsEmptyForZeroID(t *testing.T) { if got := objectIDString(primitive.NilObjectID); got != "" { t.Fatalf("objectIDString(zero) = %q, want empty", got) } }