249 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			249 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Go
		
	
	
	
| package actions_test
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"encoding/json"
 | |
| 	"io"
 | |
| 	"net/http"
 | |
| 	"net/url"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/actions/actions-runner-controller/github/actions"
 | |
| 	"github.com/actions/actions-runner-controller/github/actions/testserver"
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| 	"github.com/stretchr/testify/require"
 | |
| )
 | |
| 
 | |
| var testUserAgent = actions.UserAgentInfo{
 | |
| 	Version:    "test",
 | |
| 	CommitSHA:  "test",
 | |
| 	ScaleSetID: 1,
 | |
| }
 | |
| 
 | |
| func TestNewGitHubAPIRequest(t *testing.T) {
 | |
| 	ctx := context.Background()
 | |
| 
 | |
| 	t.Run("uses the right host/path prefix", func(t *testing.T) {
 | |
| 		scenarios := []struct {
 | |
| 			configURL string
 | |
| 			path      string
 | |
| 			expected  string
 | |
| 		}{
 | |
| 			{
 | |
| 				configURL: "https://github.com/org/repo",
 | |
| 				path:      "/app/installations/123/access_tokens",
 | |
| 				expected:  "https://api.github.com/app/installations/123/access_tokens",
 | |
| 			},
 | |
| 			{
 | |
| 				configURL: "https://www.github.com/org/repo",
 | |
| 				path:      "/app/installations/123/access_tokens",
 | |
| 				expected:  "https://api.github.com/app/installations/123/access_tokens",
 | |
| 			},
 | |
| 			{
 | |
| 				configURL: "http://github.localhost/org/repo",
 | |
| 				path:      "/app/installations/123/access_tokens",
 | |
| 				expected:  "http://api.github.localhost/app/installations/123/access_tokens",
 | |
| 			},
 | |
| 			{
 | |
| 				configURL: "https://my-instance.com/org/repo",
 | |
| 				path:      "/app/installations/123/access_tokens",
 | |
| 				expected:  "https://my-instance.com/api/v3/app/installations/123/access_tokens",
 | |
| 			},
 | |
| 			{
 | |
| 				configURL: "http://localhost/org/repo",
 | |
| 				path:      "/app/installations/123/access_tokens",
 | |
| 				expected:  "http://localhost/api/v3/app/installations/123/access_tokens",
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, scenario := range scenarios {
 | |
| 			client, err := actions.NewClient(scenario.configURL, nil)
 | |
| 			require.NoError(t, err)
 | |
| 
 | |
| 			req, err := client.NewGitHubAPIRequest(ctx, http.MethodGet, scenario.path, nil)
 | |
| 			require.NoError(t, err)
 | |
| 			assert.Equal(t, scenario.expected, req.URL.String())
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("sets user agent header if present", func(t *testing.T) {
 | |
| 		client, err := actions.NewClient("http://localhost/my-org", nil)
 | |
| 		require.NoError(t, err)
 | |
| 
 | |
| 		client.SetUserAgent(testUserAgent)
 | |
| 
 | |
| 		req, err := client.NewGitHubAPIRequest(ctx, http.MethodGet, "/app/installations/123/access_tokens", nil)
 | |
| 		require.NoError(t, err)
 | |
| 
 | |
| 		assert.Equal(t, testUserAgent.String(), req.Header.Get("User-Agent"))
 | |
| 	})
 | |
| 
 | |
| 	t.Run("sets the body we pass", func(t *testing.T) {
 | |
| 		client, err := actions.NewClient("http://localhost/my-org", nil)
 | |
| 		require.NoError(t, err)
 | |
| 
 | |
| 		req, err := client.NewGitHubAPIRequest(
 | |
| 			ctx,
 | |
| 			http.MethodGet,
 | |
| 			"/app/installations/123/access_tokens",
 | |
| 			strings.NewReader("the-body"),
 | |
| 		)
 | |
| 		require.NoError(t, err)
 | |
| 
 | |
| 		b, err := io.ReadAll(req.Body)
 | |
| 		require.NoError(t, err)
 | |
| 		assert.Equal(t, "the-body", string(b))
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestNewActionsServiceRequest(t *testing.T) {
 | |
| 	ctx := context.Background()
 | |
| 	defaultCreds := &actions.ActionsAuth{Token: "token"}
 | |
| 
 | |
| 	t.Run("manages authentication", func(t *testing.T) {
 | |
| 		t.Run("client is brand new", func(t *testing.T) {
 | |
| 			token := defaultActionsToken(t)
 | |
| 			server := testserver.New(t, nil, testserver.WithActionsToken(token))
 | |
| 
 | |
| 			client, err := actions.NewClient(server.ConfigURLForOrg("my-org"), defaultCreds)
 | |
| 			require.NoError(t, err)
 | |
| 
 | |
| 			req, err := client.NewActionsServiceRequest(ctx, http.MethodGet, "my-path", nil)
 | |
| 			require.NoError(t, err)
 | |
| 
 | |
| 			assert.Equal(t, "Bearer "+token, req.Header.Get("Authorization"))
 | |
| 		})
 | |
| 
 | |
| 		t.Run("admin token is about to expire", func(t *testing.T) {
 | |
| 			newToken := defaultActionsToken(t)
 | |
| 			server := testserver.New(t, nil, testserver.WithActionsToken(newToken))
 | |
| 
 | |
| 			client, err := actions.NewClient(server.ConfigURLForOrg("my-org"), defaultCreds)
 | |
| 			require.NoError(t, err)
 | |
| 			client.ActionsServiceAdminToken = "expiring-token"
 | |
| 			client.ActionsServiceAdminTokenExpiresAt = time.Now().Add(59 * time.Second)
 | |
| 
 | |
| 			req, err := client.NewActionsServiceRequest(ctx, http.MethodGet, "my-path", nil)
 | |
| 			require.NoError(t, err)
 | |
| 
 | |
| 			assert.Equal(t, "Bearer "+newToken, req.Header.Get("Authorization"))
 | |
| 		})
 | |
| 
 | |
| 		t.Run("admin token refresh failure", func(t *testing.T) {
 | |
| 			newToken := defaultActionsToken(t)
 | |
| 			errMessage := `{"message":"test"}`
 | |
| 			unauthorizedHandler := func(w http.ResponseWriter, r *http.Request) {
 | |
| 				w.Header().Set("Content-Type", "application/json")
 | |
| 				w.WriteHeader(http.StatusUnauthorized)
 | |
| 				w.Write([]byte(errMessage))
 | |
| 			}
 | |
| 			server := testserver.New(
 | |
| 				t,
 | |
| 				nil,
 | |
| 				testserver.WithActionsToken("random-token"),
 | |
| 				testserver.WithActionsToken(newToken),
 | |
| 				testserver.WithActionsRegistrationTokenHandler(unauthorizedHandler),
 | |
| 			)
 | |
| 			client, err := actions.NewClient(server.ConfigURLForOrg("my-org"), defaultCreds)
 | |
| 			require.NoError(t, err)
 | |
| 			expiringToken := "expiring-token"
 | |
| 			expiresAt := time.Now().Add(59 * time.Second)
 | |
| 			client.ActionsServiceAdminToken = expiringToken
 | |
| 			client.ActionsServiceAdminTokenExpiresAt = expiresAt
 | |
| 			_, err = client.NewActionsServiceRequest(ctx, http.MethodGet, "my-path", nil)
 | |
| 			require.Error(t, err)
 | |
| 			assert.Contains(t, err.Error(), errMessage)
 | |
| 			assert.Equal(t, client.ActionsServiceAdminToken, expiringToken)
 | |
| 			assert.Equal(t, client.ActionsServiceAdminTokenExpiresAt, expiresAt)
 | |
| 		})
 | |
| 
 | |
| 		t.Run("admin token refresh retry", func(t *testing.T) {
 | |
| 			newToken := defaultActionsToken(t)
 | |
| 			errMessage := `{"message":"test"}`
 | |
| 
 | |
| 			srv := "http://github.com/my-org"
 | |
| 			resp := &actions.ActionsServiceAdminConnection{
 | |
| 				AdminToken:        &newToken,
 | |
| 				ActionsServiceUrl: &srv,
 | |
| 			}
 | |
| 			failures := 0
 | |
| 			unauthorizedHandler := func(w http.ResponseWriter, r *http.Request) {
 | |
| 				if failures < 5 {
 | |
| 					failures++
 | |
| 					w.Header().Set("Content-Type", "application/json")
 | |
| 					w.WriteHeader(http.StatusUnauthorized)
 | |
| 					w.Write([]byte(errMessage))
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				w.WriteHeader(http.StatusCreated)
 | |
| 				_ = json.NewEncoder(w).Encode(resp)
 | |
| 			}
 | |
| 			server := testserver.New(t, nil, testserver.WithActionsToken("random-token"), testserver.WithActionsToken(newToken), testserver.WithActionsRegistrationTokenHandler(unauthorizedHandler))
 | |
| 			client, err := actions.NewClient(server.ConfigURLForOrg("my-org"), defaultCreds)
 | |
| 			require.NoError(t, err)
 | |
| 			expiringToken := "expiring-token"
 | |
| 			expiresAt := time.Now().Add(59 * time.Second)
 | |
| 			client.ActionsServiceAdminToken = expiringToken
 | |
| 			client.ActionsServiceAdminTokenExpiresAt = expiresAt
 | |
| 
 | |
| 			_, err = client.NewActionsServiceRequest(ctx, http.MethodGet, "my-path", nil)
 | |
| 			require.NoError(t, err)
 | |
| 			assert.Equal(t, client.ActionsServiceAdminToken, newToken)
 | |
| 			assert.Equal(t, client.ActionsServiceURL, srv)
 | |
| 			assert.NotEqual(t, client.ActionsServiceAdminTokenExpiresAt, expiresAt)
 | |
| 		})
 | |
| 
 | |
| 		t.Run("token is currently valid", func(t *testing.T) {
 | |
| 			tokenThatShouldNotBeFetched := defaultActionsToken(t)
 | |
| 			server := testserver.New(t, nil, testserver.WithActionsToken(tokenThatShouldNotBeFetched))
 | |
| 
 | |
| 			client, err := actions.NewClient(server.ConfigURLForOrg("my-org"), defaultCreds)
 | |
| 			require.NoError(t, err)
 | |
| 			client.ActionsServiceAdminToken = "healthy-token"
 | |
| 			client.ActionsServiceAdminTokenExpiresAt = time.Now().Add(1 * time.Hour)
 | |
| 
 | |
| 			req, err := client.NewActionsServiceRequest(ctx, http.MethodGet, "my-path", nil)
 | |
| 			require.NoError(t, err)
 | |
| 
 | |
| 			assert.Equal(t, "Bearer healthy-token", req.Header.Get("Authorization"))
 | |
| 		})
 | |
| 	})
 | |
| 
 | |
| 	t.Run("builds the right URL including api version", func(t *testing.T) {
 | |
| 		server := testserver.New(t, nil)
 | |
| 
 | |
| 		client, err := actions.NewClient(server.ConfigURLForOrg("my-org"), defaultCreds)
 | |
| 		require.NoError(t, err)
 | |
| 
 | |
| 		req, err := client.NewActionsServiceRequest(ctx, http.MethodGet, "/my/path?name=banana", nil)
 | |
| 		require.NoError(t, err)
 | |
| 
 | |
| 		serverURL, err := url.Parse(server.URL)
 | |
| 		require.NoError(t, err)
 | |
| 
 | |
| 		result := req.URL
 | |
| 		assert.Equal(t, serverURL.Host, result.Host)
 | |
| 		assert.Equal(t, "/tenant/123/my/path", result.Path)
 | |
| 		assert.Equal(t, "banana", result.Query().Get("name"))
 | |
| 		assert.Equal(t, "6.0-preview", result.Query().Get("api-version"))
 | |
| 	})
 | |
| 
 | |
| 	t.Run("populates header", func(t *testing.T) {
 | |
| 		server := testserver.New(t, nil)
 | |
| 
 | |
| 		client, err := actions.NewClient(server.ConfigURLForOrg("my-org"), defaultCreds)
 | |
| 		require.NoError(t, err)
 | |
| 
 | |
| 		client.SetUserAgent(testUserAgent)
 | |
| 
 | |
| 		req, err := client.NewActionsServiceRequest(ctx, http.MethodGet, "/my/path", nil)
 | |
| 		require.NoError(t, err)
 | |
| 
 | |
| 		assert.Equal(t, testUserAgent.String(), req.Header.Get("User-Agent"))
 | |
| 		assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
 | |
| 	})
 | |
| }
 |