Name Heroku personal account without an API call

GET /teams/@personal 404s, which would loop the source-name worker the
same way a stale Sentry slug did. Short-circuit the personal-account
slug to a static name before any HTTP call.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
This commit is contained in:
Aurélien Sibiril
2026-05-28 22:22:46 +02:00
parent e467060056
commit e0c53e7a51
2 changed files with 42 additions and 0 deletions

View File

@@ -726,6 +726,12 @@ func (r *herokuNameResolver) ResolveInstanceName(ctx context.Context) (string, e
return "", nil
}
// A personal account has no Team to name; short-circuit before hitting
// GET /teams/@personal, which 404s and would loop the source-name worker.
if r.teamID == herokuPersonalAccountSlug {
return herokuPersonalAccountDisplayName, nil
}
endpoint, err := url.JoinPath("https://api.heroku.com", "teams", url.PathEscape(r.teamID))
if err != nil {
return "", fmt.Errorf("cannot build heroku team URL: %w", err)

View File

@@ -248,6 +248,42 @@ func TestTailscaleNameResolver(t *testing.T) {
}
}
func TestHerokuNameResolver(t *testing.T) {
t.Parallel()
t.Run("personal-account slug returns a name without an HTTP call", func(t *testing.T) {
t.Parallel()
client := &http.Client{Transport: roundTripperFunc(func(*http.Request) (*http.Response, error) {
t.Fatalf("resolver should not make an HTTP call for a personal account")
return nil, nil
})}
got, err := NewHerokuNameResolver(client, herokuPersonalAccountSlug).ResolveInstanceName(context.Background())
require.NoError(t, err)
assert.Equal(t, "Personal account", got)
})
t.Run("team slug resolves the team name", func(t *testing.T) {
t.Parallel()
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodGet, r.Method)
assert.Equal(t, "/teams/acme", r.URL.Path)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"name":"Acme Inc"}`))
}))
defer srv.Close()
client := &http.Client{Transport: &hostRewriter{target: srv.URL}}
got, err := NewHerokuNameResolver(client, "acme").ResolveInstanceName(context.Background())
require.NoError(t, err)
assert.Equal(t, "Acme Inc", got)
})
}
// roundTripperFunc adapts a function into an http.RoundTripper, useful for
// asserting that a resolver short-circuits before making any HTTP call.
type roundTripperFunc func(*http.Request) (*http.Response, error)