|
7 | 7 | "api.audius.co/database" |
8 | 8 | "api.audius.co/trashid" |
9 | 9 | "github.com/stretchr/testify/assert" |
| 10 | + "github.com/tidwall/gjson" |
10 | 11 | ) |
11 | 12 |
|
12 | 13 | func TestV1UsersDeveloperApps(t *testing.T) { |
@@ -68,6 +69,11 @@ func TestV1UsersDeveloperApps(t *testing.T) { |
68 | 69 | "data.1.name": "app_name_2", |
69 | 70 | "data.1.description": "app_description_2", |
70 | 71 | }) |
| 72 | + // redirect_uris must be an empty array (not null) when no URIs are registered |
| 73 | + assert.True(t, gjson.GetBytes(body, "data.0.redirect_uris").IsArray()) |
| 74 | + assert.Equal(t, 0, len(gjson.GetBytes(body, "data.0.redirect_uris").Array())) |
| 75 | + assert.True(t, gjson.GetBytes(body, "data.1.redirect_uris").IsArray()) |
| 76 | + assert.Equal(t, 0, len(gjson.GetBytes(body, "data.1.redirect_uris").Array())) |
71 | 77 | } |
72 | 78 |
|
73 | 79 | { |
@@ -125,3 +131,57 @@ func TestV1UsersDeveloperAppsIncludeMetrics(t *testing.T) { |
125 | 131 | "data.0.api_access_keys.#": 0, |
126 | 132 | }) |
127 | 133 | } |
| 134 | + |
| 135 | +// TestV1UsersDeveloperAppsRedirectUrisOrdering verifies that redirect_uris are returned |
| 136 | +// in deterministic order (by oauth_redirect_uris.id) rather than alphabetically, |
| 137 | +// and that apps without registered URIs return an empty array rather than null. |
| 138 | +func TestV1UsersDeveloperAppsRedirectUrisOrdering(t *testing.T) { |
| 139 | + app := emptyTestApp(t) |
| 140 | + |
| 141 | + fixtures := database.FixtureMap{ |
| 142 | + "users": []map[string]any{ |
| 143 | + {"user_id": 1, "handle": "user1"}, |
| 144 | + }, |
| 145 | + "developer_apps": []map[string]any{ |
| 146 | + { |
| 147 | + "address": "app_address_1", |
| 148 | + "user_id": 1, |
| 149 | + "name": "app_name_1", |
| 150 | + "is_current": true, |
| 151 | + "is_delete": false, |
| 152 | + "created_at": time.Now(), |
| 153 | + }, |
| 154 | + { |
| 155 | + "address": "app_address_2", |
| 156 | + "user_id": 1, |
| 157 | + "name": "app_name_2", |
| 158 | + "is_current": true, |
| 159 | + "is_delete": false, |
| 160 | + "created_at": time.Now().Add(-time.Second), |
| 161 | + }, |
| 162 | + }, |
| 163 | + // Insert z before a: if ordering were alphabetical, a would come first. |
| 164 | + // With id ordering, z comes first because it was inserted with a lower id. |
| 165 | + "oauth_redirect_uris": []map[string]any{ |
| 166 | + {"client_id": "app_address_1", "redirect_uri": "https://z.example.com/callback"}, |
| 167 | + {"client_id": "app_address_1", "redirect_uri": "https://a.example.com/callback"}, |
| 168 | + }, |
| 169 | + } |
| 170 | + |
| 171 | + database.Seed(app.pool.Replicas[0], fixtures) |
| 172 | + |
| 173 | + status, body := testGet(t, app, "/v1/users/"+trashid.MustEncodeHashID(1)+"/developer-apps") |
| 174 | + assert.Equal(t, 200, status) |
| 175 | + // app_address_1 has 2 redirect URIs; z was inserted first (lower id) so it must come first |
| 176 | + jsonAssert(t, body, map[string]any{ |
| 177 | + "data.#": 2, |
| 178 | + "data.0.address": "app_address_1", |
| 179 | + "data.0.redirect_uris.#": 2, |
| 180 | + "data.0.redirect_uris.0": "https://z.example.com/callback", |
| 181 | + "data.0.redirect_uris.1": "https://a.example.com/callback", |
| 182 | + "data.1.address": "app_address_2", |
| 183 | + }) |
| 184 | + // app_address_2 has no redirect URIs; must be an empty array, not null |
| 185 | + assert.True(t, gjson.GetBytes(body, "data.1.redirect_uris").IsArray()) |
| 186 | + assert.Equal(t, 0, len(gjson.GetBytes(body, "data.1.redirect_uris").Array())) |
| 187 | +} |
0 commit comments