diff --git a/js/modules/k6/html/element.go b/js/modules/k6/html/element.go index e115aa8e98e..96770512a7c 100644 --- a/js/modules/k6/html/element.go +++ b/js/modules/k6/html/element.go @@ -25,10 +25,10 @@ type Element struct { } type Attribute struct { - OwnerElement *Element - Name string + OwnerElement *Element `json:"owner_element"` + Name string `json:"name"` nsPrefix string - Value string + Value string `json:"value"` } func (e Element) attrAsString(name string) string { diff --git a/js/modules/k6/html/html.go b/js/modules/k6/html/html.go index 980bd3232ec..d71f283e4b3 100644 --- a/js/modules/k6/html/html.go +++ b/js/modules/k6/html/html.go @@ -49,7 +49,7 @@ func (HTML) ParseHTML(ctx context.Context, src string) (Selection, error) { type Selection struct { rt *goja.Runtime sel *goquery.Selection - URL string + URL string `json:"url"` } func (s Selection) emptySelection() Selection { diff --git a/js/modules/k6/http/http_request.go b/js/modules/k6/http/http_request.go index 51855df6d86..ab703901311 100644 --- a/js/modules/k6/http/http_request.go +++ b/js/modules/k6/http/http_request.go @@ -51,11 +51,11 @@ import ( ) type HTTPRequest struct { - Method string - URL string - Headers map[string][]string - Body string - Cookies map[string][]*HTTPRequestCookie + Method string `json:"method"` + URL string `json:"url"` + Headers map[string][]string `json:"headers"` + Body string `json:"body"` + Cookies map[string][]*HTTPRequestCookie `json:"cookies"` } func (h *HTTP) Get(ctx context.Context, url goja.Value, args ...goja.Value) (*HTTPResponse, error) { diff --git a/js/modules/k6/http/response.go b/js/modules/k6/http/response.go index 9c0344e264f..bb8a518a678 100644 --- a/js/modules/k6/http/response.go +++ b/js/modules/k6/http/response.go @@ -26,43 +26,45 @@ import ( "encoding/json" "errors" "fmt" - "github.com/tidwall/gjson" "net/url" "strings" + "github.com/tidwall/gjson" + "github.com/dop251/goja" "github.com/loadimpact/k6/js/common" "github.com/loadimpact/k6/js/modules/k6/html" "github.com/loadimpact/k6/lib/netext" ) -type OCSP struct { - ProducedAt, ThisUpdate, NextUpdate, RevokedAt int64 - RevocationReason string - Status string -} - type HTTPResponseTimings struct { - Duration, Blocked, LookingUp, Connecting, TLSHandshaking, Sending, Waiting, Receiving float64 + Duration float64 `json:"duration"` + Blocked float64 `json:"blocked"` + LookingUp float64 `json:"looking_up"` + Connecting float64 `json:"connecting"` + TLSHandshaking float64 `json:"tls_handshaking"` + Sending float64 `json:"sending"` + Waiting float64 `json:"waiting"` + Receiving float64 `json:"receiving"` } type HTTPResponse struct { ctx context.Context - RemoteIP string - RemotePort int - URL string - Status int - Proto string - Headers map[string]string - Cookies map[string][]*HTTPCookie - Body interface{} - Timings HTTPResponseTimings - TLSVersion string - TLSCipherSuite string - OCSP netext.OCSP `js:"ocsp"` - Error string - Request HTTPRequest + RemoteIP string `json:"remote_ip"` + RemotePort int `json:"remote_port"` + URL string `json:"url"` + Status int `json:"status"` + Proto string `json:"proto"` + Headers map[string]string `json:"headers"` + Cookies map[string][]*HTTPCookie `json:"cookies"` + Body interface{} `json:"body"` + Timings HTTPResponseTimings `json:"timings"` + TLSVersion string `json:"tls_version"` + TLSCipherSuite string `json:"tls_cipher_suite"` + OCSP netext.OCSP `js:"ocsp" json:"ocsp"` + Error string `json:"error"` + Request HTTPRequest `json:"request"` cachedJSON goja.Value validatedJSON bool diff --git a/js/modules/k6/marshalling_test.go b/js/modules/k6/marshalling_test.go new file mode 100644 index 00000000000..cc66f13c54d --- /dev/null +++ b/js/modules/k6/marshalling_test.go @@ -0,0 +1,146 @@ +/* + * + * k6 - a next-generation load testing tool + * Copyright (C) 2016 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 . + * + */ + +package k6_test + +import ( + "context" + "net" + "testing" + "time" + + "github.com/loadimpact/k6/js" + "github.com/loadimpact/k6/lib" + "github.com/loadimpact/k6/lib/testutils" + "github.com/loadimpact/k6/lib/types" + "github.com/loadimpact/k6/stats" + "github.com/spf13/afero" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + null "gopkg.in/guregu/null.v3" +) + +func TestSetupDataMarshalling(t *testing.T) { + tb := testutils.NewHTTPMultiBin(t) + defer tb.Cleanup() + + script := []byte(tb.Replacer.Replace(` + import http from "k6/http"; + import html from "k6/html"; + import ws from "k6/ws"; + export let options = { setupTimeout: "10s" }; + + + function make_jar() { + let jar = http.cookieJar(); + jar.set("test", "something") + return jar; + } + + export function setup() { + let res = http.get("http://test.loadimpact.com:HTTPBIN_PORT"); + let html_selection = html.parseHTML(res.body); + let ws_res = ws.connect("ws://test.loadimpact.com:HTTPBIN_PORT/ws-echo", function(socket){ + socket.on("open", function() { + socket.send("test") + }) + socket.on("message", function (data){ + if (!data=="test") { + throw new Error ("echo'd data doesn't match our message!"); + } + socket.close() + }); + }); + + return { + http_response: res, + html_selection: html_selection, + html_element: html_selection.find('html'), + html_attribute: html_selection.find('html').children('body').get(0).attributes(), + jar: make_jar(), + ws_res: ws_res, + }; + } + + function get_non_function_properties(object) { + return Object.keys(object).filter(element => + typeof(object[element]) !== "function"); + } + + function arrays_are_equal(first, second) { + if (first.length != second.length) { + return false + } + return first.every(element => second.includes(element)) + } + + function diff_object_properties(name, first, second) { + let first_properties = get_non_function_properties(first).sort(); + let second_properties = get_non_function_properties(second).sort(); + if (!(arrays_are_equal(first_properties, second_properties))) { + console.error("for " + name + ":\n" + + "first_properties : " + JSON.stringify(first_properties) + "\n" + + "second_properties: " + JSON.stringify(second_properties) + "\n" + + "are not the same"); + throw new Error("not matching " + name); + } + first_properties. + filter(element => typeof(first[element]) === "object"). + forEach(function(element) { + diff_object_properties(name+"."+element, + first[element], + second[element]); + }); + } + + export default function (data) { + let first_properties = get_non_function_properties(data).sort(); + diff_object_properties("setupdata", data, setup()); + } + `)) + + runner, err := js.New( + &lib.SourceData{Filename: "/script.js", Data: script}, + afero.NewMemMapFs(), + lib.RuntimeOptions{Env: map[string]string{"setupTimeout": "10s"}}, + ) + + runner.SetOptions(lib.Options{ + SetupTimeout: types.NullDurationFrom(1 * time.Second), + MaxRedirects: null.IntFrom(10), + Throw: null.BoolFrom(true), + Hosts: map[string]net.IP{ + "test.loadimpact.com": net.ParseIP("127.0.0.1"), + }, + }) + + require.NoError(t, err) + + samples := make(chan stats.SampleContainer, 100) + + if !assert.NoError(t, runner.Setup(context.Background(), samples)) { + return + } + vu, err := runner.NewVU(samples) + if assert.NoError(t, err) { + err := vu.RunOnce(context.Background()) + assert.NoError(t, err) + } +} diff --git a/js/modules/k6/ws/ws.go b/js/modules/k6/ws/ws.go index 5e698c2bca6..52178d7ab8d 100644 --- a/js/modules/k6/ws/ws.go +++ b/js/modules/k6/ws/ws.go @@ -63,11 +63,11 @@ type Socket struct { } type WSHTTPResponse struct { - URL string - Status int - Headers map[string]string - Body string - Error string + URL string `json:"url"` + Status int `json:"status"` + Headers map[string]string `json:"headers"` + Body string `json:"body"` + Error string `json:"error"` } const writeWait = 10 * time.Second diff --git a/lib/netext/tls.go b/lib/netext/tls.go index 813ec3ccfc3..75584ee1419 100644 --- a/lib/netext/tls.go +++ b/lib/netext/tls.go @@ -33,9 +33,12 @@ type TLSInfo struct { CipherSuite string } type OCSP struct { - ProducedAt, ThisUpdate, NextUpdate, RevokedAt int64 - RevocationReason string - Status string + ProducedAt int64 `json:"produced_at"` + ThisUpdate int64 `json:"this_update"` + NextUpdate int64 `json:"next_update"` + RevokedAt int64 `json:"revoked_at"` + RevocationReason string `json:"revocation_reason"` + Status string `json:"status"` } func ParseTLSConnState(tlsState *tls.ConnectionState) (TLSInfo, OCSP) {