Skip to content

Commit

Permalink
cloudapi: fix lint issues (#3564)
Browse files Browse the repository at this point in the history
This has a breaking change in the API as it renames an error type.

This should not really be used by anything, but tests :crossed-fingers:

Co-authored-by: Ivan <[email protected]>
  • Loading branch information
mstoykov and codebien authored Jan 31, 2024
1 parent 4839e8d commit 5cfc152
Show file tree
Hide file tree
Showing 12 changed files with 48 additions and 25 deletions.
12 changes: 12 additions & 0 deletions cloudapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@ import (
"go.k6.io/k6/lib"
)

// ResultStatus represents the result status of a test.
type ResultStatus int

const (
// ResultStatusPassed means the test has passed
ResultStatusPassed ResultStatus = 0
// ResultStatusFailed means the test has failed
ResultStatusFailed ResultStatus = 1
)

// ThresholdResult is a helper type to make sending the thresholds result to the cloud.
type ThresholdResult map[string]map[string]bool

// TestRun represents a test run.
type TestRun struct {
Name string `json:"name"`
ProjectID int64 `json:"project_id,omitempty"`
Expand All @@ -36,19 +41,22 @@ type LogEntry struct {
Message string `json:"message"`
}

// CreateTestRunResponse represents the response of successfully created test run in the cloud.
type CreateTestRunResponse struct {
ReferenceID string `json:"reference_id"`
ConfigOverride *Config `json:"config"`
Logs []LogEntry `json:"logs"`
}

// TestProgressResponse represents the progress of a cloud test.
type TestProgressResponse struct {
RunStatusText string `json:"run_status_text"`
RunStatus RunStatus `json:"run_status"`
ResultStatus ResultStatus `json:"result_status"`
Progress float64 `json:"progress"`
}

// LoginResponse includes the token after a successful login.
type LoginResponse struct {
Token string `json:"token"`
}
Expand Down Expand Up @@ -178,6 +186,7 @@ func (c *Client) TestFinished(referenceID string, thresholds ThresholdResult, ta
return c.Do(req, nil)
}

// GetTestProgress for the provided referenceID.
func (c *Client) GetTestProgress(referenceID string) (*TestProgressResponse, error) {
url := fmt.Sprintf("%s/test-progress/%s", c.baseURL, referenceID)
req, err := c.NewRequest(http.MethodGet, url, nil)
Expand All @@ -194,6 +203,7 @@ func (c *Client) GetTestProgress(referenceID string) (*TestProgressResponse, err
return &ctrr, nil
}

// StopCloudTestRun tells the cloud to stop the test with the provided referenceID.
func (c *Client) StopCloudTestRun(referenceID string) error {
url := fmt.Sprintf("%s/tests/%s/stop", c.baseURL, referenceID)

Expand All @@ -205,6 +215,7 @@ func (c *Client) StopCloudTestRun(referenceID string) error {
return c.Do(req, nil)
}

// ValidateOptions sends the provided options to the cloud for validation.
func (c *Client) ValidateOptions(options lib.Options) error {
url := fmt.Sprintf("%s/validate-options", c.baseURL)

Expand All @@ -222,6 +233,7 @@ func (c *Client) ValidateOptions(options lib.Options) error {
return c.Do(req, nil)
}

// Login the user with the specified email and password.
func (c *Client) Login(email string, password string) (*LoginResponse, error) {
url := fmt.Sprintf("%s/login", c.baseURL)

Expand Down
14 changes: 7 additions & 7 deletions cloudapi/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import (
"go.k6.io/k6/lib/types"
)

func fprintf(t *testing.T, w io.Writer, format string, a ...interface{}) int {
n, err := fmt.Fprintf(w, format, a...)
func fprint(t *testing.T, w io.Writer, format string) int {
n, err := fmt.Fprint(w, format)
require.NoError(t, err)
return n
}
Expand All @@ -30,7 +30,7 @@ func TestCreateTestRun(t *testing.T) {
exp := `{"name":"test","vus":0,"thresholds":null,"duration":0}`
assert.JSONEq(t, exp, string(b))

fprintf(t, w, `{"reference_id": "1", "config": {"aggregationPeriod": "2s"}}`)
fprint(t, w, `{"reference_id": "1", "config": {"aggregationPeriod": "2s"}}`)
}))
defer server.Close()

Expand All @@ -51,7 +51,7 @@ func TestCreateTestRun(t *testing.T) {
func TestFinished(t *testing.T) {
t.Parallel()
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fprintf(t, w, "")
fprint(t, w, "")
}))
defer server.Close()

Expand All @@ -73,7 +73,7 @@ func TestAuthorizedError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
called++
w.WriteHeader(http.StatusForbidden)
fprintf(t, w, `{"error": {"code": 5, "message": "Not allowed"}}`)
fprint(t, w, `{"error": {"code": 5, "message": "Not allowed"}}`)
}))
defer server.Close()

Expand All @@ -92,7 +92,7 @@ func TestDetailsError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
called++
w.WriteHeader(http.StatusForbidden)
fprintf(t, w, `{"error": {"code": 0, "message": "Validation failed", "details": { "name": ["Shorter than minimum length 2."]}}}`)
fprint(t, w, `{"error": {"code": 0, "message": "Validation failed", "details": { "name": ["Shorter than minimum length 2."]}}}`)
}))
defer server.Close()

