From 04b07b6ace258edb86f5971b6f8cc32688b0d258 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Fri, 13 Nov 2026 11:58:31 -0800 Subject: [PATCH] fix(contests): feature Audius-hosted and verified contests in the list The featured sort group only held hosts the Audius account follows, so the Audius account's own contests fell in with everyone else and sorted by entries. featuredAudienceUserId was also never set in prod, so the group was empty there. Count verified hosts and the Audius account itself, and default the account id in prod. Co-Authored-By: Claude Fable 5.2 --- api/v1_events_remix_contests.go | 18 +++---- api/v1_events_remix_contests_test.go | 72 ++++++++++++++++++++++++++++ config/config.go | 9 ++-- 3 files changed, 87 insertions(+), 12 deletions(-) diff --git a/api/v1_events_remix_contests.go b/api/v1_events_remix_contests.go index 508fa081..d1f9ee20 100644 --- a/api/v1_events_remix_contests.go +++ b/api/v1_events_remix_contests.go @@ -18,13 +18,14 @@ type GetRemixContestsParams struct { // v1EventsRemixContests returns remix-contest events from the events table. // Sort priority: -// 1. Open contests whose host is followed by the Audius account. -// 2. Open contests whose host is not followed by the Audius account. -// 3. Ended contests whose host is followed by the Audius account. -// 4. Ended contests whose host is not followed by the Audius account. +// 1. Open contests with a featured host. +// 2. Open contests with any other host. +// 3. Ended contests with a featured host. +// 4. Ended contests with any other host. // -// The Audius account is config.Cfg.FeaturedAudienceUserID; 0 disables the -// follow-based tiebreak (groups collapse to open-vs-ended only). +// A featured host is verified, is the Audius account, or is followed by the +// Audius account. The Audius account is config.Cfg.FeaturedAudienceUserID; +// 0 leaves verified as the only signal. // Within each group, results are sorted by entry count descending, then by // the active-first / soonest-ending sort, then event_id ASC as a stable tiebreak. // Supports pagination and an optional `status` filter (active | ended | all). @@ -132,9 +133,10 @@ func (app *ApiServer) v1EventsRemixContests(c *fiber.Ctx) error { ORDER BY -- Primary: open (0) before ended (1). CASE WHEN e.end_date IS NULL OR e.end_date > NOW() THEN 0 ELSE 1 END ASC, - -- Secondary: Audius-followed hosts (0) before unfollowed (1). - -- When @audius_user_id is 0 the tiebreak collapses (all rows score 1). + -- Secondary: featured hosts (0) before the rest (1). CASE + WHEN u.is_verified THEN 0 + WHEN @audius_user_id::int4 != 0 AND e.user_id = @audius_user_id::int4 THEN 0 WHEN @audius_user_id::int4 != 0 AND EXISTS ( SELECT 1 FROM follows f diff --git a/api/v1_events_remix_contests_test.go b/api/v1_events_remix_contests_test.go index f8f2d97c..a380f5d6 100644 --- a/api/v1_events_remix_contests_test.go +++ b/api/v1_events_remix_contests_test.go @@ -490,6 +490,78 @@ func TestRemixContestsFollowFilterIgnoresStaleRows(t *testing.T) { }) } +// TestRemixContestsFeaturesAudiusAndVerifiedHosts: a contest hosted by the +// Audius account or by a verified host sorts ahead of other open contests, +// even with fewer entries. +func TestRemixContestsFeaturesAudiusAndVerifiedHosts(t *testing.T) { + app := emptyTestApp(t) + + audiusUserID := 9300 + verifiedHostID := 9301 + plainHostID := 9302 + remixerID := 9303 + + audiusTrackID := 8601 + verifiedTrackID := 8602 + plainTrackID := 8603 + + start := parseTime(t, "2024-01-02") + inWindow := parseTime(t, "2024-01-03") + end := parseTime(t, "2099-01-01") + + fixtures := database.FixtureMap{ + "events": []map[string]any{ + { + "event_id": 801, "event_type": "remix_contest", "entity_type": "track", + "entity_id": plainTrackID, "user_id": plainHostID, + "created_at": start, "end_date": end, + }, + { + "event_id": 802, "event_type": "remix_contest", "entity_type": "track", + "entity_id": verifiedTrackID, "user_id": verifiedHostID, + "created_at": start, "end_date": end, + }, + { + "event_id": 803, "event_type": "remix_contest", "entity_type": "track", + "entity_id": audiusTrackID, "user_id": audiusUserID, + "created_at": start, "end_date": end, + }, + }, + "users": []map[string]any{ + {"user_id": audiusUserID, "handle": "audius"}, + {"user_id": verifiedHostID, "handle": "verified_host", "is_verified": true}, + {"user_id": plainHostID, "handle": "plain_host"}, + {"user_id": remixerID, "handle": "remixer"}, + }, + "tracks": []map[string]any{ + {"track_id": audiusTrackID, "owner_id": audiusUserID, "created_at": start}, + {"track_id": verifiedTrackID, "owner_id": verifiedHostID, "created_at": start}, + {"track_id": plainTrackID, "owner_id": plainHostID, "created_at": start}, + {"track_id": 8611, "owner_id": remixerID, "created_at": inWindow}, + {"track_id": 8612, "owner_id": remixerID, "created_at": inWindow}, + }, + "remixes": []map[string]any{ + {"parent_track_id": plainTrackID, "child_track_id": 8611}, + {"parent_track_id": plainTrackID, "child_track_id": 8612}, + }, + } + database.Seed(app.pool.Replicas[0], fixtures) + + prev := config.Cfg.FeaturedAudienceUserID + config.Cfg.FeaturedAudienceUserID = int32(audiusUserID) + t.Cleanup(func() { config.Cfg.FeaturedAudienceUserID = prev }) + + status, body := testGet(t, app, "/v1/events/remix-contests") + assert.Equal(t, 200, status) + + jsonAssert(t, body, map[string]any{ + "data.#": 3, + "data.0.event_id": trashid.MustEncodeHashID(802), // verified host, 0 entries + "data.1.event_id": trashid.MustEncodeHashID(803), // Audius account, 0 entries + "data.2.event_id": trashid.MustEncodeHashID(801), // 2 entries + }) +} + // TestRemixContestsExcludesUnavailableContent covers server-side filtering // of contests whose track or host is not in a publishable state. The // frontend used to drop these on the client (the "deleted accounts surface diff --git a/config/config.go b/config/config.go index 4a4a5d95..38e48a5e 100644 --- a/config/config.go +++ b/config/config.go @@ -75,10 +75,10 @@ type Config struct { AudiusApiSecret string // Shared secret for notifications-dashboard (or other internal jobs) to read notification campaign push open counts NotificationCampaignOpenMetricsSecret string - // Audius account user id. In the public remix-contests list, hosts followed - // by this account sort ahead of other hosts within both the open and ended - // groups. Zero (the default when the env var is unset) disables follow-based - // prioritization (the list reduces to open-before-ended). + // Audius account user id. In the public remix-contests list, this account + // and the hosts it follows sort ahead of other hosts within both the open + // and ended groups. Defaults to the Audius account in prod; zero elsewhere + // unless featuredAudienceUserId is set. FeaturedAudienceUserID int32 // Genesis migration dual-write queue. @@ -270,6 +270,7 @@ func init() { case "prod": fallthrough case "production": + Cfg.FeaturedAudienceUserID = 51 Cfg.OpenAudioURLs = []string{ "rpc.audius.co", }