Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion api/dbv1/get_playlists.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion api/dbv1/get_tracks.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion api/dbv1/queries/get_playlists.sql
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
-- name: GetPlaylists :many
WITH my_follows AS (
-- See get_tracks.sql for why my_follows is MATERIALIZED.
WITH my_follows AS MATERIALIZED (
SELECT
followee_user_id as user_id,
follower_count
Expand Down
6 changes: 5 additions & 1 deletion api/dbv1/queries/get_tracks.sql
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
-- name: GetTracks :many
WITH my_follows AS (
-- MATERIALIZED forces this CTE to compute once per call. Without it the
-- planner inlines and re-evaluates the follows JOIN aggregate_user + sort
-- once per row in the followee_reposts/favorites SubPlans, which dominates
-- runtime for users with many follows.
WITH my_follows AS MATERIALIZED (
SELECT
followee_user_id as user_id,
follower_count
Expand Down
42 changes: 42 additions & 0 deletions api/v1_track_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,48 @@ func TestGetTrack(t *testing.T) {
})
}

// Regression coverage for the my_follows CTE in get_tracks.sql. The CTE
// powers `has_current_user_*`, `followee_reposts`, and `followee_favorites`
// and was changed to MATERIALIZED for performance — these assertions guard
// against future refactors silently breaking the personalization shape.
func TestGetTrackPersonalization(t *testing.T) {
app := testAppWithFixtures(t)
app.skipAuthCheck = true

var resp struct{ Data dbv1.Track }

// Track 200 (eYJyn) is reposted by user 1, who is followed by user 2 (ML51L).
// Track 100 (eYZmn) is saved by user 1.
// Querying as user 2 should populate followee_* with user 1, and current-user
// repost/save flags should be false.
_, body := testGet(t, app, "/v1/full/tracks/eYJyn?user_id=ML51L", &resp)
jsonAssert(t, body, map[string]any{
"data.id": "eYJyn",
"data.has_current_user_reposted": false,
"data.has_current_user_saved": false,
"data.followee_reposts.0.user_id": trashid.MustEncodeHashID(1),
})

_, body = testGet(t, app, "/v1/full/tracks/eYZmn?user_id=ML51L", &resp)
jsonAssert(t, body, map[string]any{
"data.id": "eYZmn",
"data.has_current_user_reposted": false,
"data.has_current_user_saved": false,
"data.followee_favorites.0.user_id": trashid.MustEncodeHashID(1),
})

// Querying as user 1 themselves: their own repost/save shows on the flags;
// followee_* should not include themselves.
_, body = testGet(t, app, "/v1/full/tracks/eYJyn?user_id="+trashid.MustEncodeHashID(1), &resp)
jsonAssert(t, body, map[string]any{
"data.has_current_user_reposted": true,
})
_, body = testGet(t, app, "/v1/full/tracks/eYZmn?user_id="+trashid.MustEncodeHashID(1), &resp)
jsonAssert(t, body, map[string]any{
"data.has_current_user_saved": true,
})
}

func TestGetTrackFollowDownloadAcess(t *testing.T) {
app := testAppWithFixtures(t)
var trackResponse struct {
Expand Down
Loading