Consolidate tests in a single file (#6)

This commit is contained in:
Francesco Renzi
2025-10-31 12:00:11 +00:00
committed by GitHub
parent 51aa04a4b2
commit e9e5ca3282
19 changed files with 1873 additions and 1825 deletions
-113
View File
@@ -1,113 +0,0 @@
package scaleset
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/stretchr/testify/require"
)
// newActionsServer returns a new httptest.Server that handles the
// authentication requests neeeded to create a new client. Any requests not
// made to the /actions/runners/registration-token or
// /actions/runner-registration endpoints will be handled by the provided
// handler. The returned server is started and will be automatically closed
// when the test ends.
func newActionsServer(t *testing.T, handler http.Handler, options ...actionsServerOption) *actionsServer {
s := httptest.NewServer(nil)
server := &actionsServer{
Server: s,
}
t.Cleanup(func() {
server.Close()
})
for _, option := range options {
option(server)
}
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// handle getRunnerRegistrationToken
if strings.HasSuffix(r.URL.Path, "/runners/registration-token") {
w.WriteHeader(http.StatusCreated)
w.Write([]byte(`{"token":"token"}`))
return
}
// handle getActionsServiceAdminConnection
if strings.HasSuffix(r.URL.Path, "/actions/runner-registration") {
if server.token == "" {
server.token = defaultActionsToken(t)
}
w.Write([]byte(`{"url":"` + s.URL + `/tenant/123/","token":"` + server.token + `"}`))
return
}
handler.ServeHTTP(w, r)
})
server.Config.Handler = h
return server
}
type actionsServerOption func(*actionsServer)
type actionsServer struct {
*httptest.Server
token string
}
func (s *actionsServer) configURLForOrg(org string) string {
return s.URL + "/" + org
}
func defaultActionsToken(t *testing.T) string {
claims := &jwt.RegisteredClaims{
IssuedAt: jwt.NewNumericDate(time.Now().Add(-10 * time.Minute)),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(10 * time.Minute)),
Issuer: "123",
}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(samplePrivateKey))
require.NoError(t, err)
tokenString, err := token.SignedString(privateKey)
require.NoError(t, err)
return tokenString
}
const samplePrivateKey = `-----BEGIN PRIVATE KEY-----
MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQC7tgquvNIp+Ik3
rRVZ9r0zJLsSzTHqr2dA6EUUmpRiQ25MzjMqKqu0OBwvh/pZyfjSIkKrhIridNK4
DWnPfPWHE2K3Muh0X2sClxtqiiFmXsvbiTzhUm5a+zCcv0pJCWYnKi0HmyXpAXjJ
iN8mWliZN896verVYXWrod7EaAnuST4TiJeqZYW4bBBG81fPNc/UP4j6CKAW8nx9
HtcX6ApvlHeCLZUTW/qhGLO0nLKoEOr3tXCPW5VjKzlm134Dl+8PN6f1wv6wMAoA
lo7Ha5+c74jhPL6gHXg7cRaHQmuJCJrtl8qbLkFAulfkBixBw/6i11xoM/MOC64l
TWmXqrxTAgMBAAECgf9zYlxfL+rdHRXCoOm7pUeSPL0dWaPFP12d/Z9LSlDAt/h6
Pd+eqYEwhf795SAbJuzNp51Ls6LUGnzmLOdojKwfqJ51ahT1qbcBcMZNOcvtGqZ9
xwLG993oyR49C361Lf2r8mKrdrR5/fW0B1+1s6A+eRFivqFOtsOc4V4iMeHYsCVJ
hM7yMu0UfpolDJA/CzopsoGq3UuQlibUEUxKULza06aDjg/gBH3PnP+fQ1m0ovDY
h0pX6SCq5fXVJFS+Pbpu7j2ePNm3mr0qQhrUONZq0qhGN/piCbBZe1CqWApyO7nA
B95VChhL1eYs1BKvQePh12ap83woIUcW2mJF2F0CgYEA+aERTuKWEm+zVNKS9t3V
qNhecCOpayKM9OlALIK/9W6KBS+pDsjQQteQAUAItjvLiDjd5KsrtSgjbSgr66IP
b615Pakywe5sdnVGzSv+07KMzuFob9Hj6Xv9als9Y2geVhUZB2Frqve/UCjmC56i
zuQTSele5QKCSSTFBV3423cCgYEAwIBv9ChsI+mse6vPaqSPpZ2n237anThMcP33
aS0luYXqMWXZ0TQ/uSmCElY4G3xqNo8szzfy6u0HpldeUsEUsIcBNUV5kIIb8wKu
Zmgcc8gBIjJkyUJI4wuz9G/fegEUj3u6Cttmmj4iWLzCRscRJdfGpqwRIhOGyXb9
2Rur5QUCgYAGWIPaH4R1H4XNiDTYNbdyvV1ZOG7cHFq89xj8iK5cjNzRWO7RQ2WX
7WbpwTj3ePmpktiBMaDA0C5mXfkP2mTOD/jfCmgR6f+z2zNbj9zAgO93at9+yDUl
AFPm2j7rQgBTa+HhACb+h6HDZebDMNsuqzmaTWZuJ+wr89VWV5c17QKBgH3jwNNQ
mCAIUidynaulQNfTOZIe7IMC7WK7g9CBmPkx7Y0uiXr6C25hCdJKFllLTP6vNWOy
uCcQqf8LhgDiilBDifO3op9xpyuOJlWMYocJVkxx3l2L/rSU07PYcbKNAFAxXuJ4
xym51qZnkznMN5ei/CPFxVKeqHgaXDpekVStAoGAV3pSWAKDXY/42XEHixrCTqLW
kBxfaf3g7iFnl3u8+7Z/7Cb4ZqFcw0bRJseKuR9mFvBhcZxSErbMDEYrevefU9aM
APeCxEyw6hJXgbWKoG7Fw2g2HP3ytCJ4YzH0zNitHjk/1h4BG7z8cEQILCSv5mN2
etFcaQuTHEZyRhhJ4BU=
-----END PRIVATE KEY-----`
-60
View File
@@ -1,60 +0,0 @@
package scaleset
import (
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestClient_Do(t *testing.T) {
t.Run("trims byte order mark from response if present", func(t *testing.T) {
t.Run("when there is no body", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
}))
defer server.Close()
client, err := NewClient("https://localhost/org/repo", &ActionsAuth{Token: "token"})
require.NoError(t, err)
req, err := http.NewRequest("GET", server.URL, nil)
require.NoError(t, err)
resp, err := client.do(req)
require.NoError(t, err)
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
assert.Empty(t, string(body))
})
responses := []string{
"\xef\xbb\xbf{\"foo\":\"bar\"}",
"{\"foo\":\"bar\"}",
}
for _, response := range responses {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(response))
}))
defer server.Close()
client, err := NewClient("https://localhost/org/repo", &ActionsAuth{Token: "token"})
require.NoError(t, err)
req, err := http.NewRequest("GET", server.URL, nil)
require.NoError(t, err)
resp, err := client.do(req)
require.NoError(t, err)
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
assert.Equal(t, "{\"foo\":\"bar\"}", string(body))
}
})
}
-60
View File
@@ -1,60 +0,0 @@
package scaleset
import (
"context"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGenerateJitRunnerConfig(t *testing.T) {
ctx := context.Background()
auth := &ActionsAuth{
Token: "token",
}
t.Run("Get JIT Config for Runner", func(t *testing.T) {
want := &RunnerScaleSetJitRunnerConfig{}
response := []byte(`{"count":1,"value":[{"id":1,"name":"scale-set-name"}]}`)
runnerSettings := &RunnerScaleSetJitRunnerSetting{}
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Write(response)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
got, err := client.GenerateJitRunnerConfig(ctx, runnerSettings, 1)
require.NoError(t, err)
assert.Equal(t, want, got)
})
t.Run("Default retries on server error", func(t *testing.T) {
runnerSettings := &RunnerScaleSetJitRunnerSetting{}
retryMax := 1
actualRetry := 0
expectedRetry := retryMax + 1
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusServiceUnavailable)
actualRetry++
}))
client, err := NewClient(
server.configURLForOrg("my-org"),
auth,
WithRetryMax(1),
WithRetryWaitMax(1*time.Millisecond),
)
require.NoError(t, err)
_, err = client.GenerateJitRunnerConfig(ctx, runnerSettings, 1)
assert.NotNil(t, err)
assert.Equalf(t, actualRetry, expectedRetry, "A retry was expected after the first request but got: %v", actualRetry)
})
}
-38
View File
@@ -1,38 +0,0 @@
package scaleset
import (
"net/http"
"net/url"
"testing"
"github.com/actions/scaleset/testserver"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/net/http/httpproxy"
)
func TestClientProxy(t *testing.T) {
serverCalled := false
proxy := testserver.New(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
serverCalled = true
}))
proxyConfig := &httpproxy.Config{
HTTPProxy: proxy.URL,
}
proxyFunc := func(req *http.Request) (*url.URL, error) {
return proxyConfig.ProxyFunc()(req.URL)
}
c, err := NewClient("http://github.com/org/repo", nil, WithProxy(proxyFunc))
require.NoError(t, err)
req, err := http.NewRequest(http.MethodGet, "http://example.com", nil)
require.NoError(t, err)
_, err = c.do(req)
require.NoError(t, err)
assert.True(t, serverCalled)
}
-247
View File
@@ -1,247 +0,0 @@
package scaleset
import (
"context"
"encoding/json"
"errors"
"net/http"
"strconv"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGetMessage(t *testing.T) {
ctx := context.Background()
auth := &ActionsAuth{
Token: "token",
}
token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjI1MTYyMzkwMjJ9.tlrHslTmDkoqnc4Kk9ISoKoUNDfHo-kjlH-ByISBqzE"
runnerScaleSetMessage := &RunnerScaleSetMessage{
MessageID: 1,
MessageType: "rssType",
}
t.Run("Get Runner Scale Set Message", func(t *testing.T) {
want := runnerScaleSetMessage
response := []byte(`{"messageId":1,"messageType":"rssType"}`)
s := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Write(response)
}))
client, err := NewClient(s.configURLForOrg("my-org"), auth)
require.NoError(t, err)
got, err := client.GetMessage(ctx, s.URL, token, 0, 10)
require.NoError(t, err)
assert.Equal(t, want, got)
})
t.Run("GetMessage sets the last message id if not 0", func(t *testing.T) {
want := runnerScaleSetMessage
response := []byte(`{"messageId":1,"messageType":"rssType"}`)
s := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query()
assert.Equal(t, "1", q.Get("lastMessageId"))
w.Write(response)
}))
client, err := NewClient(s.configURLForOrg("my-org"), auth)
require.NoError(t, err)
got, err := client.GetMessage(ctx, s.URL, token, 1, 10)
require.NoError(t, err)
assert.Equal(t, want, got)
})
t.Run("Default retries on server error", func(t *testing.T) {
retryMax := 1
actualRetry := 0
expectedRetry := retryMax + 1
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusServiceUnavailable)
actualRetry++
}))
client, err := NewClient(
server.configURLForOrg("my-org"),
auth,
WithRetryMax(retryMax),
WithRetryWaitMax(1*time.Millisecond),
)
require.NoError(t, err)
_, err = client.GetMessage(ctx, server.URL, token, 0, 10)
assert.NotNil(t, err)
assert.Equalf(t, actualRetry, expectedRetry, "A retry was expected after the first request but got: %v", actualRetry)
})
t.Run("Message token expired", func(t *testing.T) {
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
_, err = client.GetMessage(ctx, server.URL, token, 0, 10)
require.NotNil(t, err)
var expectedErr *MessageQueueTokenExpiredError
require.True(t, errors.As(err, &expectedErr))
})
t.Run("Status code not found", func(t *testing.T) {
want := ActionsError{
Err: errors.New("unknown exception"),
StatusCode: 404,
}
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusNotFound)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
_, err = client.GetMessage(ctx, server.URL, token, 0, 10)
require.NotNil(t, err)
assert.Equal(t, want.Error(), err.Error())
})
t.Run("Error when Content-Type is text/plain", func(t *testing.T) {
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusBadRequest)
w.Header().Set("Content-Type", "text/plain")
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
_, err = client.GetMessage(ctx, server.URL, token, 0, 10)
assert.NotNil(t, err)
})
t.Run("Capacity error handling", func(t *testing.T) {
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
hc := r.Header.Get(HeaderScaleSetMaxCapacity)
c, err := strconv.Atoi(hc)
require.NoError(t, err)
assert.GreaterOrEqual(t, c, 0)
w.WriteHeader(http.StatusBadRequest)
w.Header().Set("Content-Type", "text/plain")
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
_, err = client.GetMessage(ctx, server.URL, token, 0, -1)
require.Error(t, err)
// Ensure we don't send requests with negative capacity
assert.False(t, errors.Is(err, &ActionsError{}))
_, err = client.GetMessage(ctx, server.URL, token, 0, 0)
assert.Error(t, err)
var expectedErr *ActionsError
assert.ErrorAs(t, err, &expectedErr)
assert.Equal(t, http.StatusBadRequest, expectedErr.StatusCode)
})
}
func TestDeleteMessage(t *testing.T) {
ctx := context.Background()
auth := &ActionsAuth{
Token: "token",
}
token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjI1MTYyMzkwMjJ9.tlrHslTmDkoqnc4Kk9ISoKoUNDfHo-kjlH-ByISBqzE"
runnerScaleSetMessage := &RunnerScaleSetMessage{
MessageID: 1,
MessageType: "rssType",
}
t.Run("Delete existing message", func(t *testing.T) {
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusNoContent)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
err = client.DeleteMessage(ctx, server.URL, token, runnerScaleSetMessage.MessageID)
assert.Nil(t, err)
})
t.Run("Message token expired", func(t *testing.T) {
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
err = client.DeleteMessage(ctx, server.URL, token, 0)
require.NotNil(t, err)
var expectedErr *MessageQueueTokenExpiredError
assert.True(t, errors.As(err, &expectedErr))
})
t.Run("Error when Content-Type is text/plain", func(t *testing.T) {
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusBadRequest)
w.Header().Set("Content-Type", "text/plain")
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
err = client.DeleteMessage(ctx, server.URL, token, runnerScaleSetMessage.MessageID)
require.NotNil(t, err)
var expectedErr *ActionsError
assert.True(t, errors.As(err, &expectedErr))
},
)
t.Run("Default retries on server error", func(t *testing.T) {
actualRetry := 0
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusServiceUnavailable)
actualRetry++
}))
retryMax := 1
client, err := NewClient(
server.configURLForOrg("my-org"),
auth,
WithRetryMax(retryMax),
WithRetryWaitMax(1*time.Nanosecond),
)
require.NoError(t, err)
err = client.DeleteMessage(ctx, server.URL, token, runnerScaleSetMessage.MessageID)
assert.NotNil(t, err)
expectedRetry := retryMax + 1
assert.Equalf(t, actualRetry, expectedRetry, "A retry was expected after the first request but got: %v", actualRetry)
})
t.Run("No message found", func(t *testing.T) {
want := (*RunnerScaleSetMessage)(nil)
rsl, err := json.Marshal(want)
require.NoError(t, err)
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Write(rsl)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
err = client.DeleteMessage(ctx, server.URL, token, runnerScaleSetMessage.MessageID+1)
var expectedErr *ActionsError
require.True(t, errors.As(err, &expectedErr))
})
}
-219
View File
@@ -1,219 +0,0 @@
package scaleset
import (
"context"
"errors"
"net/http"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const exampleRequestID = "5ddf2050-dae0-013c-9159-04421ad31b68"
func TestCreateMessageSession(t *testing.T) {
ctx := context.Background()
auth := &ActionsAuth{
Token: "token",
}
t.Run("CreateMessageSession unmarshals correctly", func(t *testing.T) {
owner := "foo"
runnerScaleSet := RunnerScaleSet{
ID: 1,
Name: "ScaleSet",
CreatedOn: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
RunnerSetting: RunnerSetting{},
}
want := &RunnerScaleSetSession{
OwnerName: "foo",
RunnerScaleSet: &RunnerScaleSet{
ID: 1,
Name: "ScaleSet",
},
MessageQueueURL: "http://fake.github.com/123",
MessageQueueAccessToken: "fake.jwt.here",
}
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
resp := []byte(`{
"ownerName": "foo",
"runnerScaleSet": {
"id": 1,
"name": "ScaleSet"
},
"messageQueueUrl": "http://fake.github.com/123",
"messageQueueAccessToken": "fake.jwt.here"
}`)
w.Write(resp)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
got, err := client.CreateMessageSession(ctx, runnerScaleSet.ID, owner)
require.NoError(t, err)
assert.Equal(t, want, got)
})
t.Run("CreateMessageSession unmarshals errors into ActionsError", func(t *testing.T) {
owner := "foo"
runnerScaleSet := RunnerScaleSet{
ID: 1,
Name: "ScaleSet",
CreatedOn: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
RunnerSetting: RunnerSetting{},
}
want := &ActionsError{
ActivityID: exampleRequestID,
StatusCode: http.StatusBadRequest,
Err: &ActionsExceptionError{
ExceptionName: "CSharpExceptionNameHere",
Message: "could not do something",
},
}
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set(headerActionsActivityID, exampleRequestID)
w.WriteHeader(http.StatusBadRequest)
resp := []byte(`{"typeName": "CSharpExceptionNameHere","message": "could not do something"}`)
w.Write(resp)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
_, err = client.CreateMessageSession(ctx, runnerScaleSet.ID, owner)
require.NotNil(t, err)
errorTypeForComparison := &ActionsError{}
assert.True(
t,
errors.As(err, &errorTypeForComparison),
"CreateMessageSession expected to be able to parse the error into ActionsError type: %v",
err,
)
assert.Equal(t, want, errorTypeForComparison)
})
t.Run("CreateMessageSession call is retried the correct amount of times", func(t *testing.T) {
owner := "foo"
runnerScaleSet := RunnerScaleSet{
ID: 1,
Name: "ScaleSet",
CreatedOn: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
RunnerSetting: RunnerSetting{},
}
gotRetries := 0
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
gotRetries++
}))
retryMax := 3
retryWaitMax := 1 * time.Microsecond
wantRetries := retryMax + 1
client, err := NewClient(
server.configURLForOrg("my-org"),
auth,
WithRetryMax(retryMax),
WithRetryWaitMax(retryWaitMax),
)
require.NoError(t, err)
_, err = client.CreateMessageSession(ctx, runnerScaleSet.ID, owner)
assert.NotNil(t, err)
assert.Equalf(t, gotRetries, wantRetries, "CreateMessageSession got unexpected retry count: got=%v, want=%v", gotRetries, wantRetries)
})
}
func TestDeleteMessageSession(t *testing.T) {
ctx := context.Background()
auth := &ActionsAuth{
Token: "token",
}
t.Run("DeleteMessageSession call is retried the correct amount of times", func(t *testing.T) {
runnerScaleSet := RunnerScaleSet{
ID: 1,
Name: "ScaleSet",
CreatedOn: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
RunnerSetting: RunnerSetting{},
}
gotRetries := 0
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
gotRetries++
}))
retryMax := 3
retryWaitMax := 1 * time.Microsecond
wantRetries := retryMax + 1
client, err := NewClient(
server.configURLForOrg("my-org"),
auth,
WithRetryMax(retryMax),
WithRetryWaitMax(retryWaitMax),
)
require.NoError(t, err)
sessionID := uuid.New()
err = client.DeleteMessageSession(ctx, runnerScaleSet.ID, sessionID)
assert.NotNil(t, err)
assert.Equalf(t, gotRetries, wantRetries, "CreateMessageSession got unexpected retry count: got=%v, want=%v", gotRetries, wantRetries)
})
}
func TestRefreshMessageSession(t *testing.T) {
auth := &ActionsAuth{
Token: "token",
}
t.Run("RefreshMessageSession call is retried the correct amount of times", func(t *testing.T) {
runnerScaleSet := RunnerScaleSet{
ID: 1,
Name: "ScaleSet",
CreatedOn: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
RunnerSetting: RunnerSetting{},
}
gotRetries := 0
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
gotRetries++
}))
retryMax := 3
retryWaitMax := 1 * time.Microsecond
wantRetries := retryMax + 1
client, err := NewClient(
server.configURLForOrg("my-org"),
auth,
WithRetryMax(retryMax),
WithRetryWaitMax(retryWaitMax),
)
require.NoError(t, err)
sessionID := uuid.New()
_, err = client.RefreshMessageSession(context.Background(), runnerScaleSet.ID, sessionID)
assert.NotNil(t, err)
assert.Equalf(t, gotRetries, wantRetries, "CreateMessageSession got unexpected retry count: got=%v, want=%v", gotRetries, wantRetries)
})
}
-423
View File
@@ -1,423 +0,0 @@
package scaleset
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGetRunnerScaleSet(t *testing.T) {
ctx := context.Background()
auth := &ActionsAuth{
Token: "token",
}
scaleSetName := "ScaleSet"
runnerScaleSet := RunnerScaleSet{ID: 1, Name: scaleSetName}
t.Run("Get existing scale set", func(t *testing.T) {
want := &runnerScaleSet
runnerScaleSetsResp := []byte(`{"count":1,"value":[{"id":1,"name":"ScaleSet"}]}`)
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Write(runnerScaleSetsResp)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
got, err := client.GetRunnerScaleSet(ctx, 1, scaleSetName)
require.NoError(t, err)
assert.Equal(t, want, got)
})
t.Run("GetRunnerScaleSet calls correct url", func(t *testing.T) {
runnerScaleSetsResp := []byte(`{"count":1,"value":[{"id":1,"name":"ScaleSet"}]}`)
url := url.URL{}
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write(runnerScaleSetsResp)
url = *r.URL
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
_, err = client.GetRunnerScaleSet(ctx, 1, scaleSetName)
require.NoError(t, err)
expectedPath := "/tenant/123/_apis/runtime/runnerscalesets"
assert.Equal(t, expectedPath, url.Path)
assert.Equal(t, scaleSetName, url.Query().Get("name"))
assert.Equal(t, "6.0-preview", url.Query().Get("api-version"))
})
t.Run("Status code not found", func(t *testing.T) {
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusNotFound)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
_, err = client.GetRunnerScaleSet(ctx, 1, scaleSetName)
assert.NotNil(t, err)
})
t.Run("Error when Content-Type is text/plain", func(t *testing.T) {
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusBadRequest)
w.Header().Set("Content-Type", "text/plain")
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
_, err = client.GetRunnerScaleSet(ctx, 1, scaleSetName)
assert.NotNil(t, err)
})
t.Run("Default retries on server error", func(t *testing.T) {
actualRetry := 0
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusServiceUnavailable)
actualRetry++
}))
retryMax := 1
retryWaitMax := 1 * time.Microsecond
client, err := NewClient(
server.configURLForOrg("my-org"),
auth,
WithRetryMax(retryMax),
WithRetryWaitMax(retryWaitMax),
)
require.NoError(t, err)
_, err = client.GetRunnerScaleSet(ctx, 1, scaleSetName)
assert.NotNil(t, err)
expectedRetry := retryMax + 1
assert.Equalf(t, actualRetry, expectedRetry, "A retry was expected after the first request but got: %v", actualRetry)
})
t.Run("RunnerScaleSet count is zero", func(t *testing.T) {
want := (*RunnerScaleSet)(nil)
runnerScaleSetsResp := []byte(`{"count":0,"value":[{"id":1,"name":"ScaleSet"}]}`)
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Write(runnerScaleSetsResp)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
got, err := client.GetRunnerScaleSet(ctx, 1, scaleSetName)
require.NoError(t, err)
assert.Equal(t, want, got)
})
t.Run("Multiple runner scale sets found", func(t *testing.T) {
reqID := uuid.NewString()
wantErr := &ActionsError{
StatusCode: http.StatusOK,
ActivityID: reqID,
Err: fmt.Errorf("multiple runner scale sets found with name %q", scaleSetName),
}
runnerScaleSetsResp := []byte(`{"count":2,"value":[{"id":1,"name":"ScaleSet"}]}`)
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set(headerActionsActivityID, reqID)
w.Write(runnerScaleSetsResp)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
_, err = client.GetRunnerScaleSet(ctx, 1, scaleSetName)
require.NotNil(t, err)
assert.Equal(t, wantErr.Error(), err.Error())
})
}
func TestGetRunnerScaleSetByID(t *testing.T) {
ctx := context.Background()
auth := &ActionsAuth{
Token: "token",
}
scaleSetCreationDateTime := time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)
runnerScaleSet := RunnerScaleSet{ID: 1, Name: "ScaleSet", CreatedOn: scaleSetCreationDateTime, RunnerSetting: RunnerSetting{}}
t.Run("Get existing scale set by Id", func(t *testing.T) {
want := &runnerScaleSet
rsl, err := json.Marshal(want)
require.NoError(t, err)
sservere := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Write(rsl)
}))
client, err := NewClient(sservere.configURLForOrg("my-org"), auth)
require.NoError(t, err)
got, err := client.GetRunnerScaleSetByID(ctx, runnerScaleSet.ID)
require.NoError(t, err)
assert.Equal(t, want, got)
})
t.Run("GetRunnerScaleSetByID calls correct url", func(t *testing.T) {
rsl, err := json.Marshal(&runnerScaleSet)
require.NoError(t, err)
url := url.URL{}
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write(rsl)
url = *r.URL
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
_, err = client.GetRunnerScaleSetByID(ctx, runnerScaleSet.ID)
require.NoError(t, err)
expectedPath := fmt.Sprintf("/tenant/123/_apis/runtime/runnerscalesets/%d", runnerScaleSet.ID)
assert.Equal(t, expectedPath, url.Path)
assert.Equal(t, "6.0-preview", url.Query().Get("api-version"))
})
t.Run("Status code not found", func(t *testing.T) {
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusNotFound)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
_, err = client.GetRunnerScaleSetByID(ctx, runnerScaleSet.ID)
assert.NotNil(t, err)
})
t.Run("Error when Content-Type is text/plain", func(t *testing.T) {
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusBadRequest)
w.Header().Set("Content-Type", "text/plain")
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
_, err = client.GetRunnerScaleSetByID(ctx, runnerScaleSet.ID)
assert.NotNil(t, err)
})
t.Run("Default retries on server error", func(t *testing.T) {
actualRetry := 0
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusServiceUnavailable)
actualRetry++
}))
retryMax := 1
retryWaitMax := 1 * time.Microsecond
client, err := NewClient(
server.configURLForOrg("my-org"),
auth,
WithRetryMax(retryMax),
WithRetryWaitMax(retryWaitMax),
)
require.NoError(t, err)
_, err = client.GetRunnerScaleSetByID(ctx, runnerScaleSet.ID)
require.NotNil(t, err)
expectedRetry := retryMax + 1
assert.Equalf(t, actualRetry, expectedRetry, "A retry was expected after the first request but got: %v", actualRetry)
})
t.Run("No RunnerScaleSet found", func(t *testing.T) {
want := (*RunnerScaleSet)(nil)
rsl, err := json.Marshal(want)
require.NoError(t, err)
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Write(rsl)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
got, err := client.GetRunnerScaleSetByID(ctx, runnerScaleSet.ID)
require.NoError(t, err)
assert.Equal(t, want, got)
})
}
func TestCreateRunnerScaleSet(t *testing.T) {
ctx := context.Background()
auth := &ActionsAuth{
Token: "token",
}
scaleSetCreationDateTime := time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)
runnerScaleSet := RunnerScaleSet{ID: 1, Name: "ScaleSet", CreatedOn: scaleSetCreationDateTime, RunnerSetting: RunnerSetting{}}
t.Run("Create runner scale set", func(t *testing.T) {
want := &runnerScaleSet
rsl, err := json.Marshal(want)
require.NoError(t, err)
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Write(rsl)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
got, err := client.CreateRunnerScaleSet(ctx, &runnerScaleSet)
require.NoError(t, err)
assert.Equal(t, want, got)
})
t.Run("CreateRunnerScaleSet calls correct url", func(t *testing.T) {
rsl, err := json.Marshal(&runnerScaleSet)
require.NoError(t, err)
url := url.URL{}
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write(rsl)
url = *r.URL
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
_, err = client.CreateRunnerScaleSet(ctx, &runnerScaleSet)
require.NoError(t, err)
expectedPath := "/tenant/123/_apis/runtime/runnerscalesets"
assert.Equal(t, expectedPath, url.Path)
assert.Equal(t, "6.0-preview", url.Query().Get("api-version"))
})
t.Run("Error when Content-Type is text/plain", func(t *testing.T) {
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusBadRequest)
w.Header().Set("Content-Type", "text/plain")
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
_, err = client.CreateRunnerScaleSet(ctx, &runnerScaleSet)
require.NotNil(t, err)
var expectedErr *ActionsError
assert.True(t, errors.As(err, &expectedErr))
})
t.Run("Default retries on server error", func(t *testing.T) {
actualRetry := 0
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusServiceUnavailable)
actualRetry++
}))
retryMax := 1
retryWaitMax := 1 * time.Microsecond
client, err := NewClient(
server.configURLForOrg("my-org"),
auth,
WithRetryMax(retryMax),
WithRetryWaitMax(retryWaitMax),
)
require.NoError(t, err)
_, err = client.CreateRunnerScaleSet(ctx, &runnerScaleSet)
require.NotNil(t, err)
expectedRetry := retryMax + 1
assert.Equalf(t, actualRetry, expectedRetry, "A retry was expected after the first request but got: %v", actualRetry)
})
}
func TestUpdateRunnerScaleSet(t *testing.T) {
ctx := context.Background()
auth := &ActionsAuth{
Token: "token",
}
scaleSetCreationDateTime := time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)
runnerScaleSet := RunnerScaleSet{ID: 1, Name: "ScaleSet", RunnerGroupID: 1, RunnerGroupName: "group", CreatedOn: scaleSetCreationDateTime, RunnerSetting: RunnerSetting{}}
t.Run("Update runner scale set", func(t *testing.T) {
want := &runnerScaleSet
rsl, err := json.Marshal(want)
require.NoError(t, err)
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Write(rsl)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
got, err := client.UpdateRunnerScaleSet(ctx, 1, &RunnerScaleSet{RunnerGroupID: 1})
require.NoError(t, err)
assert.Equal(t, want, got)
})
t.Run("UpdateRunnerScaleSet calls correct url", func(t *testing.T) {
rsl, err := json.Marshal(&runnerScaleSet)
require.NoError(t, err)
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
expectedPath := "/tenant/123/_apis/runtime/runnerscalesets/1"
assert.Equal(t, expectedPath, r.URL.Path)
assert.Equal(t, http.MethodPatch, r.Method)
assert.Equal(t, "6.0-preview", r.URL.Query().Get("api-version"))
w.Write(rsl)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
_, err = client.UpdateRunnerScaleSet(ctx, 1, &runnerScaleSet)
require.NoError(t, err)
})
}
func TestDeleteRunnerScaleSet(t *testing.T) {
ctx := context.Background()
auth := &ActionsAuth{
Token: "token",
}
t.Run("Delete runner scale set", func(t *testing.T) {
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
assert.Contains(t, r.URL.String(), "/_apis/runtime/runnerscalesets/10?api-version=6.0-preview")
w.WriteHeader(http.StatusNoContent)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
err = client.DeleteRunnerScaleSet(ctx, 10)
assert.NoError(t, err)
})
t.Run("Delete calls with error", func(t *testing.T) {
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
assert.Contains(t, r.URL.String(), "/_apis/runtime/runnerscalesets/10?api-version=6.0-preview")
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(`{"message": "test error"}`))
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
err = client.DeleteRunnerScaleSet(ctx, 10)
assert.ErrorContains(t, err, "test error")
})
}
-217
View File
@@ -1,217 +0,0 @@
package scaleset
import (
"context"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGetRunner(t *testing.T) {
ctx := context.Background()
auth := &ActionsAuth{
Token: "token",
}
t.Run("Get Runner", func(t *testing.T) {
var runnerID int64 = 1
want := &RunnerReference{
ID: int(runnerID),
Name: "self-hosted-ubuntu",
}
response := []byte(`{"id": 1, "name": "self-hosted-ubuntu"}`)
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write(response)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
got, err := client.GetRunner(ctx, runnerID)
require.NoError(t, err)
assert.Equal(t, want, got)
})
t.Run("Default retries on server error", func(t *testing.T) {
var runnerID int64 = 1
retryWaitMax := 1 * time.Millisecond
retryMax := 1
actualRetry := 0
expectedRetry := retryMax + 1
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusServiceUnavailable)
actualRetry++
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth, WithRetryMax(retryMax), WithRetryWaitMax(retryWaitMax))
require.NoError(t, err)
_, err = client.GetRunner(ctx, runnerID)
require.Error(t, err)
assert.Equalf(t, actualRetry, expectedRetry, "A retry was expected after the first request but got: %v", actualRetry)
})
}
func TestGetRunnerByName(t *testing.T) {
ctx := context.Background()
auth := &ActionsAuth{
Token: "token",
}
t.Run("Get Runner by Name", func(t *testing.T) {
var runnerID int64 = 1
runnerName := "self-hosted-ubuntu"
want := &RunnerReference{
ID: int(runnerID),
Name: runnerName,
}
response := []byte(`{"count": 1, "value": [{"id": 1, "name": "self-hosted-ubuntu"}]}`)
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write(response)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
got, err := client.GetRunnerByName(ctx, runnerName)
require.NoError(t, err)
assert.Equal(t, want, got)
})
t.Run("Get Runner by name with not exist runner", func(t *testing.T) {
runnerName := "self-hosted-ubuntu"
response := []byte(`{"count": 0, "value": []}`)
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write(response)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
got, err := client.GetRunnerByName(ctx, runnerName)
require.NoError(t, err)
assert.Nil(t, got)
})
t.Run("Default retries on server error", func(t *testing.T) {
runnerName := "self-hosted-ubuntu"
retryWaitMax := 1 * time.Millisecond
retryMax := 1
actualRetry := 0
expectedRetry := retryMax + 1
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusServiceUnavailable)
actualRetry++
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth, WithRetryMax(retryMax), WithRetryWaitMax(retryWaitMax))
require.NoError(t, err)
_, err = client.GetRunnerByName(ctx, runnerName)
require.Error(t, err)
assert.Equalf(t, actualRetry, expectedRetry, "A retry was expected after the first request but got: %v", actualRetry)
})
}
func TestDeleteRunner(t *testing.T) {
ctx := context.Background()
auth := &ActionsAuth{
Token: "token",
}
t.Run("Delete Runner", func(t *testing.T) {
var runnerID int64 = 1
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusNoContent)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
err = client.RemoveRunner(ctx, runnerID)
assert.NoError(t, err)
})
t.Run("Default retries on server error", func(t *testing.T) {
var runnerID int64 = 1
retryWaitMax := 1 * time.Millisecond
retryMax := 1
actualRetry := 0
expectedRetry := retryMax + 1
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusServiceUnavailable)
actualRetry++
}))
client, err := NewClient(
server.configURLForOrg("my-org"),
auth,
WithRetryMax(retryMax),
WithRetryWaitMax(retryWaitMax),
)
require.NoError(t, err)
err = client.RemoveRunner(ctx, runnerID)
require.Error(t, err)
assert.Equalf(t, actualRetry, expectedRetry, "A retry was expected after the first request but got: %v", actualRetry)
})
}
func TestGetRunnerGroupByName(t *testing.T) {
ctx := context.Background()
auth := &ActionsAuth{
Token: "token",
}
t.Run("Get RunnerGroup by Name", func(t *testing.T) {
runnerGroupID := 1
runnerGroupName := "test-runner-group"
want := &RunnerGroup{
ID: runnerGroupID,
Name: runnerGroupName,
}
response := []byte(`{"count": 1, "value": [{"id": 1, "name": "test-runner-group"}]}`)
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write(response)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
got, err := client.GetRunnerGroupByName(ctx, runnerGroupName)
require.NoError(t, err)
assert.Equal(t, want, got)
})
t.Run("Get RunnerGroup by name with not exist runner group", func(t *testing.T) {
runnerGroupName := "test-runner-group"
response := []byte(`{"count": 0, "value": []}`)
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write(response)
}))
client, err := NewClient(server.configURLForOrg("my-org"), auth)
require.NoError(t, err)
got, err := client.GetRunnerGroupByName(ctx, runnerGroupName)
assert.ErrorContains(t, err, "no runner group found with name")
assert.Nil(t, got)
})
}
+1710
View File
File diff suppressed because it is too large Load Diff
-177
View File
@@ -1,177 +0,0 @@
package scaleset
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestServerWithSelfSignedCertificates(t *testing.T) {
ctx := context.Background()
// this handler is a very very barebones replica of actions api
// used during the creation of a a new client
var u string
h := func(w http.ResponseWriter, r *http.Request) {
// handle get registration token
if strings.HasSuffix(r.URL.Path, "/runners/registration-token") {
w.WriteHeader(http.StatusCreated)
w.Write([]byte(`{"token":"token"}`))
return
}
// handle getActionsServiceAdminConnection
if strings.HasSuffix(r.URL.Path, "/actions/runner-registration") {
claims := &jwt.RegisteredClaims{
IssuedAt: jwt.NewNumericDate(time.Now().Add(-1 * time.Minute)),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Minute)),
Issuer: "123",
}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(samplePrivateKey))
require.NoError(t, err)
tokenString, err := token.SignedString(privateKey)
require.NoError(t, err)
w.Write([]byte(`{"url":"` + u + `","token":"` + tokenString + `"}`))
return
}
// default happy response for RemoveRunner
w.WriteHeader(http.StatusNoContent)
}
certPath := filepath.Join("testdata", "server.crt")
keyPath := filepath.Join("testdata", "server.key")
t.Run("client without ca certs", func(t *testing.T) {
server := startNewTLSTestServer(t, certPath, keyPath, http.HandlerFunc(h))
u = server.URL
configURL := server.URL + "/my-org"
auth := &ActionsAuth{
Token: "token",
}
client, err := NewClient(configURL, auth)
require.NoError(t, err)
require.NotNil(t, client)
err = client.RemoveRunner(ctx, 1)
require.NotNil(t, err)
if runtime.GOOS == "linux" {
assert.True(t, errors.As(err, &x509.UnknownAuthorityError{}))
}
// on macOS we only get an untyped error from the system verifying the
// certificate
if runtime.GOOS == "darwin" {
assert.True(t, strings.HasSuffix(err.Error(), "certificate is not trusted"))
}
})
t.Run("client with ca certs", func(t *testing.T) {
server := startNewTLSTestServer(
t,
certPath,
keyPath,
http.HandlerFunc(h),
)
u = server.URL
configURL := server.URL + "/my-org"
auth := &ActionsAuth{
Token: "token",
}
cert, err := os.ReadFile(filepath.Join("testdata", "rootCA.crt"))
require.NoError(t, err)
pool := x509.NewCertPool()
require.True(t, pool.AppendCertsFromPEM(cert))
client, err := NewClient(
configURL,
auth,
WithRootCAs(pool),
)
require.NoError(t, err)
assert.NotNil(t, client)
err = client.RemoveRunner(ctx, 1)
assert.NoError(t, err)
})
t.Run("client with ca chain certs", func(t *testing.T) {
server := startNewTLSTestServer(
t,
filepath.Join("testdata", "leaf.crt"),
filepath.Join("testdata", "leaf.key"),
http.HandlerFunc(h),
)
u = server.URL
configURL := server.URL + "/my-org"
auth := &ActionsAuth{
Token: "token",
}
cert, err := os.ReadFile(filepath.Join("testdata", "intermediate.crt"))
require.NoError(t, err)
pool := x509.NewCertPool()
require.True(t, pool.AppendCertsFromPEM(cert))
client, err := NewClient(
configURL,
auth,
WithRootCAs(pool),
WithRetryMax(0),
)
require.NoError(t, err)
require.NotNil(t, client)
err = client.RemoveRunner(ctx, 1)
assert.NoError(t, err)
})
t.Run("client skipping tls verification", func(t *testing.T) {
server := startNewTLSTestServer(t, certPath, keyPath, http.HandlerFunc(h))
configURL := server.URL + "/my-org"
auth := &ActionsAuth{
Token: "token",
}
client, err := NewClient(configURL, auth, WithoutTLSVerify())
require.NoError(t, err)
assert.NotNil(t, client)
})
}
func startNewTLSTestServer(t *testing.T, certPath, keyPath string, handler http.Handler) *httptest.Server {
server := httptest.NewUnstartedServer(handler)
t.Cleanup(func() {
server.Close()
})
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
require.NoError(t, err)
server.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
server.StartTLS()
return server
}
-247
View File
@@ -1,247 +0,0 @@
package scaleset
import (
"context"
"encoding/json"
"io"
"net/http"
"net/url"
"strings"
"testing"
"time"
"github.com/actions/scaleset/testserver"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var testUserAgent = 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 := 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 := 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 := 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 := &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 := 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 := 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 := 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 := &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 := 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 := 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 := 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 := 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"))
})
}
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
# Generate Root CA
openssl genrsa -out rootCA.key 2048
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.crt -subj "/CN=Test Root CA" \
-addext "basicConstraints = critical, CA:TRUE" \
-addext "keyUsage = critical, keyCertSign, cRLSign"
# Generate Intermediate Certificate
openssl genrsa -out intermediate.key 2048
openssl req -new -key intermediate.key -out intermediate.csr -subj "/CN=Test Intermediate CA"
openssl x509 -req -in intermediate.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out intermediate.crt -days 1000 -sha256 \
-extfile <(echo -e "basicConstraints = critical, CA:TRUE, pathlen:0\nkeyUsage = critical, keyCertSign, cRLSign")
# Generate Leaf Certificate
openssl genrsa -out leaf.key 2048
openssl req -new -key leaf.key -out leaf.csr -subj "/CN=localhost" \
-addext "subjectAltName = IP:127.0.0.1"
openssl x509 -req -in leaf.csr -CA intermediate.crt -CAkey intermediate.key -CAcreateserial -out leaf.crt -days 500 -sha256 \
-extfile <(echo -e "authorityKeyIdentifier=keyid,issuer\nbasicConstraints=CA:FALSE\nkeyUsage = digitalSignature, keyEncipherment\nextendedKeyUsage=serverAuth\nsubjectAltName=IP:127.0.0.1")
# Generate Leaf Certificate
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/CN=localhost" \
-addext "subjectAltName = IP:127.0.0.1"
openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 500 -sha256 \
-extfile <(echo -e "authorityKeyIdentifier=keyid,issuer\nbasicConstraints=CA:FALSE\nkeyUsage = digitalSignature, keyEncipherment\nextendedKeyUsage=serverAuth\nsubjectAltName=IP:127.0.0.1")
rm rootCA.key intermediate.key *.csr *.srl
+19
View File
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDKjCCAhKgAwIBAgIUQr7R8yN5+2and6ucUOPF6oIbD44wDQYJKoZIhvcNAQEL
BQAwFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTI1MDIyODEyMDEzMFoXDTI3
MTEyNTEyMDEzMFowHzEdMBsGA1UEAwwUVGVzdCBJbnRlcm1lZGlhdGUgQ0EwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDOGvN95wCkYO35qyJnf/RwTiDb
oEVaefKnZZny1JrO34MFjlAz8C/P5WwxNUzzbQLTPh5iTqFRU+vis6HPvV0HJEoI
wTfgBCZxcdY8fEIY96FGHLju3PzfxeJaVHyi+2cTtzU+oNp4OFF8huApjYXjaV4y
pAirPbiiP/cgtcT4L5WErQi0aGZkq+1YqY2duNFNIGPTEcXV4iN4IhuD9dpqdKFg
H0wmZDgH+VE/5ACXovU8j5cxCKOJGxTVMKVZlvxPH3w69Z85x3o5AAnyxwo8E2zo
TC1FJ1eFLsmYLZki6cGBzSkIl5QlLGHakWYh+JLu/pkfTL8t+AkY3hZJM96ZAgMB
AAGjZjBkMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMB0GA1Ud
DgQWBBSmAyntm95+KoyL3ffLBXZKSpk1VTAfBgNVHSMEGDAWgBR93+rQFjh+RUFX
df4dbNcfS2hbTzANBgkqhkiG9w0BAQsFAAOCAQEABopVFLGQf/LFH+OKVCOT8FCC
y/+o1B/U5jXVvbfwlSGScaiJGQ94FsuH59XJCGySQj77ZVTeElBtntoLXmOCFjyF
jKHCDfUpB4nzeqNMvTDzuoYyPS8DhoGfEnaCgJyKf6GU4p41502gH8mQRB7azzL7
5jW0aFatCA6G6T1oogHZpHf0ice80C2JkFbWHSE9JxqARbTc06wCDBiSBFTGZQDO
JaBIbn6FL3zSkKcpwgJEqDRavVuoDUlJPDqtTzjf/fMQGGR2LUFkceJpsQqf1jrF
1yTtEZ8gjR2g2Vj6IszUAgbc87xR0AgyGDVckiUdhlX2Y6KCqo2cl9LfSVpqtw==
-----END CERTIFICATE-----
+20
View File
@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDQjCCAiqgAwIBAgIUHT3JtqsYKs7NHv1LNyS9RYC7vsAwDQYJKoZIhvcNAQEL
BQAwHzEdMBsGA1UEAwwUVGVzdCBJbnRlcm1lZGlhdGUgQ0EwHhcNMjUwMjI4MTIw
MTMwWhcNMjYwNzEzMTIwMTMwWjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHwI/xSLgCuQrx+WsvupA8w4eMdSef
WGw523OJYPJkDYJGgSGsjVb9htba6vXYbGNohuluEAZIyT7GvmPezTokeVMkuSYT
lSV8xplFEtDlQhTzaI/cofbi7qtT91/5zS/w0JSaNosThGtZg/M4ZOiMj04m0NGK
Zz56l9Lpe/yM7fPda++D9xYEGSSdwK9CqqwF+cXN09d6IK1VINIIjT3Sdb9Sssok
GWmD7UUPLvwZ5379+HRs1K8AFXqvbkeWVYtrJwJMxJGVnNSeiqKGSmMEpP7tVNHl
s4V7oyQXd8KX+HpziiayjGy9giVteJJi/bAmUp+0+hTHBes5fOWI5JyDAgMBAAGj
gYAwfjAfBgNVHSMEGDAWgBSmAyntm95+KoyL3ffLBXZKSpk1VTAJBgNVHRMEAjAA
MAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHREECDAGhwR/
AAABMB0GA1UdDgQWBBTVdJE2lkGsNIU3LlEf3rN5fyaRkjANBgkqhkiG9w0BAQsF
AAOCAQEAo1klH9WMsPWTN9qN3tdud07eatulEKo/0okaph6MJ59ozseOzxrfpwL0
67Nr8yl+VwZqrRTBurp0n6G+n0j8UHfWjSrAqN4yUHl+heT0HpnLR2FE9YgZEmxR
bPfVbPBef/eJeE7/U6imfBYzzMajua+hg05sVHUNNdPaFOP+Xj47x8uQmf9w5/kf
MrylRUSgH5RRge4+2T5hmNM9tHfF6OfDHitrXnl+X6h/x/tkBvDcUXtKa5xuEcSg
WpmJKl3pKfXvdmCIrj9Vca+UD2Bntkk2jgDTLEPJAxMgrsQRhnUJclaunnd1NQbc
FmjFW7iaNvDVKt+vYqH8ff8U9iCB2g==
-----END CERTIFICATE-----
+28
View File
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDHwI/xSLgCuQrx
+WsvupA8w4eMdSefWGw523OJYPJkDYJGgSGsjVb9htba6vXYbGNohuluEAZIyT7G
vmPezTokeVMkuSYTlSV8xplFEtDlQhTzaI/cofbi7qtT91/5zS/w0JSaNosThGtZ
g/M4ZOiMj04m0NGKZz56l9Lpe/yM7fPda++D9xYEGSSdwK9CqqwF+cXN09d6IK1V
INIIjT3Sdb9SssokGWmD7UUPLvwZ5379+HRs1K8AFXqvbkeWVYtrJwJMxJGVnNSe
iqKGSmMEpP7tVNHls4V7oyQXd8KX+HpziiayjGy9giVteJJi/bAmUp+0+hTHBes5
fOWI5JyDAgMBAAECggEADanzbrrds3n68LByD5LAeRea9xWwfyrqRE7pqVUTX5q4
9Z+xsP7+G1uU6Oa3qHVJm4XXA+tesq3peGjfpgb92i7ebB2qKB7EsLNZGqt91KDf
lALsDFib7cwLtjOuwgSyKdPqxl3Cx7QAL+Bhy9LDQZIv82HHY6NKV9J43/XWQcGK
KNZyS1o0vBWvt135YeE3qfQA9Ww8GI3jWyk49QDOVtVNZ1HRQpXPkt5exAyU8JpW
Y3Y5VqyEcKPBRlw/scEc5CRuzIP3P06Y+NEuuvnlnDt/BR/wyyuPiZoIqGXhXUKe
oDzEmtVrvB5RmhRc1PoS1l1GBfBfVqwkLUmeSitn8QKBgQD1pC6Ukiqtm7vyhirU
ynE+Dik/gHBBW6fQalD7yZwStid1+HfIvoU+RB0wyPUVwbu7eOoMnjG2ChLYX7cV
UhAcu5ZWFhlc8OS5bGGMI99d4ueBqTYONqnCV3DhiWZhs8OesiF3hohE6jV3G5xC
ra8DzImMpujyMIWXvOwnPLRWCQKBgQDQLPwTvx472XbY1aWviQkzxMgxP8l9D+lO
nBaybarLxDa+89RMWidgJX2kGM7i6FgicGgSNpNDIYNLrVZYAz11PP6o2Oqa9ZaR
5IvnfhP1iOwSgIoC8weNSE+Y0Lw/w0IOW71+XsfgswhJG45eXV8hRqYpUEkjjl1x
nQM0hCshKwKBgCQDFfEiHK+nDT7Y/J6Fr2Rxnwp4QfzS+x9K9uRzAjacDdz1uFnt
1Ir0YXMtgwDVjjhF2cpPxunxQCIIpkax6TrNJZUpWD6P8nhcs1BgUfbptRcFP6+F
xA2B1EK8ag4Y1K0HYHCtgHzZ+Uyk95uu6uGbsu6z6aLYCj3crKJz+9xBAoGACOdT
pLiQ33hul9mTa42N4jPxaAHVaU7r6JvOcLU2D98FhGdDVjyo4HjaBdG1z4imdFqg
aN8Cr2VYiz0Pq1YAI+qG7cvRRO1qEjVXMoB29BJ2Hlh3Dqc8VHOaS+vpkUSVp62O
zj/ZhqfBm/bcwPZ3YiH2a1/usOGe54QSpgVdHt8CgYBr6xpKQSrQvgtiuv9kCzI+
WQYP2Xxj+zsQb29hagXY/JllKOl5aDGz2qMV2RgOWZabB/xxkAeTF4A26wWrmcq0
wsv96jEl1MtI3lB3bi/8Y/tctkNsp0drvDZdfnGMpzxDafECKdxdFvhO63p7yBOA
LG1OvFTywkxBuOUKsNsErQ==
-----END PRIVATE KEY-----
+19
View File
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDHzCCAgegAwIBAgIUUmc9nWf4fhGFNd0oCNE0CzOXMaEwDQYJKoZIhvcNAQEL
BQAwFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTI1MDIyODEyMDEyOVoXDTI3
MTIxOTEyMDEyOVowFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw5ZFoDGTL0YyrwEA7qTu32cur0sQXDV86Xwl
G0ilk2DXXH4F70ruTTW3NG0Rniw/rt2jzJADo1Tlosq9eJKQGQKAr21N5kjhlU3J
8nFBK+1WJyG27EvyeqZOCucXOJaAm0HSbhlT0MYpZ4kzuxmOUPmTsJmt2BtK+uRU
3LlXtzyZnJo53azQuLZz26tBGd9LXsBUMi+KJ0eX1HPluIT3o+nslnJZaqGySLKm
cJnLf9hio+rAwFBb8sgDdzeI7jqZ2bmAGPJBYpIT/dIxuZUkgTfX+OMp2g3RnQea
M0w0UjhbbQeAJONH9HGREDdp7tYtuyuBbE4miNTyjSsouqk6AwIDAQABo2MwYTAd
BgNVHQ4EFgQUfd/q0BY4fkVBV3X+HWzXH0toW08wHwYDVR0jBBgwFoAUfd/q0BY4
fkVBV3X+HWzXH0toW08wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
DQYJKoZIhvcNAQELBQADggEBAGLUya6xwaCwUPWHNOjlvGBGzGwAmSstJzh5o77O
XTTvyCwb0p80AnS9XoX3An5e4ePzw69mEw6RzfVLIex7fCRDekqPFuSWjVagKGJc
G7nvCqdHoCh2z1Jkb9gFpYPd6p45dtLWBw9e9/t9cFHtDR6stC16/Hy8cLzEIr0c
EWxCNdJdZW+soJivaZQeVWtlMXxVpGIs8i33CAFYufZCTKMgyYRegZuMQ676OcDE
9VSi2vJnnhdn7OBip82xX3NDQrwVt60fvFMr25cPOlzhXRY4mQLslGOleqT3sSPV
DVJnOBBmdjgFQQ8BO7rFUNGGOaUcEZp0HLRwxPZyc6OBCIg=
-----END CERTIFICATE-----
+20
View File
@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDOjCCAiKgAwIBAgIUQr7R8yN5+2and6ucUOPF6oIbD48wDQYJKoZIhvcNAQEL
BQAwFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTI1MDIyODEyMDEzMFoXDTI2
MDcxMzEyMDEzMFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEA4oL2hAPQlDVaNJru5fIstkpoVSuam0vpswC7ciRc
XQRjF3q8kjtIA7+jdySsKJqOLGnybDX3awvRyKMEjq11IfnZLjZc+FzTlA+x4z0h
MHb0GiBFXKNzrExGI9F0KEPtFxcMIqZ119LY2ReexxWkZBQYlgTepaevp71za4c2
n4Zy1+0iS5+uklZ4ANKMTBGlN76Qgt530VnpNiIeUbiUzY58Vx4q7kFcUv/oSz8p
rbXr+/GGpAjrOc6/JsezRE8YK2po60dvV80TJ2Jt6pduvF7OSQnq/v4mJl1xuXKl
Byo9HLbeu3BuVRWQs2/EwEzx5kX3Ugysl9Bm44K2yKe9/QIDAQABo4GAMH4wHwYD
VR0jBBgwFoAUfd/q0BY4fkVBV3X+HWzXH0toW08wCQYDVR0TBAIwADALBgNVHQ8E
BAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0RBAgwBocEfwAAATAdBgNV
HQ4EFgQUe0rTTfWjho3hgeLTnajTCpddo2MwDQYJKoZIhvcNAQELBQADggEBAIR2
5zkA7rPnddxCunsz8Jjq3wyhR/KiAFz+RGeFeiXDkF2fWr7QIQ9KbFbv8tpfXR7P
B75bY0sXwutHMB2sZDi92cH5sthNBfp19fI35cxcU4oTPxp4UZJKEiA3Qx8y73CX
NJu1009nPdOJNlIboDGAFdZ5SH6RCh+YcQZ68kjHPWBIpXxLbs9FN3QmpbAvtLh1
PoPaSy7IjKmxm1u+Lf6tyIn2IiB3MiynaB3OKvbkLCseM/5SZKMk6WKSDWopOCJr
xciPOc+yeLz5I2Omn0uViOIIciqjlgxncWAyNtDgvJcecwqB2cPiIhk6GY0QZ1uM
e7KoqGzWXvWLqJ13a9U=
-----END CERTIFICATE-----
+28
View File
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDigvaEA9CUNVo0
mu7l8iy2SmhVK5qbS+mzALtyJFxdBGMXerySO0gDv6N3JKwomo4safJsNfdrC9HI
owSOrXUh+dkuNlz4XNOUD7HjPSEwdvQaIEVco3OsTEYj0XQoQ+0XFwwipnXX0tjZ
F57HFaRkFBiWBN6lp6+nvXNrhzafhnLX7SJLn66SVngA0oxMEaU3vpCC3nfRWek2
Ih5RuJTNjnxXHiruQVxS/+hLPymttev78YakCOs5zr8mx7NETxgramjrR29XzRMn
Ym3ql268Xs5JCer+/iYmXXG5cqUHKj0ctt67cG5VFZCzb8TATPHmRfdSDKyX0Gbj
grbIp739AgMBAAECggEADgUIbbAFbJbyHV1q5Jqc/9oSeRW40lyG0Mh+fEMZ4Gam
x3ZA+QAS+1W/hV6ktTf+YsCv+4NKQWWQN3iM41PYcyDmu1XWt/Hu5TQk0NQgxhd8
EP3nAnkvbf5OkmWiveHuaRvJFCqfZ/Cp8U3lSvHg+edwhMs1CKXHWSeAXwBrIMEb
ajpxuD3B/NT/CGmKnj3cgAuIbvNHVIcwu8ACbpczDL++vi7KrWmOJn1QzSlUlNFi
fsgnF0heO5Uff4vkjXU84INQxOP3tbvXcDNiwDewZy75h2d3Pv+ku8GoZYWFUXSJ
yKtafJMJUD0kJMuKhkzrwYcQGY6ioSYisPK+JoungQKBgQD8fWmuHwCXbM4Ckyns
Wg4f+kG8d+wypgIs6ENmgr9UnNB0N6n7nO7v/4l/l7IN9CQQmdtSvek2ytk5rGBM
XUAWxZaokE3MecxR0EUJx42k/k3dN4XgU/YNk4D6/wpEsyUATE4nIFDVjxE+Jc07
CZ2CUWKyxTPGz2kfHnEQ0vFiYQKBgQDlqRiGlJ6c99zTas2wrvr+50aQhn6BryDK
kjGM6woPnnwMq+Jy6vum3o0cU+iNNeFAijShXo2XR3iZJcoJ2sPhy3dRWdBNdFyy
hwxgD0cXzEjQL0M03DPDykTnM7ZvE6KUZjnxJZkytJHLKapoGzxBH9656zx5qnuH
MPYwTWg5HQKBgDKBD4OBtgeT/v0q3KbnOI4S69U8E6Xp6ON8rgayPn05RMUKYVjw
AidFcQZxnG8IF7KuY92AGUcZeiv8G+MKgAhOC526B6XP8xumUjjrjpyjNYX7Vi8R
/FSo3ZLXMwGc59jQao2O/DxLesJ4oz2c5cGsb9acdYfd8wQDfdBEsX3hAoGAc4Pu
NiMi9MknZZ/e/fPFg9lIgQFlOE2iLMID8mF2mgyZULZUHIFdOr3ONGVwHzbuqcva
VSB+D41/d2iuiu5igHwa8+w8/fh9d7691sNYevvh0/Ux1LC9yMlAhxpXtN8nc4VH
t6e1uu9gNdQrRloMoKUrHlDYBkpd/838xqbouXECgYEArwn+eXKD5zgNN4jEbNBp
ygIp+Oh2abt+CNQjfLUa+qon5ziH53mHixJ2hpaOa6Rxxu9R2ZgNLtbodm+ccD8z
ZNA7Z0rApAwfuhD8zIzkZ4HuARN8eopYmTubpzDkAcfRWhw1EBDQc0V6trl+EJsK
xfbmGepRVWXw2dLmxhA9/zM=
-----END PRIVATE KEY-----
-24
View File
@@ -1,24 +0,0 @@
package scaleset
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestUserAgentInfoString(t *testing.T) {
userAgentInfo := UserAgentInfo{
System: "actions-runner-controller",
Version: "0.1.0",
CommitSHA: "1234567890abcdef",
ScaleSetID: 10,
HasProxy: true,
Subsystem: "test",
}
userAgent := userAgentInfo.String()
expectedProduct := "actions-runner-controller/0.1.0 (1234567890abcdef; test)"
assert.Contains(t, userAgent, expectedProduct)
expectedScaleSet := "ScaleSetID/10 (Proxy/enabled)"
assert.Contains(t, userAgent, expectedScaleSet)
}