-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add methods for setup and teardown control in the API
This would hopefully be enough for fully implementing #539 in the backend
- Loading branch information
Showing
6 changed files
with
274 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
/* | ||
* | ||
* k6 - a next-generation load testing tool | ||
* Copyright (C) 2018 Load Impact | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Affero General Public License as | ||
* published by the Free Software Foundation, either version 3 of the | ||
* License, or (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Affero General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
* | ||
*/ | ||
|
||
package v1 | ||
|
||
import ( | ||
"encoding/json" | ||
"io/ioutil" | ||
"net/http" | ||
|
||
"github.com/julienschmidt/httprouter" | ||
"github.com/loadimpact/k6/api/common" | ||
"github.com/manyminds/api2go/jsonapi" | ||
) | ||
|
||
// SetupData is just a simple wrapper to satisfy jsonapi | ||
type SetupData struct { | ||
Data interface{} `json:"data" yaml:"data"` | ||
} | ||
|
||
// GetName is a dummy method so we can satisfy the jsonapi.EntityNamer interface | ||
func (sd SetupData) GetName() string { | ||
return "setupData" | ||
} | ||
|
||
// GetID is a dummy method so we can satisfy the jsonapi.MarshalIdentifier interface | ||
func (sd SetupData) GetID() string { | ||
return "default" | ||
} | ||
|
||
func handleSetupDataOutput(rw http.ResponseWriter, setupData interface{}) { | ||
rw.Header().Set("Content-Type", "application/json") | ||
|
||
data, err := jsonapi.Marshal(SetupData{setupData}) | ||
if err != nil { | ||
apiError(rw, "Encoding error", err.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
_, _ = rw.Write(data) | ||
} | ||
|
||
// HandleGetSetupData just returns the current JSON-encoded setup data | ||
func HandleGetSetupData(rw http.ResponseWriter, r *http.Request, p httprouter.Params) { | ||
runner := common.GetEngine(r.Context()).Executor.GetRunner() | ||
handleSetupDataOutput(rw, runner.GetSetupData()) | ||
} | ||
|
||
// HandleSetSetupData just parses the JSON request body and sets the result as setup data for the runner | ||
func HandleSetSetupData(rw http.ResponseWriter, r *http.Request, p httprouter.Params) { | ||
body, err := ioutil.ReadAll(r.Body) | ||
if err != nil { | ||
apiError(rw, "Error reading request body", err.Error(), http.StatusBadRequest) | ||
return | ||
} | ||
|
||
var setupData interface{} | ||
if err := json.Unmarshal(body, &setupData); err != nil { | ||
apiError(rw, "Error parsing request body", err.Error(), http.StatusBadRequest) | ||
return | ||
} | ||
|
||
runner := common.GetEngine(r.Context()).Executor.GetRunner() | ||
runner.SetSetupData(setupData) | ||
|
||
handleSetupDataOutput(rw, runner.GetSetupData()) | ||
} | ||
|
||
// HandleRunSetup executes the runner's Setup() method and returns the result | ||
func HandleRunSetup(rw http.ResponseWriter, r *http.Request, p httprouter.Params) { | ||
runner := common.GetEngine(r.Context()).Executor.GetRunner() | ||
|
||
if err := runner.Setup(r.Context()); err != nil { | ||
apiError(rw, "Error executing setup", err.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
handleSetupDataOutput(rw, runner.GetSetupData()) | ||
} | ||
|
||
// HandleRunTeardown executes the runner's Teardown() method | ||
func HandleRunTeardown(rw http.ResponseWriter, r *http.Request, p httprouter.Params) { | ||
runner := common.GetEngine(r.Context()).Executor.GetRunner() | ||
|
||
if err := runner.Teardown(r.Context()); err != nil { | ||
apiError(rw, "Error executing teardown", err.Error(), http.StatusInternalServerError) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
/* | ||
* | ||
* k6 - a next-generation load testing tool | ||
* Copyright (C) 2018 Load Impact | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Affero General Public License as | ||
* published by the Free Software Foundation, either version 3 of the | ||
* License, or (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Affero General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
* | ||
*/ | ||
|
||
package v1 | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"encoding/json" | ||
"net/http" | ||
"net/http/httptest" | ||
"testing" | ||
"time" | ||
|
||
"github.com/loadimpact/k6/core" | ||
"github.com/loadimpact/k6/core/local" | ||
"github.com/loadimpact/k6/js" | ||
"github.com/loadimpact/k6/lib" | ||
"github.com/loadimpact/k6/lib/types" | ||
"github.com/manyminds/api2go/jsonapi" | ||
"github.com/spf13/afero" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
null "gopkg.in/guregu/null.v3" | ||
) | ||
|
||
func TestSetupData(t *testing.T) { | ||
t.Parallel() | ||
runner, err := js.New( | ||
&lib.SourceData{Filename: "/script.js", Data: []byte(` | ||
export function setup() { | ||
return {"v": 1}; | ||
} | ||
export default function(data) { | ||
if (!data || data.v != 2) { | ||
throw new Error("incorrect data: " + JSON.stringify(data)); | ||
} | ||
}; | ||
export function teardown(data) { | ||
if (!data || data.v != 2) { | ||
throw new Error("incorrect teardown data: " + JSON.stringify(data)); | ||
} | ||
} | ||
`)}, | ||
afero.NewMemMapFs(), | ||
lib.RuntimeOptions{}, | ||
) | ||
require.NoError(t, err) | ||
runner.SetOptions(lib.Options{ | ||
Paused: null.BoolFrom(true), | ||
VUs: null.IntFrom(2), | ||
VUsMax: null.IntFrom(2), | ||
Iterations: null.IntFrom(3), | ||
SetupTimeout: types.NullDurationFrom(1 * time.Second), | ||
TeardownTimeout: types.NullDurationFrom(1 * time.Second), | ||
}) | ||
executor := local.New(runner) | ||
executor.SetRunSetup(false) | ||
engine, err := core.NewEngine(executor, runner.GetOptions()) | ||
require.NoError(t, err) | ||
|
||
handler := NewHandler() | ||
|
||
checkSetup := func(method, body, expResult string) { | ||
rw := httptest.NewRecorder() | ||
handler.ServeHTTP(rw, newRequestWithEngine(engine, method, "/v1/setup", bytes.NewBufferString(body))) | ||
res := rw.Result() | ||
assert.Equal(t, http.StatusOK, res.StatusCode) | ||
|
||
var doc jsonapi.Document | ||
assert.NoError(t, json.Unmarshal(rw.Body.Bytes(), &doc)) | ||
if !assert.NotNil(t, doc.Data.DataObject) { | ||
return | ||
} | ||
assert.Equal(t, "setupData", doc.Data.DataObject.Type) | ||
assert.JSONEq(t, expResult, string(doc.Data.DataObject.Attributes)) | ||
} | ||
|
||
checkSetup("GET", "", `{"data": null}`) | ||
checkSetup("POST", "", `{"data": {"v":1}}`) | ||
checkSetup("GET", "", `{"data": {"v":1}}`) | ||
checkSetup("PUT", `{"v":2, "test":"mest"}`, `{"data": {"v":2, "test":"mest"}}`) | ||
checkSetup("GET", "", `{"data": {"v":2, "test":"mest"}}`) | ||
|
||
ctx, cancel := context.WithCancel(context.Background()) | ||
errC := make(chan error) | ||
go func() { errC <- engine.Run(ctx) }() | ||
|
||
engine.Executor.SetPaused(false) | ||
|
||
select { | ||
case <-time.After(10 * time.Second): | ||
cancel() | ||
t.Fatal("Test timed out") | ||
case err := <-errC: | ||
cancel() | ||
require.NoError(t, err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters