From 31d317dab742595c2d5ded3c221b90d5a0c77c28 Mon Sep 17 00:00:00 2001 From: oleiade Date: Fri, 13 Jan 2023 14:21:48 +0100 Subject: [PATCH 1/9] Expose the instrumentHTTP function as part of the module's public API --- js/modules/k6/experimental/tracing/module.go | 70 ++++++++++++++++++- .../tracing/tracing-instrumentHTTP.js | 24 +++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 samples/experimental/tracing/tracing-instrumentHTTP.js diff --git a/js/modules/k6/experimental/tracing/module.go b/js/modules/k6/experimental/tracing/module.go index 480a537259a..4844697eadd 100644 --- a/js/modules/k6/experimental/tracing/module.go +++ b/js/modules/k6/experimental/tracing/module.go @@ -18,6 +18,9 @@ type ( // ModuleInstance represents an instance of the JS module. ModuleInstance struct { vu modules.VU + + // Client holds the module's default tracing client. + *Client } ) @@ -45,7 +48,8 @@ func (*RootModule) NewModuleInstance(vu modules.VU) modules.Instance { func (mi *ModuleInstance) Exports() modules.Exports { return modules.Exports{ Named: map[string]interface{}{ - "Client": mi.newClient, + "Client": mi.newClient, + "instrumentHTTP": mi.instrumentHTTP, }, } } @@ -69,3 +73,67 @@ func (mi *ModuleInstance) newClient(cc goja.ConstructorCall) *goja.Object { return rt.ToValue(NewClient(mi.vu, opts)).ToObject(rt) } + +// InstrumentHTTP instruments the HTTP module with tracing headers. +// +// When used in the context of a k6 script, it will automatically replace +// the imported http module's methods with instrumented ones. +func (mi *ModuleInstance) instrumentHTTP(options options) { + rt := mi.vu.Runtime() + + if mi.vu.State() != nil { + common.Throw(rt, common.NewInitContextError("tracing module's instrumentHTTP can only be called in the init context")) + } + + if mi.Client != nil { + err := errors.New( + "tracing module's instrumentHTTP can only be called once. " + + "if you were attempting to reconfigure the instrumentation, " + + "please consider using the tracing.Client instead", + ) + common.Throw(rt, err) + } + + // Initialize the tracing module's instance default client, + // and configure it using the user-supplied set of options. + mi.Client = NewClient(mi.vu, options) + + // Explicitly inject the http module in the VU's runtime. + // This allows us to later on override the http module's methods + // with instrumented ones. + httpModuleValue, err := rt.RunString(`require('k6/http')`) + if err != nil { + common.Throw(rt, err) + } + + httpModuleObj := httpModuleValue.ToObject(rt) + + // Closure overriding a method of the provided imported module object. + // + // The `onModule` argument should be a *goja.Object obtained by requiring + // or importing the 'k6/http' module and converting it to an object. + // + // The `value` argument is expected to be callable. + mustSetHTTPMethod := func(method string, onModule *goja.Object, value interface{}) { + // Inject the new get function, adding tracing headers + // to the request in the HTTP module object. + err = onModule.Set(method, value) + if err != nil { + common.Throw( + rt, + fmt.Errorf("unable to overwrite http.%s method with instrumented one; reason: %w", method, err), + ) + } + } + + // Overwrite the implementation of the http module's method with the instrumented + // ones exposed by the `tracing.Client` struct. + mustSetHTTPMethod("del", httpModuleObj, mi.Client.Del) + mustSetHTTPMethod("get", httpModuleObj, mi.Client.Get) + mustSetHTTPMethod("head", httpModuleObj, mi.Client.Head) + mustSetHTTPMethod("options", httpModuleObj, mi.Client.Options) + mustSetHTTPMethod("patch", httpModuleObj, mi.Client.Patch) + mustSetHTTPMethod("post", httpModuleObj, mi.Client.Patch) + mustSetHTTPMethod("put", httpModuleObj, mi.Client.Patch) + mustSetHTTPMethod("request", httpModuleObj, mi.Client.Request) +} diff --git a/samples/experimental/tracing/tracing-instrumentHTTP.js b/samples/experimental/tracing/tracing-instrumentHTTP.js new file mode 100644 index 00000000000..aaf866089d1 --- /dev/null +++ b/samples/experimental/tracing/tracing-instrumentHTTP.js @@ -0,0 +1,24 @@ +import http from "k6/http"; +import { check } from "k6"; +import tracing from "k6/experimental/tracing"; + +// instrumentHTTP will ensure that all requests made by the http module +// will be traced. The first argument is a configuration object that +// can be used to configure the tracer. +// +// Currently supported HTTP methods are: get, post, put, patch, head, +// del, options, and request. +tracing.instrumentHTTP({ + propagator: "w3c", +}); + +export default () => { + let res = http.get("http://httpbin.org/get", { + headers: { + "X-Example-Header": "instrumented/get", + }, + }); + check(res, { + "status is 200": (r) => r.status === 200, + }); +}; From 22f1f7051961e41bdf441736274a8066296c218b Mon Sep 17 00:00:00 2001 From: oleiade Date: Fri, 13 Jan 2023 14:21:27 +0100 Subject: [PATCH 2/9] Add integration tests covering the instrumentHTTP function --- cmd/tests/tracing_module_test.go | 255 +++++++++++++++++++++++++++---- 1 file changed, 224 insertions(+), 31 deletions(-) diff --git a/cmd/tests/tracing_module_test.go b/cmd/tests/tracing_module_test.go index a098c147cdb..3606fd70fee 100644 --- a/cmd/tests/tracing_module_test.go +++ b/cmd/tests/tracing_module_test.go @@ -56,37 +56,7 @@ func TestTracingModuleClient(t *testing.T) { jsonResults, err := afero.ReadFile(ts.FS, "results.json") require.NoError(t, err) - gotHTTPDataPoints := false - - for _, jsonLine := range bytes.Split(jsonResults, []byte("\n")) { - if len(jsonLine) == 0 { - continue - } - - var line sampleEnvelope - require.NoError(t, json.Unmarshal(jsonLine, &line)) - - if line.Type != "Point" { - continue - } - - // Filter metric samples which are not related to http - if !strings.HasPrefix(line.Metric, "http_") { - continue - } - - gotHTTPDataPoints = true - - anyTraceID, hasTraceID := line.Data.Metadata["trace_id"] - require.True(t, hasTraceID) - - traceID, gotTraceID := anyTraceID.(string) - require.True(t, gotTraceID) - - assert.Len(t, traceID, 32) - } - - assert.True(t, gotHTTPDataPoints) + assertHasTraceIDMetadata(t, jsonResults) } func TestTracingClient_DoesNotInterfereWithHTTPModule(t *testing.T) { @@ -128,6 +98,229 @@ func TestTracingClient_DoesNotInterfereWithHTTPModule(t *testing.T) { assert.Equal(t, int64(2), atomic.LoadInt64(&gotInstrumentedRequests)) } +func TestTracingInstrumentHTTP_W3C(t *testing.T) { + t.Parallel() + tb := httpmultibin.NewHTTPMultiBin(t) + + var gotRequests int64 + + tb.Mux.HandleFunc("/tracing", func(w http.ResponseWriter, r *http.Request) { + atomic.AddInt64(&gotRequests, 1) + assert.NotEmpty(t, r.Header.Get("traceparent")) + assert.Len(t, r.Header.Get("traceparent"), 55) + }) + + script := tb.Replacer.Replace(` + import http from "k6/http"; + import { check } from "k6"; + import tracing from "k6/experimental/tracing"; + + tracing.instrumentHTTP({ + propagator: "w3c", + }) + + export default function () { + http.del("HTTPBIN_IP_URL/tracing"); + http.get("HTTPBIN_IP_URL/tracing"); + http.head("HTTPBIN_IP_URL/tracing"); + http.options("HTTPBIN_IP_URL/tracing"); + http.patch("HTTPBIN_IP_URL/tracing"); + http.post("HTTPBIN_IP_URL/tracing"); + http.put("HTTPBIN_IP_URL/tracing"); + http.request("GET", "HTTPBIN_IP_URL/tracing"); + }; + `) + + ts := getSingleFileTestState(t, script, []string{"--out", "json=results.json"}, 0) + cmd.ExecuteWithGlobalState(ts.GlobalState) + + assert.Equal(t, int64(8), atomic.LoadInt64(&gotRequests)) + + jsonResults, err := afero.ReadFile(ts.FS, "results.json") + require.NoError(t, err) + + assertHasTraceIDMetadata(t, jsonResults) +} + +func TestTracingInstrumentHTTP_Jaeger(t *testing.T) { + t.Parallel() + tb := httpmultibin.NewHTTPMultiBin(t) + + var gotRequests int64 + + tb.Mux.HandleFunc("/tracing", func(w http.ResponseWriter, r *http.Request) { + atomic.AddInt64(&gotRequests, 1) + assert.NotEmpty(t, r.Header.Get("uber-trace-id")) + assert.Len(t, r.Header.Get("uber-trace-id"), 45) + }) + + script := tb.Replacer.Replace(` + import http from "k6/http"; + import { check } from "k6"; + import tracing from "k6/experimental/tracing"; + + tracing.instrumentHTTP({ + propagator: "jaeger", + }) + + export default function () { + http.del("HTTPBIN_IP_URL/tracing"); + http.get("HTTPBIN_IP_URL/tracing"); + http.head("HTTPBIN_IP_URL/tracing"); + http.options("HTTPBIN_IP_URL/tracing"); + http.patch("HTTPBIN_IP_URL/tracing"); + http.post("HTTPBIN_IP_URL/tracing"); + http.put("HTTPBIN_IP_URL/tracing"); + http.request("GET", "HTTPBIN_IP_URL/tracing"); + }; + `) + + ts := getSingleFileTestState(t, script, []string{"--out", "json=results.json"}, 0) + cmd.ExecuteWithGlobalState(ts.GlobalState) + + assert.Equal(t, int64(8), atomic.LoadInt64(&gotRequests)) + + jsonResults, err := afero.ReadFile(ts.FS, "results.json") + require.NoError(t, err) + + assertHasTraceIDMetadata(t, jsonResults) +} + +func TestTracingInstrumentHTTP_FillsParams(t *testing.T) { + t.Parallel() + tb := httpmultibin.NewHTTPMultiBin(t) + + var gotRequests int64 + + tb.Mux.HandleFunc("/tracing", func(w http.ResponseWriter, r *http.Request) { + atomic.AddInt64(&gotRequests, 1) + + assert.NotEmpty(t, r.Header.Get("traceparent")) + assert.Len(t, r.Header.Get("traceparent"), 55) + + assert.NotEmpty(t, r.Header.Get("X-Test-Header")) + assert.Equal(t, "test", r.Header.Get("X-Test-Header")) + }) + + script := tb.Replacer.Replace(` + import http from "k6/http"; + import { check } from "k6"; + import tracing from "k6/experimental/tracing"; + + tracing.instrumentHTTP({ + propagator: "w3c", + }) + + const testHeaders = { + "X-Test-Header": "test", + } + + export default function () { + http.del("HTTPBIN_IP_URL/tracing", null, { headers: testHeaders }); + http.get("HTTPBIN_IP_URL/tracing", { headers: testHeaders }); + http.head("HTTPBIN_IP_URL/tracing", { headers: testHeaders }); + http.options("HTTPBIN_IP_URL/tracing", null, { headers: testHeaders }); + http.patch("HTTPBIN_IP_URL/tracing", null, { headers: testHeaders }); + http.post("HTTPBIN_IP_URL/tracing", null, { headers: testHeaders }); + http.put("HTTPBIN_IP_URL/tracing", null, { headers: testHeaders }); + http.request("GET", "HTTPBIN_IP_URL/tracing", null, { headers: testHeaders }); + }; + `) + + ts := getSingleFileTestState(t, script, []string{"--out", "json=results.json"}, 0) + cmd.ExecuteWithGlobalState(ts.GlobalState) + + assert.Equal(t, int64(8), atomic.LoadInt64(&gotRequests)) + + jsonResults, err := afero.ReadFile(ts.FS, "results.json") + require.NoError(t, err) + + assertHasTraceIDMetadata(t, jsonResults) +} + +func TestTracingInstrumentHTTP_FailsOutsideOfInitContext(t *testing.T) { + t.Parallel() + tb := httpmultibin.NewHTTPMultiBin(t) + + script := tb.Replacer.Replace(` + import tracing from "k6/experimental/tracing"; + + export default function() { + tracing.instrumentHTTP({ + propagator: "w3c", + }) + } + `) + + ts := getSingleFileTestState(t, script, []string{}, 0) + ts.ExpectedExitCode = 0 + cmd.ExecuteWithGlobalState(ts.GlobalState) + + assert.Contains(t, ts.Stderr.String(), "instrumentHTTP can only be called in the init context") +} + +func TestTracingInstrumentHTTP_CannotBeCalledTwice(t *testing.T) { + t.Parallel() + tb := httpmultibin.NewHTTPMultiBin(t) + + script := tb.Replacer.Replace(` + import tracing from "k6/experimental/tracing"; + + tracing.instrumentHTTP({ + propagator: "w3c", + }) + + tracing.instrumentHTTP({ + propagator: "w3c", + }) + + export default function() { + } + `) + + ts := getSingleFileTestState(t, script, []string{}, 0) + ts.ExpectedExitCode = 107 + cmd.ExecuteWithGlobalState(ts.GlobalState) + + assert.Contains(t, ts.Stderr.String(), "instrumentHTTP can only be called once") +} + +// assertHasTraceIDMetadata checks that the trace_id metadata is present and has the correct format +// for all http metrics in the json results file. +func assertHasTraceIDMetadata(t *testing.T, jsonResults []byte) { + gotHTTPDataPoints := false + + for _, jsonLine := range bytes.Split(jsonResults, []byte("\n")) { + if len(jsonLine) == 0 { + continue + } + + var line sampleEnvelope + require.NoError(t, json.Unmarshal(jsonLine, &line)) + + if line.Type != "Point" { + continue + } + + // Filter metric samples which are not related to http + if !strings.HasPrefix(line.Metric, "http_") { + continue + } + + gotHTTPDataPoints = true + + anyTraceID, hasTraceID := line.Data.Metadata["trace_id"] + require.True(t, hasTraceID) + + traceID, gotTraceID := anyTraceID.(string) + require.True(t, gotTraceID) + + assert.Len(t, traceID, 32) + } + + assert.True(t, gotHTTPDataPoints) +} + // sampleEnvelope is a trimmed version of the struct found // in output/json/wrapper.go // TODO: use the json output's wrapper struct instead if it's ever exported From 2ff52461d505666bbd74f4653fb363f0f6560ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Crevon?= Date: Wed, 25 Jan 2023 17:17:52 +0100 Subject: [PATCH 3/9] Update cmd/tests/tracing_module_test.go Co-authored-by: Ivan <2103732+codebien@users.noreply.github.com> --- cmd/tests/tracing_module_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/tests/tracing_module_test.go b/cmd/tests/tracing_module_test.go index 3606fd70fee..eb0b49baac3 100644 --- a/cmd/tests/tracing_module_test.go +++ b/cmd/tests/tracing_module_test.go @@ -112,7 +112,6 @@ func TestTracingInstrumentHTTP_W3C(t *testing.T) { script := tb.Replacer.Replace(` import http from "k6/http"; - import { check } from "k6"; import tracing from "k6/experimental/tracing"; tracing.instrumentHTTP({ From 7f858e9a120ffcc6ee26f70600c2659d395fd5b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Crevon?= Date: Wed, 25 Jan 2023 17:25:36 +0100 Subject: [PATCH 4/9] Update cmd/tests/tracing_module_test.go Co-authored-by: Ivan <2103732+codebien@users.noreply.github.com> --- cmd/tests/tracing_module_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/tests/tracing_module_test.go b/cmd/tests/tracing_module_test.go index eb0b49baac3..54e24070a97 100644 --- a/cmd/tests/tracing_module_test.go +++ b/cmd/tests/tracing_module_test.go @@ -203,7 +203,6 @@ func TestTracingInstrumentHTTP_FillsParams(t *testing.T) { script := tb.Replacer.Replace(` import http from "k6/http"; - import { check } from "k6"; import tracing from "k6/experimental/tracing"; tracing.instrumentHTTP({ From 7163425ac618bba0839f60ad16b02c8b45935e18 Mon Sep 17 00:00:00 2001 From: oleiade Date: Thu, 26 Jan 2023 09:39:20 +0100 Subject: [PATCH 5/9] Add unit tests for the instrumentHTTP function --- .../k6/experimental/tracing/module_test.go | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 js/modules/k6/experimental/tracing/module_test.go diff --git a/js/modules/k6/experimental/tracing/module_test.go b/js/modules/k6/experimental/tracing/module_test.go new file mode 100644 index 00000000000..188a22823cc --- /dev/null +++ b/js/modules/k6/experimental/tracing/module_test.go @@ -0,0 +1,86 @@ +package tracing + +import ( + "testing" + + "github.com/dop251/goja" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.k6.io/k6/js/modules/k6/http" + "go.k6.io/k6/js/modulestest" + "go.k6.io/k6/lib" +) + +func TestInstrumentHTTP_SucceedsInInitContext(t *testing.T) { + t.Parallel() + + ts := modulestest.NewRuntime(t) + m := new(RootModule).NewModuleInstance(ts.VU) + + rt := ts.VU.Runtime() + require.NoError(t, rt.Set("instrumentHTTP", m.Exports().Named["instrumentHTTP"])) + + require.NoError(t, rt.Set("require", func(module string) *goja.Object { + require.Equal(t, "k6/http", module) + export := http.New().NewModuleInstance(ts.VU).Exports().Default + + return rt.ToValue(export).ToObject(rt) + })) + + // Calling in the init context should succeed + _, err := ts.VU.Runtime().RunString(` + instrumentHTTP({propagator: 'w3c'}) + `) + + assert.NoError(t, err) +} + +func TestInstrumentHTTP_FailsWhenCalledTwice(t *testing.T) { + t.Parallel() + + ts := modulestest.NewRuntime(t) + m := new(RootModule).NewModuleInstance(ts.VU) + + rt := ts.VU.Runtime() + require.NoError(t, rt.Set("instrumentHTTP", m.Exports().Named["instrumentHTTP"])) + + require.NoError(t, rt.Set("require", func(module string) *goja.Object { + require.Equal(t, "k6/http", module) + export := http.New().NewModuleInstance(ts.VU).Exports().Default + + return rt.ToValue(export).ToObject(rt) + })) + + // Calling it twice in the init context should fail + _, err := ts.VU.Runtime().RunString(` + instrumentHTTP({propagator: 'w3c'}) + instrumentHTTP({propagator: 'w3c'}) + `) + + assert.Error(t, err) +} + +func TestInstrumentHTTP_FailsInVUContext(t *testing.T) { + t.Parallel() + + ts := modulestest.NewRuntime(t) + ts.MoveToVUContext(&lib.State{}) + m := new(RootModule).NewModuleInstance(ts.VU) + + rt := ts.VU.Runtime() + require.NoError(t, rt.Set("instrumentHTTP", m.Exports().Named["instrumentHTTP"])) + + require.NoError(t, rt.Set("require", func(module string) *goja.Object { + require.Equal(t, "k6/http", module) + export := http.New().NewModuleInstance(ts.VU).Exports().Default + + return rt.ToValue(export).ToObject(rt) + })) + + // Calling in the VU context should fail + _, err := ts.VU.Runtime().RunString(` + instrumentHTTP({propagator: 'w3c'}) + `) + + assert.Error(t, err) +} From 60e1e3bbcac8eb7f1a414d923d83c268c08b8162 Mon Sep 17 00:00:00 2001 From: oleiade Date: Thu, 26 Jan 2023 09:40:55 +0100 Subject: [PATCH 6/9] Remove unnecessary integration tests --- cmd/tests/tracing_module_test.go | 47 -------------------------------- 1 file changed, 47 deletions(-) diff --git a/cmd/tests/tracing_module_test.go b/cmd/tests/tracing_module_test.go index 54e24070a97..bb70717e6cd 100644 --- a/cmd/tests/tracing_module_test.go +++ b/cmd/tests/tracing_module_test.go @@ -236,53 +236,6 @@ func TestTracingInstrumentHTTP_FillsParams(t *testing.T) { assertHasTraceIDMetadata(t, jsonResults) } -func TestTracingInstrumentHTTP_FailsOutsideOfInitContext(t *testing.T) { - t.Parallel() - tb := httpmultibin.NewHTTPMultiBin(t) - - script := tb.Replacer.Replace(` - import tracing from "k6/experimental/tracing"; - - export default function() { - tracing.instrumentHTTP({ - propagator: "w3c", - }) - } - `) - - ts := getSingleFileTestState(t, script, []string{}, 0) - ts.ExpectedExitCode = 0 - cmd.ExecuteWithGlobalState(ts.GlobalState) - - assert.Contains(t, ts.Stderr.String(), "instrumentHTTP can only be called in the init context") -} - -func TestTracingInstrumentHTTP_CannotBeCalledTwice(t *testing.T) { - t.Parallel() - tb := httpmultibin.NewHTTPMultiBin(t) - - script := tb.Replacer.Replace(` - import tracing from "k6/experimental/tracing"; - - tracing.instrumentHTTP({ - propagator: "w3c", - }) - - tracing.instrumentHTTP({ - propagator: "w3c", - }) - - export default function() { - } - `) - - ts := getSingleFileTestState(t, script, []string{}, 0) - ts.ExpectedExitCode = 107 - cmd.ExecuteWithGlobalState(ts.GlobalState) - - assert.Contains(t, ts.Stderr.String(), "instrumentHTTP can only be called once") -} - // assertHasTraceIDMetadata checks that the trace_id metadata is present and has the correct format // for all http metrics in the json results file. func assertHasTraceIDMetadata(t *testing.T, jsonResults []byte) { From 7d468aad65e3834df0b7de90408dcab8626d43ce Mon Sep 17 00:00:00 2001 From: oleiade Date: Thu, 26 Jan 2023 10:18:46 +0100 Subject: [PATCH 7/9] Add integration test illustrating instrumentHTTP in multi-file setup --- cmd/tests/tracing_module_test.go | 54 ++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/cmd/tests/tracing_module_test.go b/cmd/tests/tracing_module_test.go index bb70717e6cd..28c65b9ce22 100644 --- a/cmd/tests/tracing_module_test.go +++ b/cmd/tests/tracing_module_test.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "net/http" + "path/filepath" "strings" "sync/atomic" "testing" @@ -236,6 +237,59 @@ func TestTracingInstrumentHTTP_FillsParams(t *testing.T) { assertHasTraceIDMetadata(t, jsonResults) } +func TestTracingInstrummentHTTP_SupportsMultipleTestScripts(t *testing.T) { + t.Parallel() + + var gotRequests int64 + + tb := httpmultibin.NewHTTPMultiBin(t) + tb.Mux.HandleFunc("/tracing", func(w http.ResponseWriter, r *http.Request) { + atomic.AddInt64(&gotRequests, 1) + + assert.NotEmpty(t, r.Header.Get("traceparent")) + assert.Len(t, r.Header.Get("traceparent"), 55) + }) + + mainScript := tb.Replacer.Replace(` + import http from "k6/http"; + import tracing from "k6/experimental/tracing"; + + import { iShouldBeInstrumented } from "./imported.js"; + + tracing.instrumentHTTP({ + propagator: "w3c", + }) + + export default function() { + http.get("HTTPBIN_IP_URL/tracing"); + iShouldBeInstrumented(); + }; + `) + + importedScript := tb.Replacer.Replace(` + import http from "k6/http"; + + export function iShouldBeInstrumented() { + http.head("HTTPBIN_IP_URL/tracing"); + } + `) + + ts := NewGlobalTestState(t) + require.NoError(t, afero.WriteFile(ts.FS, filepath.Join(ts.Cwd, "main.js"), []byte(mainScript), 0o644)) + require.NoError(t, afero.WriteFile(ts.FS, filepath.Join(ts.Cwd, "imported.js"), []byte(importedScript), 0o644)) + + ts.CmdArgs = []string{"k6", "run", "--out", "json=results.json", "main.js"} + ts.ExpectedExitCode = 0 + + cmd.ExecuteWithGlobalState(ts.GlobalState) + + jsonResults, err := afero.ReadFile(ts.FS, "results.json") + require.NoError(t, err) + + assert.Equal(t, int64(2), atomic.LoadInt64(&gotRequests)) + assertHasTraceIDMetadata(t, jsonResults) +} + // assertHasTraceIDMetadata checks that the trace_id metadata is present and has the correct format // for all http metrics in the json results file. func assertHasTraceIDMetadata(t *testing.T, jsonResults []byte) { From d5616dca7787066f1ce417f21eef4288900a4778 Mon Sep 17 00:00:00 2001 From: oleiade Date: Thu, 26 Jan 2023 11:03:25 +0100 Subject: [PATCH 8/9] Move unit test setup in dedicated struct --- .../k6/experimental/tracing/module_test.go | 58 ++++++++----------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/js/modules/k6/experimental/tracing/module_test.go b/js/modules/k6/experimental/tracing/module_test.go index 188a22823cc..27c7d2c5840 100644 --- a/js/modules/k6/experimental/tracing/module_test.go +++ b/js/modules/k6/experimental/tracing/module_test.go @@ -14,21 +14,10 @@ import ( func TestInstrumentHTTP_SucceedsInInitContext(t *testing.T) { t.Parallel() - ts := modulestest.NewRuntime(t) - m := new(RootModule).NewModuleInstance(ts.VU) - - rt := ts.VU.Runtime() - require.NoError(t, rt.Set("instrumentHTTP", m.Exports().Named["instrumentHTTP"])) - - require.NoError(t, rt.Set("require", func(module string) *goja.Object { - require.Equal(t, "k6/http", module) - export := http.New().NewModuleInstance(ts.VU).Exports().Default - - return rt.ToValue(export).ToObject(rt) - })) + ts := newTestSetup(t) // Calling in the init context should succeed - _, err := ts.VU.Runtime().RunString(` + _, err := ts.TestRuntime.VU.Runtime().RunString(` instrumentHTTP({propagator: 'w3c'}) `) @@ -38,21 +27,10 @@ func TestInstrumentHTTP_SucceedsInInitContext(t *testing.T) { func TestInstrumentHTTP_FailsWhenCalledTwice(t *testing.T) { t.Parallel() - ts := modulestest.NewRuntime(t) - m := new(RootModule).NewModuleInstance(ts.VU) - - rt := ts.VU.Runtime() - require.NoError(t, rt.Set("instrumentHTTP", m.Exports().Named["instrumentHTTP"])) - - require.NoError(t, rt.Set("require", func(module string) *goja.Object { - require.Equal(t, "k6/http", module) - export := http.New().NewModuleInstance(ts.VU).Exports().Default - - return rt.ToValue(export).ToObject(rt) - })) + ts := newTestSetup(t) // Calling it twice in the init context should fail - _, err := ts.VU.Runtime().RunString(` + _, err := ts.TestRuntime.VU.Runtime().RunString(` instrumentHTTP({propagator: 'w3c'}) instrumentHTTP({propagator: 'w3c'}) `) @@ -63,8 +41,24 @@ func TestInstrumentHTTP_FailsWhenCalledTwice(t *testing.T) { func TestInstrumentHTTP_FailsInVUContext(t *testing.T) { t.Parallel() + ts := newTestSetup(t) + ts.TestRuntime.MoveToVUContext(&lib.State{}) + + // Calling in the VU context should fail + _, err := ts.TestRuntime.VU.Runtime().RunString(` + instrumentHTTP({propagator: 'w3c'}) + `) + + assert.Error(t, err) +} + +type testSetup struct { + t *testing.T + TestRuntime *modulestest.Runtime +} + +func newTestSetup(t *testing.T) testSetup { ts := modulestest.NewRuntime(t) - ts.MoveToVUContext(&lib.State{}) m := new(RootModule).NewModuleInstance(ts.VU) rt := ts.VU.Runtime() @@ -77,10 +71,8 @@ func TestInstrumentHTTP_FailsInVUContext(t *testing.T) { return rt.ToValue(export).ToObject(rt) })) - // Calling in the VU context should fail - _, err := ts.VU.Runtime().RunString(` - instrumentHTTP({propagator: 'w3c'}) - `) - - assert.Error(t, err) + return testSetup{ + t: t, + TestRuntime: ts, + } } From 7187e1232dee091af0c95d98baf953d8e829ac76 Mon Sep 17 00:00:00 2001 From: oleiade Date: Thu, 26 Jan 2023 11:22:16 +0100 Subject: [PATCH 9/9] Adjust test ensuring instrumenting function from other module works --- cmd/tests/tracing_module_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/tests/tracing_module_test.go b/cmd/tests/tracing_module_test.go index 28c65b9ce22..1e9ee1b20ad 100644 --- a/cmd/tests/tracing_module_test.go +++ b/cmd/tests/tracing_module_test.go @@ -261,7 +261,6 @@ func TestTracingInstrummentHTTP_SupportsMultipleTestScripts(t *testing.T) { }) export default function() { - http.get("HTTPBIN_IP_URL/tracing"); iShouldBeInstrumented(); }; `) @@ -286,7 +285,7 @@ func TestTracingInstrummentHTTP_SupportsMultipleTestScripts(t *testing.T) { jsonResults, err := afero.ReadFile(ts.FS, "results.json") require.NoError(t, err) - assert.Equal(t, int64(2), atomic.LoadInt64(&gotRequests)) + assert.Equal(t, int64(1), atomic.LoadInt64(&gotRequests)) assertHasTraceIDMetadata(t, jsonResults) }