Expand Down Expand Up @@ -145,7 +145,7 @@ func TestClientRetrySuccessOnSecond(t *testing.T) {
assert.Equal(t, idempotencyKey, gotK6IdempotencyKey)
called++
if called == 2 {
fprintf(t, w, `{"reference_id": "1"}`)
fprint(t, w, `{"reference_id": "1"}`)
return
}
w.WriteHeader(http.StatusInternalServerError)
Expand Down
8 changes: 5 additions & 3 deletions cloudapi/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"math/rand"
Expand Down Expand Up @@ -70,14 +71,15 @@ func (c *Client) NewRequest(method, url string, data interface{}) (*http.Request
buf = bytes.NewBuffer(b)
}

req, err := http.NewRequest(method, url, buf)
req, err := http.NewRequest(method, url, buf) //nolint:noctx // the user can add this
if err != nil {
return nil, err
}

return req, nil
}

// Do is simpler to http.Do but also unmarshals the response in the provided v
func (c *Client) Do(req *http.Request, v interface{}) error {
if req.Body != nil && req.GetBody == nil {
originalBody, err := io.ReadAll(req.Body)
Expand Down Expand Up @@ -155,7 +157,7 @@ func (c *Client) do(req *http.Request, v interface{}, attempt int) (retry bool,
}

if v != nil {
if err = json.NewDecoder(resp.Body).Decode(v); err == io.EOF {
if err = json.NewDecoder(resp.Body).Decode(v); errors.Is(err, io.EOF) {
err = nil // Ignore EOF from empty body
}
}
Expand All @@ -181,7 +183,7 @@ func CheckResponse(r *http.Response) error {
}

var payload struct {
Error ErrorResponse `json:"error"`
Error ResponseError `json:"error"`
}
if err := json.Unmarshal(data, &payload); err != nil {
if r.StatusCode == http.StatusUnauthorized {
Expand Down
2 changes: 2 additions & 0 deletions cloudapi/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ func NewConfig() Config {
}

// Apply saves config non-zero config values from the passed config in the receiver.
//
//nolint:funlen,gocognit,cyclop
func (c Config) Apply(cfg Config) Config {
if cfg.Token.Valid {
c.Token = cfg.Token
Expand Down
15 changes: 8 additions & 7 deletions cloudapi/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ var (
errUnknown = errors.New("an error occurred communicating with k6 Cloud")
)

// ErrorResponse represents an error cause by talking to the API
type ErrorResponse struct {
// ResponseError represents an error cause by talking to the API
type ResponseError struct {
Response *http.Response `json:"-"`

Code int `json:"code"`
Expand All @@ -33,7 +33,7 @@ func contains(s []string, e string) bool {
return false
}

func (e ErrorResponse) Error() string {
func (e ResponseError) Error() string {
msg := e.Message

for _, v := range e.Errors {
Expand All @@ -46,7 +46,7 @@ func (e ErrorResponse) Error() string {

// `e.Details` is the old API version
// TODO: do not handle `details` when the old API becomes obsolete
var details []string
var details []string //nolint:prealloc
var detail string
for k, v := range e.Details {
detail = k + ": " + strings.Join(v, ", ")
Expand All @@ -66,11 +66,12 @@ func (e ErrorResponse) Error() string {
}

var code string
if e.Code > 0 && e.Response != nil {
switch {
case e.Code > 0 && e.Response != nil:
code = fmt.Sprintf("%d/E%d", e.Response.StatusCode, e.Code)
} else if e.Response != nil {
case e.Response != nil:
code = fmt.Sprintf("%d", e.Response.StatusCode)
} else if e.Code > 0 {
case e.Code > 0:
code = fmt.Sprintf("E%d", e.Code)
}

Expand Down
2 changes: 1 addition & 1 deletion cloudapi/errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestErrorResponse_Error(t *testing.T) {
msg1 := "some message"
msg2 := "some other message"

errResp := ErrorResponse{
errResp := ResponseError{
Message: msg1,
Errors: []string{msg2},
FieldErrors: map[string][]string{
Expand Down
2 changes: 2 additions & 0 deletions cloudapi/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ func (c *Config) logtailConn(ctx context.Context, referenceID string, since time

// StreamLogsToLogger streams the logs for the configured test to the provided logger until ctx is
// Done or an error occurs.
//
//nolint:funlen
func (c *Config) StreamLogsToLogger(
ctx context.Context, logger logrus.FieldLogger, referenceID string, tailFrom time.Duration,
) error {
Expand Down
4 changes: 4 additions & 0 deletions cloudapi/logs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
)

func TestMsgParsing(t *testing.T) {
t.Parallel()
m := `{
"streams": [
{
Expand Down Expand Up @@ -71,6 +72,7 @@ func TestMsgParsing(t *testing.T) {
}

func TestMSGLog(t *testing.T) {
t.Parallel()
expectMsg := msg{
Streams: []msgStreams{
{
Expand Down Expand Up @@ -148,7 +150,9 @@ func TestRetry(t *testing.T) {
}

for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
var sleepRequests []time.Duration
// sleepCollector tracks the request duration value for sleep requests.
sleepCollector := sleeperFunc(func(d time.Duration) {
Expand Down
2 changes: 1 addition & 1 deletion output/cloud/expv2/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ func (o *Output) handleFlushError(err error) {

o.logger.WithError(err).Error("Failed to push metrics to the cloud")

var errResp cloudapi.ErrorResponse
var errResp cloudapi.ResponseError
if !errors.As(err, &errResp) || errResp.Response == nil {
return
}
Expand Down
6 changes: 3 additions & 3 deletions output/cloud/expv2/output_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func TestOutputHandleFlushError(t *testing.T) {
},
{
name: "error code equals 4 but no abort",
err: cloudapi.ErrorResponse{
err: cloudapi.ResponseError{
Response: &http.Response{StatusCode: http.StatusForbidden},
Code: 4,
},
Expand All @@ -169,7 +169,7 @@ func TestOutputHandleFlushError(t *testing.T) {
},
{
name: "error code equals 4 and abort",
err: cloudapi.ErrorResponse{
err: cloudapi.ResponseError{
Response: &http.Response{StatusCode: http.StatusForbidden},
Code: 4,
},
Expand Down Expand Up @@ -235,7 +235,7 @@ func TestOutputHandleFlushErrorMultipleTimes(t *testing.T) {
}
o.config.StopOnError = null.BoolFrom(true)

er := cloudapi.ErrorResponse{
er := cloudapi.ResponseError{
Response: &http.Response{
StatusCode: http.StatusForbidden,
},
Expand Down
2 changes: 1 addition & 1 deletion output/cloud/v1/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ func (out *Output) shouldStopSendingMetrics(err error) bool {
if err == nil {
return false
}
if errResp, ok := err.(cloudapi.ErrorResponse); ok && errResp.Response != nil { //nolint:errorlint
if errResp, ok := err.(cloudapi.ResponseError); ok && errResp.Response != nil { //nolint:errorlint
// The Cloud service returns the error code 4 when it doesn't accept any more metrics.
// So, when k6 sees that, the cloud output just stops prematurely.
return errResp.Response.StatusCode == http.StatusForbidden && errResp.Code == 4
Expand Down
4 changes: 2 additions & 2 deletions output/cloud/v1/output_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,10 +402,10 @@ func testCloudOutputStopSendingMetric(t *testing.T, stopOnError bool) {
count++
if count == max {
type payload struct {
Error cloudapi.ErrorResponse `json:"error"`
Error cloudapi.ResponseError `json:"error"`
}
res := &payload{}
res.Error = cloudapi.ErrorResponse{Code: 4}
res.Error = cloudapi.ResponseError{Code: 4}
w.Header().Set("Content-Type", "application/json")
data, err := json.Marshal(res)
if err != nil {
Expand Down

0 comments on commit 5cfc152

Please sign in to comment.