From 3503f78a58c4c0093dd7559f8fe981bcc82dbdcd Mon Sep 17 00:00:00 2001 From: JamBalaya56562 Date: Thu, 29 Oct 2026 06:17:37 +0900 Subject: [PATCH] test: Cover stateless GitHub App installation tokens --- github/apps_test.go | 30 ++++++++++++++++++++++++++++++ github/github_test.go | 17 +++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/github/apps_test.go b/github/apps_test.go index ac195d5ee35..61eae145b4b 100644 --- a/github/apps_test.go +++ b/github/apps_test.go @@ -8,6 +8,7 @@ package github import ( "fmt" "net/http" + "strings" "testing" "github.com/google/go-cmp/cmp" @@ -448,6 +449,35 @@ func TestAppsService_CreateInstallationToken(t *testing.T) { }) } +// statelessInstallationToken returns a token in the stateless ghs_APPID_JWT +// format of GitHub App installation tokens, which are about 520 characters long +// instead of the 40 characters of the legacy format. +func statelessInstallationToken() string { + return "ghs_12345_" + strings.Repeat("h", 36) + "." + strings.Repeat("p", 386) + "." + strings.Repeat("s", 86) +} + +func TestAppsService_CreateInstallationTokenStateless(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + token := statelessInstallationToken() + mux.HandleFunc("/app/installations/1/access_tokens", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprintf(w, `{"token":%q}`, token) + }) + + ctx := t.Context() + got, _, err := client.Apps.CreateInstallationToken(ctx, 1, nil) + if err != nil { + t.Errorf("Apps.CreateInstallationToken returned error: %v", err) + } + + want := &InstallationToken{Token: new(token)} + if !cmp.Equal(got, want) { + t.Errorf("Apps.CreateInstallationToken returned %+v, want %+v", got, want) + } +} + func TestAppsService_CreateInstallationTokenWithOptions(t *testing.T) { t.Parallel() client, mux, _ := setup(t) diff --git a/github/github_test.go b/github/github_test.go index a7522882304..d5ab29b2331 100644 --- a/github/github_test.go +++ b/github/github_test.go @@ -5033,6 +5033,23 @@ func TestNormalizedPort(t *testing.T) { } } +func TestClient_statelessInstallationToken(t *testing.T) { + t.Parallel() + server, auth := authRecorderServer(t) + + token := statelessInstallationToken() + client := mustNewClient(t, WithAuthToken(token), WithURLs(&server.URL, &server.URL)) + + req, err := client.NewRequest(t.Context(), "GET", server.URL, nil) + if err != nil { + t.Fatalf("NewRequest returned error: %v", err) + } + if _, err := client.Do(req, nil); err != nil { + t.Fatalf("Do returned error: %v", err) + } + assertRecordedAuthHeader(t, auth, "Bearer "+token) +} + // TestClient_tokenOriginScope is the policy's end-to-end guarantee on the // default client: a [WithAuthToken] token reaches the client's configured API // and upload origins, and no other destination.