Best K6 code snippet using k6.Exports
runner_test.go
Source:runner_test.go  
1/*2 *3 * k6 - a next-generation load testing tool4 * Copyright (C) 2016 Load Impact5 *6 * This program is free software: you can redistribute it and/or modify7 * it under the terms of the GNU Affero General Public License as8 * published by the Free Software Foundation, either version 3 of the9 * License, or (at your option) any later version.10 *11 * This program is distributed in the hope that it will be useful,12 * but WITHOUT ANY WARRANTY; without even the implied warranty of13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the14 * GNU Affero General Public License for more details.15 *16 * You should have received a copy of the GNU Affero General Public License17 * along with this program.  If not, see <http://www.gnu.org/licenses/>.18 *19 */20package js21import (22	"bytes"23	"context"24	"crypto/tls"25	"crypto/x509"26	"fmt"27	"go/build"28	"io/ioutil"29	stdlog "log"30	"net"31	"net/http"32	"net/url"33	"os"34	"strings"35	"sync"36	"testing"37	"time"38	"github.com/sirupsen/logrus"39	logtest "github.com/sirupsen/logrus/hooks/test"40	"github.com/spf13/afero"41	"github.com/stretchr/testify/assert"42	"github.com/stretchr/testify/require"43	"google.golang.org/grpc/test/grpc_testing"44	"gopkg.in/guregu/null.v3"45	"go.k6.io/k6/core"46	"go.k6.io/k6/core/local"47	"go.k6.io/k6/js/common"48	"go.k6.io/k6/js/modules/k6"49	k6http "go.k6.io/k6/js/modules/k6/http"50	k6metrics "go.k6.io/k6/js/modules/k6/metrics"51	"go.k6.io/k6/js/modules/k6/ws"52	"go.k6.io/k6/lib"53	_ "go.k6.io/k6/lib/executor" // TODO: figure out something better54	"go.k6.io/k6/lib/metrics"55	"go.k6.io/k6/lib/testutils"56	"go.k6.io/k6/lib/testutils/httpmultibin"57	"go.k6.io/k6/lib/testutils/mockoutput"58	"go.k6.io/k6/lib/types"59	"go.k6.io/k6/loader"60	"go.k6.io/k6/output"61	"go.k6.io/k6/stats"62)63func TestRunnerNew(t *testing.T) {64	t.Parallel()65	t.Run("Valid", func(t *testing.T) {66		t.Parallel()67		r, err := getSimpleRunner(t, "/script.js", `68			var counter = 0;69			exports.default = function() { counter++; }70		`)71		assert.NoError(t, err)72		t.Run("NewVU", func(t *testing.T) {73			t.Parallel()74			initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))75			assert.NoError(t, err)76			vuc, ok := initVU.(*VU)77			assert.True(t, ok)78			assert.Equal(t, int64(0), vuc.Runtime.Get("counter").Export())79			ctx, cancel := context.WithCancel(context.Background())80			defer cancel()81			vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})82			t.Run("RunOnce", func(t *testing.T) {83				err = vu.RunOnce()84				assert.NoError(t, err)85				assert.Equal(t, int64(1), vuc.Runtime.Get("counter").Export())86			})87		})88	})89	t.Run("Invalid", func(t *testing.T) {90		t.Parallel()91		_, err := getSimpleRunner(t, "/script.js", `blarg`)92		assert.EqualError(t, err, "ReferenceError: blarg is not defined\n\tat file:///script.js:1:1(0)\n")93	})94}95func TestRunnerGetDefaultGroup(t *testing.T) {96	t.Parallel()97	r1, err := getSimpleRunner(t, "/script.js", `exports.default = function() {};`)98	if assert.NoError(t, err) {99		assert.NotNil(t, r1.GetDefaultGroup())100	}101	r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})102	if assert.NoError(t, err) {103		assert.NotNil(t, r2.GetDefaultGroup())104	}105}106func TestRunnerOptions(t *testing.T) {107	t.Parallel()108	r1, err := getSimpleRunner(t, "/script.js", `exports.default = function() {};`)109	require.NoError(t, err)110	r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})111	require.NoError(t, err)112	testdata := map[string]*Runner{"Source": r1, "Archive": r2}113	for name, r := range testdata {114		name, r := name, r115		t.Run(name, func(t *testing.T) {116			t.Parallel()117			assert.Equal(t, r.Bundle.Options, r.GetOptions())118			assert.Equal(t, null.NewBool(false, false), r.Bundle.Options.Paused)119			r.SetOptions(lib.Options{Paused: null.BoolFrom(true)})120			assert.Equal(t, r.Bundle.Options, r.GetOptions())121			assert.Equal(t, null.NewBool(true, true), r.Bundle.Options.Paused)122			r.SetOptions(lib.Options{Paused: null.BoolFrom(false)})123			assert.Equal(t, r.Bundle.Options, r.GetOptions())124			assert.Equal(t, null.NewBool(false, true), r.Bundle.Options.Paused)125		})126	}127}128func TestOptionsSettingToScript(t *testing.T) {129	t.Parallel()130	optionVariants := []string{131		"",132		"var options = null;",133		"var options = undefined;",134		"var options = {};",135		"var options = {teardownTimeout: '1s'};",136	}137	for i, variant := range optionVariants {138		variant := variant139		t.Run(fmt.Sprintf("Variant#%d", i), func(t *testing.T) {140			t.Parallel()141			data := variant + `142					exports.default = function() {143						if (!options) {144							throw new Error("Expected options to be defined!");145						}146						if (options.teardownTimeout != __ENV.expectedTeardownTimeout) {147							throw new Error("expected teardownTimeout to be " + __ENV.expectedTeardownTimeout + " but it was " + options.teardownTimeout);148						}149					};`150			r, err := getSimpleRunner(t, "/script.js", data,151				lib.RuntimeOptions{Env: map[string]string{"expectedTeardownTimeout": "4s"}})152			require.NoError(t, err)153			newOptions := lib.Options{TeardownTimeout: types.NullDurationFrom(4 * time.Second)}154			r.SetOptions(newOptions)155			require.Equal(t, newOptions, r.GetOptions())156			samples := make(chan stats.SampleContainer, 100)157			initVU, err := r.NewVU(1, 1, samples)158			if assert.NoError(t, err) {159				ctx, cancel := context.WithCancel(context.Background())160				defer cancel()161				vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})162				err := vu.RunOnce()163				assert.NoError(t, err)164			}165		})166	}167}168func TestOptionsPropagationToScript(t *testing.T) {169	t.Parallel()170	data := `171			var options = { setupTimeout: "1s", myOption: "test" };172			exports.options = options;173			exports.default = function() {174				if (options.external) {175					throw new Error("Unexpected property external!");176				}177				if (options.myOption != "test") {178					throw new Error("expected myOption to remain unchanged but it was '" + options.myOption + "'");179				}180				if (options.setupTimeout != __ENV.expectedSetupTimeout) {181					throw new Error("expected setupTimeout to be " + __ENV.expectedSetupTimeout + " but it was " + options.setupTimeout);182				}183			};`184	expScriptOptions := lib.Options{SetupTimeout: types.NullDurationFrom(1 * time.Second)}185	r1, err := getSimpleRunner(t, "/script.js", data,186		lib.RuntimeOptions{Env: map[string]string{"expectedSetupTimeout": "1s"}})187	require.NoError(t, err)188	require.Equal(t, expScriptOptions, r1.GetOptions())189	r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{Env: map[string]string{"expectedSetupTimeout": "3s"}})190	require.NoError(t, err)191	require.Equal(t, expScriptOptions, r2.GetOptions())192	newOptions := lib.Options{SetupTimeout: types.NullDurationFrom(3 * time.Second)}193	require.NoError(t, r2.SetOptions(newOptions))194	require.Equal(t, newOptions, r2.GetOptions())195	testdata := map[string]*Runner{"Source": r1, "Archive": r2}196	for name, r := range testdata {197		r := r198		t.Run(name, func(t *testing.T) {199			t.Parallel()200			samples := make(chan stats.SampleContainer, 100)201			initVU, err := r.NewVU(1, 1, samples)202			if assert.NoError(t, err) {203				ctx, cancel := context.WithCancel(context.Background())204				defer cancel()205				vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})206				err := vu.RunOnce()207				assert.NoError(t, err)208			}209		})210	}211}212func TestMetricName(t *testing.T) {213	t.Parallel()214	script := `215		var Counter = require("k6/metrics").Counter;216		var myCounter = new Counter("not ok name @");217		exports.default = function(data) {218			myCounter.add(1);219		}220	`221	_, err := getSimpleRunner(t, "/script.js", script)222	require.Error(t, err)223}224func TestSetupDataIsolation(t *testing.T) {225	t.Parallel()226	script := `227		var Counter = require("k6/metrics").Counter;228		exports.options = {229			scenarios: {230				shared_iters: {231					executor: "shared-iterations",232					vus: 5,233					iterations: 500,234				},235			},236			teardownTimeout: "5s",237			setupTimeout: "5s",238		};239		var myCounter = new Counter("mycounter");240		exports.setup = function() {241			return { v: 0 };242		}243		exports.default = function(data) {244			if (data.v !== __ITER) {245				throw new Error("default: wrong data for iter " + __ITER + ": " + JSON.stringify(data));246			}247			data.v += 1;248			myCounter.add(1);249		}250		exports.teardown = function(data) {251			if (data.v !== 0) {252				throw new Error("teardown: wrong data: " + data.v);253			}254			myCounter.add(1);255		}256	`257	runner, err := getSimpleRunner(t, "/script.js", script)258	require.NoError(t, err)259	options := runner.GetOptions()260	require.Empty(t, options.Validate())261	execScheduler, err := local.NewExecutionScheduler(runner, testutils.NewLogger(t))262	require.NoError(t, err)263	mockOutput := mockoutput.New()264	engine, err := core.NewEngine(265		execScheduler, options, lib.RuntimeOptions{}, []output.Output{mockOutput}, testutils.NewLogger(t),266	)267	require.NoError(t, err)268	ctx, cancel := context.WithCancel(context.Background())269	run, wait, err := engine.Init(ctx, ctx)270	require.NoError(t, err)271	require.Empty(t, runner.defaultGroup.Groups)272	errC := make(chan error)273	go func() { errC <- run() }()274	select {275	case <-time.After(10 * time.Second):276		cancel()277		t.Fatal("Test timed out")278	case err := <-errC:279		cancel()280		require.NoError(t, err)281		wait()282		require.False(t, engine.IsTainted())283	}284	require.Contains(t, runner.defaultGroup.Groups, "setup")285	require.Contains(t, runner.defaultGroup.Groups, "teardown")286	var count int287	for _, s := range mockOutput.Samples {288		if s.Metric.Name == "mycounter" {289			count += int(s.Value)290		}291	}292	require.Equal(t, 501, count, "mycounter should be the number of iterations + 1 for the teardown")293}294func testSetupDataHelper(t *testing.T, data string) {295	t.Helper()296	expScriptOptions := lib.Options{297		SetupTimeout:    types.NullDurationFrom(1 * time.Second),298		TeardownTimeout: types.NullDurationFrom(1 * time.Second),299	}300	r1, err := getSimpleRunner(t, "/script.js", data) // TODO fix this301	require.NoError(t, err)302	require.Equal(t, expScriptOptions, r1.GetOptions())303	testdata := map[string]*Runner{"Source": r1}304	for name, r := range testdata {305		r := r306		t.Run(name, func(t *testing.T) {307			t.Parallel()308			samples := make(chan stats.SampleContainer, 100)309			if !assert.NoError(t, r.Setup(context.Background(), samples)) {310				return311			}312			initVU, err := r.NewVU(1, 1, samples)313			if assert.NoError(t, err) {314				ctx, cancel := context.WithCancel(context.Background())315				defer cancel()316				vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})317				err := vu.RunOnce()318				assert.NoError(t, err)319			}320		})321	}322}323func TestSetupDataReturnValue(t *testing.T) {324	t.Parallel()325	testSetupDataHelper(t, `326	exports.options = { setupTimeout: "1s", teardownTimeout: "1s" };327	exports.setup = function() {328		return 42;329	}330	exports.default = function(data) {331		if (data != 42) {332			throw new Error("default: wrong data: " + JSON.stringify(data))333		}334	};335	exports.teardown = function(data) {336		if (data != 42) {337			throw new Error("teardown: wrong data: " + JSON.stringify(data))338		}339	};`)340}341func TestSetupDataNoSetup(t *testing.T) {342	t.Parallel()343	testSetupDataHelper(t, `344	exports.options = { setupTimeout: "1s", teardownTimeout: "1s" };345	exports.default = function(data) {346		if (data !== undefined) {347			throw new Error("default: wrong data: " + JSON.stringify(data))348		}349	};350	exports.teardown = function(data) {351		if (data !== undefined) {352			console.log(data);353			throw new Error("teardown: wrong data: " + JSON.stringify(data))354		}355	};`)356}357func TestConsoleInInitContext(t *testing.T) {358	t.Parallel()359	r1, err := getSimpleRunner(t, "/script.js", `360			console.log("1");361			exports.default = function(data) {362			};363		`)364	require.NoError(t, err)365	testdata := map[string]*Runner{"Source": r1}366	for name, r := range testdata {367		r := r368		t.Run(name, func(t *testing.T) {369			t.Parallel()370			samples := make(chan stats.SampleContainer, 100)371			initVU, err := r.NewVU(1, 1, samples)372			if assert.NoError(t, err) {373				ctx, cancel := context.WithCancel(context.Background())374				defer cancel()375				vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})376				err := vu.RunOnce()377				assert.NoError(t, err)378			}379		})380	}381}382func TestSetupDataNoReturn(t *testing.T) {383	t.Parallel()384	testSetupDataHelper(t, `385	exports.options = { setupTimeout: "1s", teardownTimeout: "1s" };386	exports.setup = function() { }387	exports.default = function(data) {388		if (data !== undefined) {389			throw new Error("default: wrong data: " + JSON.stringify(data))390		}391	};392	exports.teardown = function(data) {393		if (data !== undefined) {394			throw new Error("teardown: wrong data: " + JSON.stringify(data))395		}396	};`)397}398func TestRunnerIntegrationImports(t *testing.T) {399	t.Parallel()400	t.Run("Modules", func(t *testing.T) {401		t.Parallel()402		modules := []string{403			"k6",404			"k6/http",405			"k6/metrics",406			"k6/html",407		}408		rtOpts := lib.RuntimeOptions{CompatibilityMode: null.StringFrom("extended")}409		for _, mod := range modules {410			mod := mod411			t.Run(mod, func(t *testing.T) {412				t.Run("Source", func(t *testing.T) {413					_, err := getSimpleRunner(t, "/script.js", fmt.Sprintf(`import "%s"; exports.default = function() {}`, mod), rtOpts)414					assert.NoError(t, err)415				})416			})417		}418	})419	t.Run("Files", func(t *testing.T) {420		t.Parallel()421		testdata := map[string]struct{ filename, path string }{422			"Absolute":       {"/path/script.js", "/path/to/lib.js"},423			"Relative":       {"/path/script.js", "./to/lib.js"},424			"Adjacent":       {"/path/to/script.js", "./lib.js"},425			"STDIN-Absolute": {"-", "/path/to/lib.js"},426			"STDIN-Relative": {"-", "./path/to/lib.js"},427		}428		for name, data := range testdata {429			name, data := name, data430			t.Run(name, func(t *testing.T) {431				t.Parallel()432				fs := afero.NewMemMapFs()433				require.NoError(t, fs.MkdirAll("/path/to", 0o755))434				require.NoError(t, afero.WriteFile(fs, "/path/to/lib.js", []byte(`exports.default = "hi!";`), 0o644))435				r1, err := getSimpleRunner(t, data.filename, fmt.Sprintf(`436					var hi = require("%s").default;437					exports.default = function() {438						if (hi != "hi!") { throw new Error("incorrect value"); }439					}`, data.path), fs)440				require.NoError(t, err)441				r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})442				require.NoError(t, err)443				testdata := map[string]*Runner{"Source": r1, "Archive": r2}444				for name, r := range testdata {445					r := r446					t.Run(name, func(t *testing.T) {447						initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))448						require.NoError(t, err)449						ctx, cancel := context.WithCancel(context.Background())450						defer cancel()451						vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})452						err = vu.RunOnce()453						require.NoError(t, err)454					})455				}456			})457		}458	})459}460func TestVURunContext(t *testing.T) {461	t.Parallel()462	r1, err := getSimpleRunner(t, "/script.js", `463		exports.options = { vus: 10 };464		exports.default = function() { fn(); }465		`)466	require.NoError(t, err)467	r1.SetOptions(r1.GetOptions().Apply(lib.Options{Throw: null.BoolFrom(true)}))468	r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})469	require.NoError(t, err)470	testdata := map[string]*Runner{"Source": r1, "Archive": r2}471	for name, r := range testdata {472		r := r473		t.Run(name, func(t *testing.T) {474			t.Parallel()475			vu, err := r.newVU(1, 1, make(chan stats.SampleContainer, 100))476			require.NoError(t, err)477			fnCalled := false478			vu.Runtime.Set("fn", func() {479				fnCalled = true480				assert.Equal(t, vu.Runtime, common.GetRuntime(*vu.Context), "incorrect runtime in context")481				assert.Nil(t, common.GetInitEnv(*vu.Context)) // shouldn't get this in the vu context482				state := lib.GetState(*vu.Context)483				if assert.NotNil(t, state) {484					assert.Equal(t, null.IntFrom(10), state.Options.VUs)485					assert.Equal(t, null.BoolFrom(true), state.Options.Throw)486					assert.NotNil(t, state.Logger)487					assert.Equal(t, r.GetDefaultGroup(), state.Group)488					assert.Equal(t, vu.Transport, state.Transport)489				}490			})491			ctx, cancel := context.WithCancel(context.Background())492			defer cancel()493			activeVU := vu.Activate(&lib.VUActivationParams{RunContext: ctx})494			err = activeVU.RunOnce()495			assert.NoError(t, err)496			assert.True(t, fnCalled, "fn() not called")497		})498	}499}500func TestVURunInterrupt(t *testing.T) {501	t.Parallel()502	r1, err := getSimpleRunner(t, "/script.js", `503		exports.default = function() { while(true) {} }504		`)505	require.NoError(t, err)506	require.NoError(t, r1.SetOptions(lib.Options{Throw: null.BoolFrom(true)}))507	r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})508	require.NoError(t, err)509	testdata := map[string]*Runner{"Source": r1, "Archive": r2}510	for name, r := range testdata {511		name, r := name, r512		t.Run(name, func(t *testing.T) {513			t.Parallel()514			samples := make(chan stats.SampleContainer, 100)515			defer close(samples)516			go func() {517				for range samples {518				}519			}()520			vu, err := r.newVU(1, 1, samples)521			require.NoError(t, err)522			ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond)523			defer cancel()524			activeVU := vu.Activate(&lib.VUActivationParams{RunContext: ctx})525			err = activeVU.RunOnce()526			assert.Error(t, err)527			assert.Contains(t, err.Error(), "context canceled")528		})529	}530}531func TestVURunInterruptDoesntPanic(t *testing.T) {532	t.Parallel()533	r1, err := getSimpleRunner(t, "/script.js", `534		exports.default = function() { while(true) {} }535		`)536	require.NoError(t, err)537	require.NoError(t, r1.SetOptions(lib.Options{Throw: null.BoolFrom(true)}))538	r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})539	require.NoError(t, err)540	testdata := map[string]*Runner{"Source": r1, "Archive": r2}541	for name, r := range testdata {542		r := r543		t.Run(name, func(t *testing.T) {544			t.Parallel()545			ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)546			defer cancel()547			samples := make(chan stats.SampleContainer, 100)548			defer close(samples)549			go func() {550				for range samples {551				}552			}()553			var wg sync.WaitGroup554			initVU, err := r.newVU(1, 1, samples)555			require.NoError(t, err)556			for i := 0; i < 1000; i++ {557				wg.Add(1)558				newCtx, newCancel := context.WithCancel(ctx)559				vu := initVU.Activate(&lib.VUActivationParams{560					RunContext:         newCtx,561					DeactivateCallback: func(_ lib.InitializedVU) { wg.Done() },562				})563				ch := make(chan struct{})564				go func() {565					close(ch)566					vuErr := vu.RunOnce()567					assert.Error(t, vuErr)568					assert.Contains(t, vuErr.Error(), "context canceled")569				}()570				<-ch571				time.Sleep(time.Millisecond * 1) // NOTE: increase this in case of problems ;)572				newCancel()573				wg.Wait()574			}575		})576	}577}578func TestVUIntegrationGroups(t *testing.T) {579	t.Parallel()580	r1, err := getSimpleRunner(t, "/script.js", `581		var group = require("k6").group;582		exports.default = function() {583			fnOuter();584			group("my group", function() {585				fnInner();586				group("nested group", function() {587					fnNested();588				})589			});590		}591		`)592	require.NoError(t, err)593	r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})594	require.NoError(t, err)595	testdata := map[string]*Runner{"Source": r1, "Archive": r2}596	for name, r := range testdata {597		r := r598		t.Run(name, func(t *testing.T) {599			t.Parallel()600			vu, err := r.newVU(1, 1, make(chan stats.SampleContainer, 100))601			require.NoError(t, err)602			fnOuterCalled := false603			fnInnerCalled := false604			fnNestedCalled := false605			vu.Runtime.Set("fnOuter", func() {606				fnOuterCalled = true607				assert.Equal(t, r.GetDefaultGroup(), lib.GetState(*vu.Context).Group)608			})609			vu.Runtime.Set("fnInner", func() {610				fnInnerCalled = true611				g := lib.GetState(*vu.Context).Group612				assert.Equal(t, "my group", g.Name)613				assert.Equal(t, r.GetDefaultGroup(), g.Parent)614			})615			vu.Runtime.Set("fnNested", func() {616				fnNestedCalled = true617				g := lib.GetState(*vu.Context).Group618				assert.Equal(t, "nested group", g.Name)619				assert.Equal(t, "my group", g.Parent.Name)620				assert.Equal(t, r.GetDefaultGroup(), g.Parent.Parent)621			})622			ctx, cancel := context.WithCancel(context.Background())623			defer cancel()624			activeVU := vu.Activate(&lib.VUActivationParams{RunContext: ctx})625			err = activeVU.RunOnce()626			assert.NoError(t, err)627			assert.True(t, fnOuterCalled, "fnOuter() not called")628			assert.True(t, fnInnerCalled, "fnInner() not called")629			assert.True(t, fnNestedCalled, "fnNested() not called")630		})631	}632}633func TestVUIntegrationMetrics(t *testing.T) {634	t.Parallel()635	r1, err := getSimpleRunner(t, "/script.js", `636		var group = require("k6").group;637		var Trend = require("k6/metrics").Trend;638		var myMetric = new Trend("my_metric");639		exports.default = function() { myMetric.add(5); }640		`)641	require.NoError(t, err)642	r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})643	require.NoError(t, err)644	testdata := map[string]*Runner{"Source": r1, "Archive": r2}645	for name, r := range testdata {646		r := r647		t.Run(name, func(t *testing.T) {648			t.Parallel()649			samples := make(chan stats.SampleContainer, 100)650			defer close(samples)651			vu, err := r.newVU(1, 1, samples)652			require.NoError(t, err)653			ctx, cancel := context.WithCancel(context.Background())654			defer cancel()655			activeVU := vu.Activate(&lib.VUActivationParams{RunContext: ctx})656			err = activeVU.RunOnce()657			assert.NoError(t, err)658			sampleCount := 0659			for i, sampleC := range stats.GetBufferedSamples(samples) {660				for j, s := range sampleC.GetSamples() {661					sampleCount++662					switch i + j {663					case 0:664						assert.Equal(t, 5.0, s.Value)665						assert.Equal(t, "my_metric", s.Metric.Name)666						assert.Equal(t, stats.Trend, s.Metric.Type)667					case 1:668						assert.Equal(t, 0.0, s.Value)669						assert.Equal(t, metrics.DataSent, s.Metric, "`data_sent` sample is before `data_received` and `iteration_duration`")670					case 2:671						assert.Equal(t, 0.0, s.Value)672						assert.Equal(t, metrics.DataReceived, s.Metric, "`data_received` sample is after `data_received`")673					case 3:674						assert.Equal(t, metrics.IterationDuration, s.Metric, "`iteration-duration` sample is after `data_received`")675					case 4:676						assert.Equal(t, metrics.Iterations, s.Metric, "`iterations` sample is after `iteration_duration`")677						assert.Equal(t, float64(1), s.Value)678					}679				}680			}681			assert.Equal(t, sampleCount, 5)682		})683	}684}685func TestVUIntegrationInsecureRequests(t *testing.T) {686	t.Parallel()687	testdata := map[string]struct {688		opts   lib.Options689		errMsg string690	}{691		"Null": {692			lib.Options{},693			"x509: certificate has expired or is not yet valid",694		},695		"False": {696			lib.Options{InsecureSkipTLSVerify: null.BoolFrom(false)},697			"x509: certificate has expired or is not yet valid",698		},699		"True": {700			lib.Options{InsecureSkipTLSVerify: null.BoolFrom(true)},701			"",702		},703	}704	for name, data := range testdata {705		data := data706		t.Run(name, func(t *testing.T) {707			t.Parallel()708			r1, err := getSimpleRunner(t, "/script.js", `709					var http = require("k6/http");;710					exports.default = function() { http.get("https://expired.badssl.com/"); }711				`)712			require.NoError(t, err)713			require.NoError(t, r1.SetOptions(lib.Options{Throw: null.BoolFrom(true)}.Apply(data.opts)))714			r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})715			require.NoError(t, err)716			runners := map[string]*Runner{"Source": r1, "Archive": r2}717			for name, r := range runners {718				r := r719				t.Run(name, func(t *testing.T) {720					t.Parallel()721					r.Logger, _ = logtest.NewNullLogger()722					initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))723					require.NoError(t, err)724					ctx, cancel := context.WithCancel(context.Background())725					defer cancel()726					vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})727					err = vu.RunOnce()728					if data.errMsg != "" {729						require.Error(t, err)730						assert.Contains(t, err.Error(), data.errMsg)731					} else {732						assert.NoError(t, err)733					}734				})735			}736		})737	}738}739func TestVUIntegrationBlacklistOption(t *testing.T) {740	t.Parallel()741	r1, err := getSimpleRunner(t, "/script.js", `742					var http = require("k6/http");;743					exports.default = function() { http.get("http://10.1.2.3/"); }744				`)745	require.NoError(t, err)746	cidr, err := lib.ParseCIDR("10.0.0.0/8")747	require.NoError(t, err)748	require.NoError(t, r1.SetOptions(lib.Options{749		Throw:        null.BoolFrom(true),750		BlacklistIPs: []*lib.IPNet{cidr},751	}))752	r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})753	require.NoError(t, err)754	runners := map[string]*Runner{"Source": r1, "Archive": r2}755	for name, r := range runners {756		r := r757		t.Run(name, func(t *testing.T) {758			t.Parallel()759			initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))760			require.NoError(t, err)761			ctx, cancel := context.WithCancel(context.Background())762			defer cancel()763			vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})764			err = vu.RunOnce()765			require.Error(t, err)766			assert.Contains(t, err.Error(), "IP (10.1.2.3) is in a blacklisted range (10.0.0.0/8)")767		})768	}769}770func TestVUIntegrationBlacklistScript(t *testing.T) {771	t.Parallel()772	r1, err := getSimpleRunner(t, "/script.js", `773					var http = require("k6/http");;774					exports.options = {775						throw: true,776						blacklistIPs: ["10.0.0.0/8"],777					};778					exports.default = function() { http.get("http://10.1.2.3/"); }779				`)780	require.NoError(t, err)781	r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})782	require.NoError(t, err)783	runners := map[string]*Runner{"Source": r1, "Archive": r2}784	for name, r := range runners {785		r := r786		t.Run(name, func(t *testing.T) {787			t.Parallel()788			initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))789			require.NoError(t, err)790			ctx, cancel := context.WithCancel(context.Background())791			defer cancel()792			vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})793			err = vu.RunOnce()794			require.Error(t, err)795			assert.Contains(t, err.Error(), "IP (10.1.2.3) is in a blacklisted range (10.0.0.0/8)")796		})797	}798}799func TestVUIntegrationBlockHostnamesOption(t *testing.T) {800	t.Parallel()801	r1, err := getSimpleRunner(t, "/script.js", `802					var http = require("k6/http");803					exports.default = function() { http.get("https://k6.io/"); }804				`)805	require.NoError(t, err)806	hostnames, err := types.NewNullHostnameTrie([]string{"*.io"})807	require.NoError(t, err)808	require.NoError(t, r1.SetOptions(lib.Options{809		Throw:            null.BoolFrom(true),810		BlockedHostnames: hostnames,811	}))812	r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})813	require.NoError(t, err)814	runners := map[string]*Runner{"Source": r1, "Archive": r2}815	for name, r := range runners {816		r := r817		t.Run(name, func(t *testing.T) {818			t.Parallel()819			initVu, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))820			require.NoError(t, err)821			vu := initVu.Activate(&lib.VUActivationParams{RunContext: context.Background()})822			err = vu.RunOnce()823			require.Error(t, err)824			assert.Contains(t, err.Error(), "hostname (k6.io) is in a blocked pattern (*.io)")825		})826	}827}828func TestVUIntegrationBlockHostnamesScript(t *testing.T) {829	t.Parallel()830	r1, err := getSimpleRunner(t, "/script.js", `831					var http = require("k6/http");832					exports.options = {833						throw: true,834						blockHostnames: ["*.io"],835					};836					exports.default = function() { http.get("https://k6.io/"); }837				`)838	require.NoError(t, err)839	r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})840	require.NoError(t, err)841	runners := map[string]*Runner{"Source": r1, "Archive": r2}842	for name, r := range runners {843		r := r844		t.Run(name, func(t *testing.T) {845			t.Parallel()846			initVu, err := r.NewVU(0, 0, make(chan stats.SampleContainer, 100))847			require.NoError(t, err)848			vu := initVu.Activate(&lib.VUActivationParams{RunContext: context.Background()})849			err = vu.RunOnce()850			require.Error(t, err)851			assert.Contains(t, err.Error(), "hostname (k6.io) is in a blocked pattern (*.io)")852		})853	}854}855func TestVUIntegrationHosts(t *testing.T) {856	t.Parallel()857	tb := httpmultibin.NewHTTPMultiBin(t)858	r1, err := getSimpleRunner(t, "/script.js",859		tb.Replacer.Replace(`860					var k6 = require("k6");861					var check = k6.check;862					var fail = k6.fail;863					var http = require("k6/http");;864					exports.default = function() {865						var res = http.get("http://test.loadimpact.com:HTTPBIN_PORT/");866						check(res, {867							"is correct IP": function(r) { return r.remote_ip === "127.0.0.1" }868						}) || fail("failed to override dns");869					}870				`))871	require.NoError(t, err)872	r1.SetOptions(lib.Options{873		Throw: null.BoolFrom(true),874		Hosts: map[string]*lib.HostAddress{875			"test.loadimpact.com": {IP: net.ParseIP("127.0.0.1")},876		},877	})878	r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})879	require.NoError(t, err)880	runners := map[string]*Runner{"Source": r1, "Archive": r2}881	for name, r := range runners {882		r := r883		t.Run(name, func(t *testing.T) {884			t.Parallel()885			initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))886			require.NoError(t, err)887			ctx, cancel := context.WithCancel(context.Background())888			defer cancel()889			vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})890			err = vu.RunOnce()891			require.NoError(t, err)892		})893	}894}895func TestVUIntegrationTLSConfig(t *testing.T) {896	t.Parallel()897	unsupportedVersionErrorMsg := "remote error: tls: handshake failure"898	for _, tag := range build.Default.ReleaseTags {899		if tag == "go1.12" {900			unsupportedVersionErrorMsg = "tls: no supported versions satisfy MinVersion and MaxVersion"901			break902		}903	}904	testdata := map[string]struct {905		opts   lib.Options906		errMsg string907	}{908		"NullCipherSuites": {909			lib.Options{},910			"",911		},912		"SupportedCipherSuite": {913			lib.Options{TLSCipherSuites: &lib.TLSCipherSuites{tls.TLS_RSA_WITH_AES_128_GCM_SHA256}},914			"",915		},916		"UnsupportedCipherSuite": {917			lib.Options{TLSCipherSuites: &lib.TLSCipherSuites{tls.TLS_RSA_WITH_RC4_128_SHA}},918			"remote error: tls: handshake failure",919		},920		"NullVersion": {921			lib.Options{},922			"",923		},924		"SupportedVersion": {925			lib.Options{TLSVersion: &lib.TLSVersions{Min: tls.VersionTLS12, Max: tls.VersionTLS12}},926			"",927		},928		"UnsupportedVersion": {929			lib.Options{TLSVersion: &lib.TLSVersions{Min: tls.VersionSSL30, Max: tls.VersionSSL30}},930			unsupportedVersionErrorMsg,931		},932	}933	for name, data := range testdata {934		data := data935		t.Run(name, func(t *testing.T) {936			t.Parallel()937			r1, err := getSimpleRunner(t, "/script.js", `938					var http = require("k6/http");;939					exports.default = function() { http.get("https://sha256.badssl.com/"); }940				`)941			require.NoError(t, err)942			require.NoError(t, r1.SetOptions(lib.Options{Throw: null.BoolFrom(true)}.Apply(data.opts)))943			r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})944			require.NoError(t, err)945			runners := map[string]*Runner{"Source": r1, "Archive": r2}946			for name, r := range runners {947				r := r948				t.Run(name, func(t *testing.T) {949					t.Parallel()950					r.Logger, _ = logtest.NewNullLogger()951					initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))952					require.NoError(t, err)953					ctx, cancel := context.WithCancel(context.Background())954					defer cancel()955					vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})956					err = vu.RunOnce()957					if data.errMsg != "" {958						require.Error(t, err)959						assert.Contains(t, err.Error(), data.errMsg)960					} else {961						assert.NoError(t, err)962					}963				})964			}965		})966	}967}968func TestVUIntegrationOpenFunctionError(t *testing.T) {969	t.Parallel()970	r, err := getSimpleRunner(t, "/script.js", `971			exports.default = function() { open("/tmp/foo") }972		`)973	assert.NoError(t, err)974	initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))975	assert.NoError(t, err)976	ctx, cancel := context.WithCancel(context.Background())977	defer cancel()978	vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})979	err = vu.RunOnce()980	assert.Error(t, err)981	assert.Contains(t, err.Error(), "only available in the init stage")982}983func TestVUIntegrationOpenFunctionErrorWhenSneaky(t *testing.T) {984	t.Parallel()985	r, err := getSimpleRunner(t, "/script.js", `986			var sneaky = open;987			exports.default = function() { sneaky("/tmp/foo") }988		`)989	assert.NoError(t, err)990	initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))991	assert.NoError(t, err)992	ctx, cancel := context.WithCancel(context.Background())993	defer cancel()994	vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})995	err = vu.RunOnce()996	assert.Error(t, err)997	assert.Contains(t, err.Error(), "only available in the init stage")998}999func TestVUIntegrationCookiesReset(t *testing.T) {1000	t.Parallel()1001	tb := httpmultibin.NewHTTPMultiBin(t)1002	r1, err := getSimpleRunner(t, "/script.js", tb.Replacer.Replace(`1003			var http = require("k6/http");;1004			exports.default = function() {1005				var url = "HTTPBIN_URL";1006				var preRes = http.get(url + "/cookies");1007				if (preRes.status != 200) { throw new Error("wrong status (pre): " + preRes.status); }1008				if (preRes.json().k1 || preRes.json().k2) {1009					throw new Error("cookies persisted: " + preRes.body);1010				}1011				var res = http.get(url + "/cookies/set?k2=v2&k1=v1");1012				if (res.status != 200) { throw new Error("wrong status: " + res.status) }1013				if (res.json().k1 != "v1" || res.json().k2 != "v2") {1014					throw new Error("wrong cookies: " + res.body);1015				}1016			}1017		`))1018	require.NoError(t, err)1019	r1.SetOptions(lib.Options{1020		Throw:        null.BoolFrom(true),1021		MaxRedirects: null.IntFrom(10),1022		Hosts:        tb.Dialer.Hosts,1023	})1024	r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})1025	require.NoError(t, err)1026	runners := map[string]*Runner{"Source": r1, "Archive": r2}1027	for name, r := range runners {1028		r := r1029		t.Run(name, func(t *testing.T) {1030			t.Parallel()1031			initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))1032			require.NoError(t, err)1033			ctx, cancel := context.WithCancel(context.Background())1034			defer cancel()1035			vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1036			for i := 0; i < 2; i++ {1037				err = vu.RunOnce()1038				assert.NoError(t, err)1039			}1040		})1041	}1042}1043func TestVUIntegrationCookiesNoReset(t *testing.T) {1044	t.Parallel()1045	tb := httpmultibin.NewHTTPMultiBin(t)1046	r1, err := getSimpleRunner(t, "/script.js", tb.Replacer.Replace(`1047			var http = require("k6/http");;1048			exports.default = function() {1049				var url = "HTTPBIN_URL";1050				if (__ITER == 0) {1051					var res = http.get(url + "/cookies/set?k2=v2&k1=v1");1052					if (res.status != 200) { throw new Error("wrong status: " + res.status) }1053					if (res.json().k1 != "v1" || res.json().k2 != "v2") {1054						throw new Error("wrong cookies: " + res.body);1055					}1056				}1057				if (__ITER == 1) {1058					var res = http.get(url + "/cookies");1059					if (res.status != 200) { throw new Error("wrong status (pre): " + res.status); }1060					if (res.json().k1 != "v1" || res.json().k2 != "v2") {1061						throw new Error("wrong cookies: " + res.body);1062					}1063				}1064			}1065		`))1066	require.NoError(t, err)1067	r1.SetOptions(lib.Options{1068		Throw:          null.BoolFrom(true),1069		MaxRedirects:   null.IntFrom(10),1070		Hosts:          tb.Dialer.Hosts,1071		NoCookiesReset: null.BoolFrom(true),1072	})1073	r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})1074	require.NoError(t, err)1075	runners := map[string]*Runner{"Source": r1, "Archive": r2}1076	for name, r := range runners {1077		r := r1078		t.Run(name, func(t *testing.T) {1079			t.Parallel()1080			initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))1081			require.NoError(t, err)1082			ctx, cancel := context.WithCancel(context.Background())1083			defer cancel()1084			vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1085			err = vu.RunOnce()1086			assert.NoError(t, err)1087			err = vu.RunOnce()1088			assert.NoError(t, err)1089		})1090	}1091}1092func TestVUIntegrationVUID(t *testing.T) {1093	t.Parallel()1094	r1, err := getSimpleRunner(t, "/script.js", `1095			exports.default = function() {1096				if (__VU != 1234) { throw new Error("wrong __VU: " + __VU); }1097			}`,1098	)1099	require.NoError(t, err)1100	r1.SetOptions(lib.Options{Throw: null.BoolFrom(true)})1101	r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})1102	require.NoError(t, err)1103	runners := map[string]*Runner{"Source": r1, "Archive": r2}1104	for name, r := range runners {1105		r := r1106		t.Run(name, func(t *testing.T) {1107			t.Parallel()1108			initVU, err := r.NewVU(1234, 1234, make(chan stats.SampleContainer, 100))1109			require.NoError(t, err)1110			ctx, cancel := context.WithCancel(context.Background())1111			defer cancel()1112			vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1113			err = vu.RunOnce()1114			assert.NoError(t, err)1115		})1116	}1117}1118func TestVUIntegrationClientCerts(t *testing.T) {1119	t.Parallel()1120	clientCAPool := x509.NewCertPool()1121	assert.True(t, clientCAPool.AppendCertsFromPEM(1122		[]byte("-----BEGIN CERTIFICATE-----\n"+1123			"MIIBYzCCAQqgAwIBAgIUMYw1pqZ1XhXdFG0S2ITXhfHBsWgwCgYIKoZIzj0EAwIw\n"+1124			"EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE1MTYxODAwWhcNMjIwODE0MTYxODAw\n"+1125			"WjAQMQ4wDAYDVQQDEwVNeSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFWO\n"+1126			"fg4dgL8cdvjoSWDQFLBJxlbQFlZfOSyUR277a4g91BD07KWX+9ny+Q8WuUODog06\n"+1127			"xH1g8fc6zuaejllfzM6jQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD\n"+1128			"AQH/MB0GA1UdDgQWBBTeoSFylGCmyqj1X4sWez1r6hkhjDAKBggqhkjOPQQDAgNH\n"+1129			"ADBEAiAfuKi6u/BVXenCkgnU2sfXsYjel6rACuXEcx01yaaWuQIgXAtjrDisdlf4\n"+1130			"0ZdoIoYjNhDAXUtnyRBt+V6+rIklv/8=\n"+1131			"-----END CERTIFICATE-----"),1132	))1133	serverCert, err := tls.X509KeyPair(1134		[]byte("-----BEGIN CERTIFICATE-----\n"+1135			"MIIBxjCCAW2gAwIBAgIUICcYHG1bI28NZm676wHlMPxL+CEwCgYIKoZIzj0EAwIw\n"+1136			"EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE3MTQwNjAwWhcNMTgwODE3MTQwNjAw\n"+1137			"WjAZMRcwFQYDVQQDEw4xMjcuMC4wLjE6Njk2OTBZMBMGByqGSM49AgEGCCqGSM49\n"+1138			"AwEHA0IABCdD1IqowucJ5oUjGYCZZnXvgi7EMD4jD1osbOkzOFFnHSLRvdm6fcJu\n"+1139			"vPUcl4g8zUs466sC0AVUNpk21XbA/QajgZswgZgwDgYDVR0PAQH/BAQDAgWgMB0G\n"+1140			"A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1Ud\n"+1141			"DgQWBBTeAc8HY3sgGIV+fu/lY0OKr2Ho0jAfBgNVHSMEGDAWgBTeoSFylGCmyqj1\n"+1142			"X4sWez1r6hkhjDAZBgNVHREEEjAQgg4xMjcuMC4wLjE6Njk2OTAKBggqhkjOPQQD\n"+1143			"AgNHADBEAiAt3gC5FGQfSJXQ5DloXAOeJDFnKIL7d6xhftgPS5O08QIgRuAyysB8\n"+1144			"5JXHvvze5DMN/clHYptos9idVFc+weUZAUQ=\n"+1145			"-----END CERTIFICATE-----\n"+1146			"-----BEGIN CERTIFICATE-----\n"+1147			"MIIBYzCCAQqgAwIBAgIUMYw1pqZ1XhXdFG0S2ITXhfHBsWgwCgYIKoZIzj0EAwIw\n"+1148			"EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE1MTYxODAwWhcNMjIwODE0MTYxODAw\n"+1149			"WjAQMQ4wDAYDVQQDEwVNeSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFWO\n"+1150			"fg4dgL8cdvjoSWDQFLBJxlbQFlZfOSyUR277a4g91BD07KWX+9ny+Q8WuUODog06\n"+1151			"xH1g8fc6zuaejllfzM6jQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD\n"+1152			"AQH/MB0GA1UdDgQWBBTeoSFylGCmyqj1X4sWez1r6hkhjDAKBggqhkjOPQQDAgNH\n"+1153			"ADBEAiAfuKi6u/BVXenCkgnU2sfXsYjel6rACuXEcx01yaaWuQIgXAtjrDisdlf4\n"+1154			"0ZdoIoYjNhDAXUtnyRBt+V6+rIklv/8=\n"+1155			"-----END CERTIFICATE-----"),1156		[]byte("-----BEGIN EC PRIVATE KEY-----\n"+1157			"MHcCAQEEIKYptA4VtQ8UOKL+d1wkhl+51aPpvO+ppY62nLF9Z1w5oAoGCCqGSM49\n"+1158			"AwEHoUQDQgAEJ0PUiqjC5wnmhSMZgJlmde+CLsQwPiMPWixs6TM4UWcdItG92bp9\n"+1159			"wm689RyXiDzNSzjrqwLQBVQ2mTbVdsD9Bg==\n"+1160			"-----END EC PRIVATE KEY-----"),1161	)1162	require.NoError(t, err)1163	listener, err := tls.Listen("tcp", "127.0.0.1:0", &tls.Config{1164		Certificates: []tls.Certificate{serverCert},1165		ClientAuth:   tls.RequireAndVerifyClientCert,1166		ClientCAs:    clientCAPool,1167	})1168	require.NoError(t, err)1169	t.Cleanup(func() { _ = listener.Close() })1170	srv := &http.Server{1171		Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {1172			_, _ = fmt.Fprintf(w, "ok")1173		}),1174		ErrorLog: stdlog.New(ioutil.Discard, "", 0),1175	}1176	go func() { _ = srv.Serve(listener) }()1177	t.Run("Unauthenticated", func(t *testing.T) {1178		t.Parallel()1179		r1, err := getSimpleRunner(t, "/script.js", fmt.Sprintf(`1180			var http = require("k6/http");;1181			exports.default = function() { http.get("https://%s")}1182		`, listener.Addr().String()))1183		require.NoError(t, err)1184		require.NoError(t, r1.SetOptions(lib.Options{1185			Throw:                 null.BoolFrom(true),1186			InsecureSkipTLSVerify: null.BoolFrom(true),1187		}))1188		r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})1189		require.NoError(t, err)1190		runners := map[string]*Runner{"Source": r1, "Archive": r2}1191		for name, r := range runners {1192			r := r1193			t.Run(name, func(t *testing.T) {1194				t.Parallel()1195				r.Logger, _ = logtest.NewNullLogger()1196				initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))1197				if assert.NoError(t, err) {1198					ctx, cancel := context.WithCancel(context.Background())1199					defer cancel()1200					vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1201					err := vu.RunOnce()1202					require.Error(t, err)1203					assert.Contains(t, err.Error(), "remote error: tls: bad certificate")1204				}1205			})1206		}1207	})1208	t.Run("Authenticated", func(t *testing.T) {1209		t.Parallel()1210		r1, err := getSimpleRunner(t, "/script.js", fmt.Sprintf(`1211			var http = require("k6/http");;1212			exports.default = function() { http.get("https://%s")}1213		`, listener.Addr().String()))1214		require.NoError(t, err)1215		require.NoError(t, r1.SetOptions(lib.Options{1216			Throw:                 null.BoolFrom(true),1217			InsecureSkipTLSVerify: null.BoolFrom(true),1218		}))1219		require.NoError(t, r1.SetOptions(lib.Options{1220			TLSAuth: []*lib.TLSAuth{1221				{1222					TLSAuthFields: lib.TLSAuthFields{1223						Domains: []string{"127.0.0.1"},1224						Cert: "-----BEGIN CERTIFICATE-----\n" +1225							"MIIBoTCCAUigAwIBAgIUd6XedDxP+rGo+kq0APqHElGZzs4wCgYIKoZIzj0EAwIw\n" +1226							"EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE3MTUwNjAwWhcNMTgwODE3MTUwNjAw\n" +1227							"WjARMQ8wDQYDVQQDEwZjbGllbnQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATL\n" +1228							"mi/a1RVvk05FyrYmartbo/9cW+53DrQLW1twurII2q5ZfimdMX05A32uB3Ycoy/J\n" +1229							"x+w7Ifyd/YRw0zEc3NHQo38wfTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI\n" +1230							"KwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFN2SR/TD\n" +1231							"yNW5DQWxZSkoXHQWsLY+MB8GA1UdIwQYMBaAFN6hIXKUYKbKqPVfixZ7PWvqGSGM\n" +1232							"MAoGCCqGSM49BAMCA0cAMEQCICtETmyOmupmg4w3tw59VYJyOBqRTxg6SK+rOQmq\n" +1233							"kE1VAiAUvsflDfmWBZ8EMPu46OhX6RX6MbvJ9NNvRco2G5ek1w==\n" +1234							"-----END CERTIFICATE-----",1235						Key: "-----BEGIN EC PRIVATE KEY-----\n" +1236							"MHcCAQEEIOrnhT05alCeQEX66HgnSHah/m5LazjJHLDawYRnhUtZoAoGCCqGSM49\n" +1237							"AwEHoUQDQgAEy5ov2tUVb5NORcq2Jmq7W6P/XFvudw60C1tbcLqyCNquWX4pnTF9\n" +1238							"OQN9rgd2HKMvycfsOyH8nf2EcNMxHNzR0A==\n" +1239							"-----END EC PRIVATE KEY-----",1240					},1241				},1242			},1243		}))1244		r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})1245		require.NoError(t, err)1246		runners := map[string]*Runner{"Source": r1, "Archive": r2}1247		for name, r := range runners {1248			r := r1249			t.Run(name, func(t *testing.T) {1250				initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))1251				if assert.NoError(t, err) {1252					ctx, cancel := context.WithCancel(context.Background())1253					defer cancel()1254					vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1255					err := vu.RunOnce()1256					assert.NoError(t, err)1257				}1258			})1259		}1260	})1261}1262func TestHTTPRequestInInitContext(t *testing.T) {1263	t.Parallel()1264	tb := httpmultibin.NewHTTPMultiBin(t)1265	_, err := getSimpleRunner(t, "/script.js", tb.Replacer.Replace(`1266					var k6 = require("k6");1267					var check = k6.check;1268					var fail = k6.fail;1269					var http = require("k6/http");;1270					var res = http.get("HTTPBIN_URL/");1271					exports.default = function() {1272						console.log(test);1273					}1274				`))1275	if assert.Error(t, err) {1276		assert.Contains(1277			t,1278			err.Error(),1279			k6http.ErrHTTPForbiddenInInitContext.Error())1280	}1281}1282func TestInitContextForbidden(t *testing.T) {1283	t.Parallel()1284	table := [...][3]string{1285		{1286			"http.request",1287			`var http = require("k6/http");;1288			 var res = http.get("HTTPBIN_URL");1289			 exports.default = function() { console.log("p"); }`,1290			k6http.ErrHTTPForbiddenInInitContext.Error(),1291		},1292		{1293			"http.batch",1294			`var http = require("k6/http");;1295			 var res = http.batch("HTTPBIN_URL/something", "HTTPBIN_URL/else");1296			 exports.default = function() { console.log("p"); }`,1297			k6http.ErrBatchForbiddenInInitContext.Error(),1298		},1299		{1300			"http.cookieJar",1301			`var http = require("k6/http");;1302			 var jar = http.cookieJar();1303			 exports.default = function() { console.log("p"); }`,1304			k6http.ErrJarForbiddenInInitContext.Error(),1305		},1306		{1307			"check",1308			`var check = require("k6").check;1309			 check("test", {'is test': function(test) { return test == "test"}})1310			 exports.default = function() { console.log("p"); }`,1311			k6.ErrCheckInInitContext.Error(),1312		},1313		{1314			"group",1315			`var group = require("k6").group;1316			 group("group1", function () { console.log("group1");})1317			 exports.default = function() { console.log("p"); }`,1318			k6.ErrGroupInInitContext.Error(),1319		},1320		{1321			"ws",1322			`var ws = require("k6/ws");1323			 var url = "ws://echo.websocket.org";1324			 var params = { "tags": { "my_tag": "hello" } };1325			 var response = ws.connect(url, params, function (socket) {1326			   socket.on('open', function open() {1327					console.log('connected');1328			   })1329		   });1330			 exports.default = function() { console.log("p"); }`,1331			ws.ErrWSInInitContext.Error(),1332		},1333		{1334			"metric",1335			`var Counter = require("k6/metrics").Counter;1336			 var counter = Counter("myCounter");1337			 counter.add(1);1338			 exports.default = function() { console.log("p"); }`,1339			k6metrics.ErrMetricsAddInInitContext.Error(),1340		},1341	}1342	tb := httpmultibin.NewHTTPMultiBin(t)1343	for _, test := range table {1344		test := test1345		t.Run(test[0], func(t *testing.T) {1346			t.Parallel()1347			_, err := getSimpleRunner(t, "/script.js", tb.Replacer.Replace(test[1]))1348			if assert.Error(t, err) {1349				assert.Contains(1350					t,1351					err.Error(),1352					test[2])1353			}1354		})1355	}1356}1357func TestArchiveRunningIntegrity(t *testing.T) {1358	t.Parallel()1359	fs := afero.NewMemMapFs()1360	data := `1361			var fput = open("/home/somebody/test.json");1362			exports.options = { setupTimeout: "10s", teardownTimeout: "10s" };1363			exports.setup = function () {1364				return JSON.parse(fput);1365			}1366			exports.default = function(data) {1367				if (data != 42) {1368					throw new Error("incorrect answer " + data);1369				}1370			}1371		`1372	require.NoError(t, afero.WriteFile(fs, "/home/somebody/test.json", []byte(`42`), os.ModePerm))1373	require.NoError(t, afero.WriteFile(fs, "/script.js", []byte(data), os.ModePerm))1374	r1, err := getSimpleRunner(t, "/script.js", data, fs)1375	require.NoError(t, err)1376	buf := bytes.NewBuffer(nil)1377	require.NoError(t, r1.MakeArchive().Write(buf))1378	arc, err := lib.ReadArchive(buf)1379	require.NoError(t, err)1380	r2, err := NewFromArchive(testutils.NewLogger(t), arc, lib.RuntimeOptions{})1381	require.NoError(t, err)1382	runners := map[string]*Runner{"Source": r1, "Archive": r2}1383	for name, r := range runners {1384		r := r1385		t.Run(name, func(t *testing.T) {1386			t.Parallel()1387			var err error1388			ch := make(chan stats.SampleContainer, 100)1389			err = r.Setup(context.Background(), ch)1390			require.NoError(t, err)1391			initVU, err := r.NewVU(1, 1, ch)1392			require.NoError(t, err)1393			ctx, cancel := context.WithCancel(context.Background())1394			defer cancel()1395			vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1396			err = vu.RunOnce()1397			require.NoError(t, err)1398		})1399	}1400}1401func TestArchiveNotPanicking(t *testing.T) {1402	t.Parallel()1403	fs := afero.NewMemMapFs()1404	require.NoError(t, afero.WriteFile(fs, "/non/existent", []byte(`42`), os.ModePerm))1405	r1, err := getSimpleRunner(t, "/script.js", `1406			var fput = open("/non/existent");1407			exports.default = function(data) {}1408		`, fs)1409	require.NoError(t, err)1410	arc := r1.MakeArchive()1411	arc.Filesystems = map[string]afero.Fs{"file": afero.NewMemMapFs()}1412	r2, err := NewFromArchive(testutils.NewLogger(t), arc, lib.RuntimeOptions{})1413	// we do want this to error here as this is where we find out that a given file is not in the1414	// archive1415	require.Error(t, err)1416	require.Nil(t, r2)1417}1418func TestStuffNotPanicking(t *testing.T) {1419	t.Parallel()1420	tb := httpmultibin.NewHTTPMultiBin(t)1421	r, err := getSimpleRunner(t, "/script.js", tb.Replacer.Replace(`1422			var http = require("k6/http");1423			var ws = require("k6/ws");1424			var group = require("k6").group;1425			var parseHTML = require("k6/html").parseHTML;1426			exports.options = { iterations: 1, vus: 1 };1427			exports.default = function() {1428				var doc = parseHTML(http.get("HTTPBIN_URL/html").body);1429				var testCases = [1430					function() { return group()},1431					function() { return group("test")},1432					function() { return group("test", "wat")},1433					function() { return doc.find('p').each()},1434					function() { return doc.find('p').each("wat")},1435					function() { return doc.find('p').map()},1436					function() { return doc.find('p').map("wat")},1437					function() { return ws.connect("WSBIN_URL/ws-echo")},1438				];1439				testCases.forEach(function(fn, idx) {1440					var hasException;1441					try {1442						fn();1443						hasException = false;1444					} catch (e) {1445						hasException = true;1446					}1447					if (hasException === false) {1448						throw new Error("Expected test case #" + idx + " to return an error");1449					} else if (hasException === undefined) {1450						throw new Error("Something strange happened with test case #" + idx);1451					}1452				});1453			}1454		`))1455	require.NoError(t, err)1456	ch := make(chan stats.SampleContainer, 1000)1457	initVU, err := r.NewVU(1, 1, ch)1458	require.NoError(t, err)1459	ctx, cancel := context.WithCancel(context.Background())1460	vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1461	errC := make(chan error)1462	go func() { errC <- vu.RunOnce() }()1463	select {1464	case <-time.After(15 * time.Second):1465		cancel()1466		t.Fatal("Test timed out")1467	case err := <-errC:1468		cancel()1469		require.NoError(t, err)1470	}1471}1472func TestPanicOnSimpleHTML(t *testing.T) {1473	t.Parallel()1474	r, err := getSimpleRunner(t, "/script.js", `1475			var parseHTML = require("k6/html").parseHTML;1476			exports.options = { iterations: 1, vus: 1 };1477			exports.default = function() {1478				var doc = parseHTML("<html>");1479				var o = doc.find(".something").slice(0, 4).toArray()1480			};1481		`)1482	require.NoError(t, err)1483	ch := make(chan stats.SampleContainer, 1000)1484	initVU, err := r.NewVU(1, 1, ch)1485	require.NoError(t, err)1486	ctx, cancel := context.WithCancel(context.Background())1487	vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1488	errC := make(chan error)1489	go func() { errC <- vu.RunOnce() }()1490	select {1491	case <-time.After(15 * time.Second):1492		cancel()1493		t.Fatal("Test timed out")1494	case err := <-errC:1495		cancel()1496		require.NoError(t, err)1497	}1498}1499func TestSystemTags(t *testing.T) {1500	t.Parallel()1501	tb := httpmultibin.NewHTTPMultiBin(t)1502	// Handle paths with custom logic1503	tb.Mux.HandleFunc("/wrong-redirect", func(w http.ResponseWriter, r *http.Request) {1504		w.Header().Add("Location", "%")1505		w.WriteHeader(http.StatusTemporaryRedirect)1506	})1507	httpURL, err := url.Parse(tb.ServerHTTP.URL)1508	require.NoError(t, err)1509	testedSystemTags := []struct{ tag, exec, expVal string }{1510		{"proto", "http_get", "HTTP/1.1"},1511		{"status", "http_get", "200"},1512		{"method", "http_get", "GET"},1513		{"url", "http_get", tb.ServerHTTP.URL},1514		{"url", "https_get", tb.ServerHTTPS.URL},1515		{"ip", "http_get", httpURL.Hostname()},1516		{"name", "http_get", tb.ServerHTTP.URL},1517		{"group", "http_get", ""},1518		{"vu", "http_get", "8"},1519		{"vu", "noop", "9"},1520		{"iter", "http_get", "0"},1521		{"iter", "noop", "0"},1522		{"tls_version", "https_get", "tls1.3"},1523		{"ocsp_status", "https_get", "unknown"},1524		{"error", "bad_url_get", `dial: connection refused`},1525		{"error_code", "bad_url_get", "1212"},1526		{"scenario", "http_get", "default"},1527		// TODO: add more tests1528	}1529	for num, tc := range testedSystemTags {1530		num, tc := num, tc1531		t.Run(fmt.Sprintf("TC %d with only %s", num, tc.tag), func(t *testing.T) {1532			t.Parallel()1533			samples := make(chan stats.SampleContainer, 100)1534			r, err := getSimpleRunner(t, "/script.js", tb.Replacer.Replace(`1535				var http = require("k6/http");1536				exports.http_get = function() {1537					http.get("HTTPBIN_IP_URL");1538				};1539				exports.https_get = function() {1540					http.get("HTTPSBIN_IP_URL");1541				};1542				exports.bad_url_get = function() {1543					http.get("http://127.0.0.1:1");1544				};1545				exports.noop = function() {};1546			`), lib.RuntimeOptions{CompatibilityMode: null.StringFrom("base")})1547			require.NoError(t, err)1548			require.NoError(t, r.SetOptions(r.GetOptions().Apply(lib.Options{1549				Throw:                 null.BoolFrom(false),1550				TLSVersion:            &lib.TLSVersions{Max: tls.VersionTLS13},1551				SystemTags:            stats.ToSystemTagSet([]string{tc.tag}),1552				InsecureSkipTLSVerify: null.BoolFrom(true),1553			})))1554			vu, err := r.NewVU(uint64(num), 0, samples)1555			require.NoError(t, err)1556			activeVU := vu.Activate(&lib.VUActivationParams{1557				RunContext: context.Background(),1558				Exec:       tc.exec,1559				Scenario:   "default",1560			})1561			require.NoError(t, activeVU.RunOnce())1562			bufSamples := stats.GetBufferedSamples(samples)1563			require.NotEmpty(t, bufSamples)1564			for _, sample := range bufSamples[0].GetSamples() {1565				assert.NotEmpty(t, sample.Tags)1566				for emittedTag, emittedVal := range sample.Tags.CloneTags() {1567					assert.Equal(t, tc.tag, emittedTag)1568					assert.Equal(t, tc.expVal, emittedVal)1569				}1570			}1571		})1572	}1573}1574func TestVUPanic(t *testing.T) {1575	t.Parallel()1576	r1, err := getSimpleRunner(t, "/script.js", `1577			var group = require("k6").group;1578			exports.default = function() {1579				group("panic here", function() {1580					if (__ITER == 0) {1581						panic("here we panic");1582					}1583					console.log("here we don't");1584				})1585			}`,1586	)1587	require.NoError(t, err)1588	r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{})1589	require.NoError(t, err)1590	runners := map[string]*Runner{"Source": r1, "Archive": r2}1591	for name, r := range runners {1592		r := r1593		t.Run(name, func(t *testing.T) {1594			t.Parallel()1595			initVU, err := r.NewVU(1, 1234, make(chan stats.SampleContainer, 100))1596			require.NoError(t, err)1597			logger := logrus.New()1598			logger.SetLevel(logrus.InfoLevel)1599			logger.Out = ioutil.Discard1600			hook := testutils.SimpleLogrusHook{1601				HookedLevels: []logrus.Level{logrus.InfoLevel, logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel},1602			}1603			logger.AddHook(&hook)1604			ctx, cancel := context.WithCancel(context.Background())1605			defer cancel()1606			vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1607			vu.(*ActiveVU).Runtime.Set("panic", func(str string) { panic(str) })1608			vu.(*ActiveVU).state.Logger = logger1609			vu.(*ActiveVU).Console.logger = logger.WithField("source", "console")1610			err = vu.RunOnce()1611			require.Error(t, err)1612			assert.Contains(t, err.Error(), "a panic occurred in VU code but was caught: here we panic")1613			entries := hook.Drain()1614			require.Len(t, entries, 1)1615			assert.Equal(t, logrus.ErrorLevel, entries[0].Level)1616			require.True(t, strings.HasPrefix(entries[0].Message, "panic: here we panic"))1617			// broken since goja@f3cfc97811c0b4d8337902c3e42fb2371ba1d524 see1618			// https://github.com/dop251/goja/issues/179#issuecomment-7835720201619			// require.True(t, strings.HasSuffix(entries[0].Message, "Goja stack:\nfile:///script.js:3:4(12)"))1620			err = vu.RunOnce()1621			assert.NoError(t, err)1622			entries = hook.Drain()1623			require.Len(t, entries, 1)1624			assert.Equal(t, logrus.InfoLevel, entries[0].Level)1625			require.Contains(t, entries[0].Message, "here we don't")1626		})1627	}1628}1629type multiFileTestCase struct {1630	fses       map[string]afero.Fs1631	rtOpts     lib.RuntimeOptions1632	cwd        string1633	script     string1634	expInitErr bool1635	expVUErr   bool1636	samples    chan stats.SampleContainer1637}1638func runMultiFileTestCase(t *testing.T, tc multiFileTestCase, tb *httpmultibin.HTTPMultiBin) {1639	t.Helper()1640	logger := testutils.NewLogger(t)1641	runner, err := New(1642		logger,1643		&loader.SourceData{1644			URL:  &url.URL{Path: tc.cwd + "/script.js", Scheme: "file"},1645			Data: []byte(tc.script),1646		},1647		tc.fses,1648		tc.rtOpts,1649	)1650	if tc.expInitErr {1651		require.Error(t, err)1652		return1653	}1654	require.NoError(t, err)1655	options := runner.GetOptions()1656	require.Empty(t, options.Validate())1657	vu, err := runner.NewVU(1, 1, tc.samples)1658	require.NoError(t, err)1659	jsVU, ok := vu.(*VU)1660	require.True(t, ok)1661	jsVU.state.Dialer = tb.Dialer1662	jsVU.state.TLSConfig = tb.TLSClientConfig1663	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)1664	defer cancel()1665	activeVU := vu.Activate(&lib.VUActivationParams{RunContext: ctx})1666	err = activeVU.RunOnce()1667	if tc.expVUErr {1668		require.Error(t, err)1669	} else {1670		require.NoError(t, err)1671	}1672	arc := runner.MakeArchive()1673	runnerFromArc, err := NewFromArchive(logger, arc, tc.rtOpts)1674	require.NoError(t, err)1675	vuFromArc, err := runnerFromArc.NewVU(2, 2, tc.samples)1676	require.NoError(t, err)1677	jsVUFromArc, ok := vuFromArc.(*VU)1678	require.True(t, ok)1679	jsVUFromArc.state.Dialer = tb.Dialer1680	jsVUFromArc.state.TLSConfig = tb.TLSClientConfig1681	activeVUFromArc := jsVUFromArc.Activate(&lib.VUActivationParams{RunContext: ctx})1682	err = activeVUFromArc.RunOnce()1683	if tc.expVUErr {1684		require.Error(t, err)1685		return1686	}1687	require.NoError(t, err)1688}1689func TestComplicatedFileImportsForGRPC(t *testing.T) {1690	t.Parallel()1691	tb := httpmultibin.NewHTTPMultiBin(t)1692	tb.GRPCStub.UnaryCallFunc = func(ctx context.Context, sreq *grpc_testing.SimpleRequest) (1693		*grpc_testing.SimpleResponse, error,1694	) {1695		return &grpc_testing.SimpleResponse{1696			Username: "foo",1697		}, nil1698	}1699	fs := afero.NewMemMapFs()1700	protoFile, err := ioutil.ReadFile("../vendor/google.golang.org/grpc/test/grpc_testing/test.proto")1701	require.NoError(t, err)1702	require.NoError(t, afero.WriteFile(fs, "/path/to/service.proto", protoFile, 0644))1703	require.NoError(t, afero.WriteFile(fs, "/path/to/same-dir.proto", []byte(1704		`syntax = "proto3";package whatever;import "service.proto";`,1705	), 0644))1706	require.NoError(t, afero.WriteFile(fs, "/path/subdir.proto", []byte(1707		`syntax = "proto3";package whatever;import "to/service.proto";`,1708	), 0644))1709	require.NoError(t, afero.WriteFile(fs, "/path/to/abs.proto", []byte(1710		`syntax = "proto3";package whatever;import "/path/to/service.proto";`,1711	), 0644))1712	grpcTestCase := func(expInitErr, expVUErr bool, cwd, loadCode string) multiFileTestCase {1713		script := tb.Replacer.Replace(fmt.Sprintf(`1714			var grpc = require('k6/net/grpc');1715			var client = new grpc.Client();1716			%s // load statements1717			exports.default = function() {1718				client.connect('GRPCBIN_ADDR', {timeout: '3s'});1719				var resp = client.invoke('grpc.testing.TestService/UnaryCall', {})1720				if (!resp.message || resp.error || resp.message.username !== 'foo') {1721					throw new Error('unexpected response message: ' + JSON.stringify(resp.message))1722				}1723			}1724		`, loadCode))1725		return multiFileTestCase{1726			fses:    map[string]afero.Fs{"file": fs, "https": afero.NewMemMapFs()},1727			rtOpts:  lib.RuntimeOptions{CompatibilityMode: null.NewString("base", true)},1728			samples: make(chan stats.SampleContainer, 100),1729			cwd:     cwd, expInitErr: expInitErr, expVUErr: expVUErr, script: script,1730		}1731	}1732	testCases := []multiFileTestCase{1733		grpcTestCase(false, true, "/", `/* no grpc loads */`), // exp VU error with no proto files loaded1734		// Init errors when the protobuf file can't be loaded1735		grpcTestCase(true, false, "/", `client.load(null, 'service.proto');`),1736		grpcTestCase(true, false, "/", `client.load(null, '/wrong/path/to/service.proto');`),1737		grpcTestCase(true, false, "/", `client.load(['/', '/path/'], 'service.proto');`),1738		// Direct imports of service.proto1739		grpcTestCase(false, false, "/", `client.load(null, '/path/to/service.proto');`), // full path should be fine1740		grpcTestCase(false, false, "/path/to/", `client.load([], 'service.proto');`),    // file name from same folder1741		grpcTestCase(false, false, "/", `client.load(['./path//to/'], 'service.proto');`),1742		grpcTestCase(false, false, "/path/", `client.load(['./to/'], 'service.proto');`),1743		grpcTestCase(false, false, "/whatever", `client.load(['/path/to/'], 'service.proto');`),  // with import paths1744		grpcTestCase(false, false, "/path", `client.load(['/', '/path/to/'], 'service.proto');`), // with import paths1745		grpcTestCase(false, false, "/whatever", `client.load(['../path/to/'], 'service.proto');`),1746		// Import another file that imports "service.proto" directly1747		grpcTestCase(true, false, "/", `client.load([], '/path/to/same-dir.proto');`),1748		grpcTestCase(true, false, "/path/", `client.load([], 'to/same-dir.proto');`),1749		grpcTestCase(true, false, "/", `client.load(['/path/'], 'to/same-dir.proto');`),1750		grpcTestCase(false, false, "/path/to/", `client.load([], 'same-dir.proto');`),1751		grpcTestCase(false, false, "/", `client.load(['/path/to/'], 'same-dir.proto');`),1752		grpcTestCase(false, false, "/whatever", `client.load(['/other', '/path/to/'], 'same-dir.proto');`),1753		grpcTestCase(false, false, "/", `client.load(['./path//to/'], 'same-dir.proto');`),1754		grpcTestCase(false, false, "/path/", `client.load(['./to/'], 'same-dir.proto');`),1755		grpcTestCase(false, false, "/whatever", `client.load(['../path/to/'], 'same-dir.proto');`),1756		// Import another file that imports "to/service.proto" directly1757		grpcTestCase(true, false, "/", `client.load([], '/path/to/subdir.proto');`),1758		grpcTestCase(false, false, "/path/", `client.load([], 'subdir.proto');`),1759		grpcTestCase(false, false, "/", `client.load(['/path/'], 'subdir.proto');`),1760		grpcTestCase(false, false, "/", `client.load(['./path/'], 'subdir.proto');`),1761		grpcTestCase(false, false, "/whatever", `client.load(['/other', '/path/'], 'subdir.proto');`),1762		grpcTestCase(false, false, "/whatever", `client.load(['../other', '../path/'], 'subdir.proto');`),1763		// Import another file that imports "/path/to/service.proto" directly1764		grpcTestCase(true, false, "/", `client.load(['/path'], '/path/to/abs.proto');`),1765		grpcTestCase(false, false, "/", `client.load([], '/path/to/abs.proto');`),1766		grpcTestCase(false, false, "/whatever", `client.load(['/'], '/path/to/abs.proto');`),1767	}1768	for i, tc := range testCases {1769		i, tc := i, tc1770		t.Run(fmt.Sprintf("TestCase_%d", i), func(t *testing.T) {1771			t.Parallel()1772			t.Logf(1773				"CWD: %s, expInitErr: %t, expVUErr: %t, script injected with: `%s`",1774				tc.cwd, tc.expInitErr, tc.expVUErr, tc.script,1775			)1776			runMultiFileTestCase(t, tc, tb)1777		})1778	}1779}1780func TestMinIterationDurationIsCancellable(t *testing.T) {1781	t.Parallel()1782	r, err := getSimpleRunner(t, "/script.js", `1783			exports.options = { iterations: 1, vus: 1, minIterationDuration: '1m' };1784			exports.default = function() { /* do nothing */ };1785		`)1786	require.NoError(t, err)1787	ch := make(chan stats.SampleContainer, 1000)1788	initVU, err := r.NewVU(1, 1, ch)1789	require.NoError(t, err)1790	ctx, cancel := context.WithCancel(context.Background())1791	vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1792	errC := make(chan error)1793	go func() { errC <- vu.RunOnce() }()1794	time.Sleep(200 * time.Millisecond) // give it some time to actually start1795	cancel() // simulate the end of gracefulStop or a Ctrl+C event1796	select {1797	case <-time.After(3 * time.Second):1798		t.Fatal("Test timed out or minIterationDuration prevailed")1799	case err := <-errC:1800		require.NoError(t, err)1801	}1802}...initcontext_test.go
Source:initcontext_test.go  
1/*2 *3 * k6 - a next-generation load testing tool4 * Copyright (C) 2016 Load Impact5 *6 * This program is free software: you can redistribute it and/or modify7 * it under the terms of the GNU Affero General Public License as8 * published by the Free Software Foundation, either version 3 of the9 * License, or (at your option) any later version.10 *11 * This program is distributed in the hope that it will be useful,12 * but WITHOUT ANY WARRANTY; without even the implied warranty of13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the14 * GNU Affero General Public License for more details.15 *16 * You should have received a copy of the GNU Affero General Public License17 * along with this program.  If not, see <http://www.gnu.org/licenses/>.18 *19 */20package js21import (22	"context"23	"fmt"24	"io/ioutil"25	"net"26	"net/http"27	"net/http/httptest"28	"path/filepath"29	"testing"30	"time"31	"github.com/dop251/goja"32	"github.com/oxtoacart/bpool"33	"github.com/sirupsen/logrus"34	"github.com/spf13/afero"35	"github.com/stretchr/testify/assert"36	"github.com/stretchr/testify/require"37	"go.k6.io/k6/js/common"38	"go.k6.io/k6/lib"39	"go.k6.io/k6/lib/consts"40	"go.k6.io/k6/lib/netext"41	"go.k6.io/k6/lib/testutils"42	"go.k6.io/k6/lib/types"43	"go.k6.io/k6/stats"44)45func TestInitContextRequire(t *testing.T) {46	t.Parallel()47	t.Run("Modules", func(t *testing.T) {48		t.Run("Nonexistent", func(t *testing.T) {49			t.Parallel()50			_, err := getSimpleBundle(t, "/script.js", `import "k6/NONEXISTENT";`)51			assert.Contains(t, err.Error(), "unknown module: k6/NONEXISTENT")52		})53		t.Run("k6", func(t *testing.T) {54			t.Parallel()55			logger := testutils.NewLogger(t)56			b, err := getSimpleBundle(t, "/script.js", `57					import k6 from "k6";58					export let _k6 = k6;59					export let dummy = "abc123";60					export default function() {}61			`)62			if !assert.NoError(t, err, "bundle error") {63				return64			}65			bi, err := b.Instantiate(logger, 0)66			if !assert.NoError(t, err, "instance error") {67				return68			}69			exports := bi.Runtime.Get("exports").ToObject(bi.Runtime)70			if assert.NotNil(t, exports) {71				_, defaultOk := goja.AssertFunction(exports.Get("default"))72				assert.True(t, defaultOk, "default export is not a function")73				assert.Equal(t, "abc123", exports.Get("dummy").String())74			}75			k6 := bi.Runtime.Get("_k6").ToObject(bi.Runtime)76			if assert.NotNil(t, k6) {77				_, groupOk := goja.AssertFunction(k6.Get("group"))78				assert.True(t, groupOk, "k6.group is not a function")79			}80		})81		t.Run("group", func(t *testing.T) {82			logger := testutils.NewLogger(t)83			t.Parallel()84			b, err := getSimpleBundle(t, "/script.js", `85						import { group } from "k6";86						export let _group = group;87						export let dummy = "abc123";88						export default function() {}89				`)90			require.NoError(t, err)91			bi, err := b.Instantiate(logger, 0)92			require.NoError(t, err)93			exports := bi.Runtime.Get("exports").ToObject(bi.Runtime)94			if assert.NotNil(t, exports) {95				_, defaultOk := goja.AssertFunction(exports.Get("default"))96				assert.True(t, defaultOk, "default export is not a function")97				assert.Equal(t, "abc123", exports.Get("dummy").String())98			}99			_, groupOk := goja.AssertFunction(exports.Get("_group"))100			assert.True(t, groupOk, "{ group } is not a function")101		})102	})103	t.Run("Files", func(t *testing.T) {104		t.Parallel()105		t.Run("Nonexistent", func(t *testing.T) {106			t.Parallel()107			path := filepath.FromSlash("/nonexistent.js")108			_, err := getSimpleBundle(t, "/script.js", `import "/nonexistent.js"; export default function() {}`)109			assert.NotNil(t, err)110			assert.Contains(t, err.Error(), fmt.Sprintf(`"%s" couldn't be found on local disk`, filepath.ToSlash(path)))111		})112		t.Run("Invalid", func(t *testing.T) {113			t.Parallel()114			fs := afero.NewMemMapFs()115			assert.NoError(t, afero.WriteFile(fs, "/file.js", []byte{0x00}, 0o755))116			_, err := getSimpleBundle(t, "/script.js", `import "/file.js"; export default function() {}`, fs)117			require.Error(t, err)118			assert.Contains(t, err.Error(), "SyntaxError: file:///file.js: Unexpected character '\x00' (1:0)\n> 1 | \x00\n")119		})120		t.Run("Error", func(t *testing.T) {121			t.Parallel()122			fs := afero.NewMemMapFs()123			assert.NoError(t, afero.WriteFile(fs, "/file.js", []byte(`throw new Error("aaaa")`), 0o755))124			_, err := getSimpleBundle(t, "/script.js", `import "/file.js"; export default function() {}`, fs)125			assert.EqualError(t, err, "Error: aaaa\n\tat file:///file.js:2:7(4)\n\tat reflect.methodValueCall (native)\n\tat file:///script.js:1:117(14)\n")126		})127		imports := map[string]struct {128			LibPath    string129			ConstPaths map[string]string130		}{131			"./lib.js": {"/path/to/lib.js", map[string]string{132				"":               "",133				"./const.js":     "/path/to/const.js",134				"../const.js":    "/path/const.js",135				"./sub/const.js": "/path/to/sub/const.js",136			}},137			"../lib.js": {"/path/lib.js", map[string]string{138				"":               "",139				"./const.js":     "/path/const.js",140				"../const.js":    "/const.js",141				"./sub/const.js": "/path/sub/const.js",142			}},143			"./dir/lib.js": {"/path/to/dir/lib.js", map[string]string{144				"":               "",145				"./const.js":     "/path/to/dir/const.js",146				"../const.js":    "/path/to/const.js",147				"./sub/const.js": "/path/to/dir/sub/const.js",148			}},149			"/path/to/lib.js": {"/path/to/lib.js", map[string]string{150				"":               "",151				"./const.js":     "/path/to/const.js",152				"../const.js":    "/path/const.js",153				"./sub/const.js": "/path/to/sub/const.js",154			}},155		}156		for libName, data := range imports {157			libName, data := libName, data158			t.Run("lib=\""+libName+"\"", func(t *testing.T) {159				t.Parallel()160				for constName, constPath := range data.ConstPaths {161					constName, constPath := constName, constPath162					name := "inline"163					if constName != "" {164						name = "const=\"" + constName + "\""165					}166					t.Run(name, func(t *testing.T) {167						t.Parallel()168						fs := afero.NewMemMapFs()169						logger := testutils.NewLogger(t)170						jsLib := `export default function() { return 12345; }`171						if constName != "" {172							jsLib = fmt.Sprintf(173								`import { c } from "%s"; export default function() { return c; }`,174								constName,175							)176							constsrc := `export let c = 12345;`177							assert.NoError(t, fs.MkdirAll(filepath.Dir(constPath), 0o755))178							assert.NoError(t, afero.WriteFile(fs, constPath, []byte(constsrc), 0o644))179						}180						assert.NoError(t, fs.MkdirAll(filepath.Dir(data.LibPath), 0o755))181						assert.NoError(t, afero.WriteFile(fs, data.LibPath, []byte(jsLib), 0o644))182						data := fmt.Sprintf(`183								import fn from "%s";184								let v = fn();185								export default function() {};`,186							libName)187						b, err := getSimpleBundle(t, "/path/to/script.js", data, fs)188						require.NoError(t, err)189						if constPath != "" {190							assert.Contains(t, b.BaseInitContext.programs, "file://"+constPath)191						}192						_, err = b.Instantiate(logger, 0)193						require.NoError(t, err)194					})195				}196			})197		}198		t.Run("Isolation", func(t *testing.T) {199			t.Parallel()200			logger := testutils.NewLogger(t)201			fs := afero.NewMemMapFs()202			assert.NoError(t, afero.WriteFile(fs, "/a.js", []byte(`const myvar = "a";`), 0o644))203			assert.NoError(t, afero.WriteFile(fs, "/b.js", []byte(`const myvar = "b";`), 0o644))204			data := `205				import "./a.js";206				import "./b.js";207				export default function() {208					if (typeof myvar != "undefined") {209						throw new Error("myvar is set in global scope");210					}211				};`212			b, err := getSimpleBundle(t, "/script.js", data, fs)213			require.NoError(t, err)214			bi, err := b.Instantiate(logger, 0)215			require.NoError(t, err)216			_, err = bi.exports[consts.DefaultFn](goja.Undefined())217			assert.NoError(t, err)218		})219	})220}221func createAndReadFile(t *testing.T, file string, content []byte, expectedLength int, binary string) (*BundleInstance, error) {222	t.Helper()223	fs := afero.NewMemMapFs()224	assert.NoError(t, fs.MkdirAll("/path/to", 0o755))225	assert.NoError(t, afero.WriteFile(fs, "/path/to/"+file, content, 0o644))226	data := fmt.Sprintf(`227		let binArg = "%s";228		export let data = open("/path/to/%s", binArg);229		var expectedLength = %d;230		var len = binArg === "b" ? "byteLength" : "length";231		if (data[len] != expectedLength) {232			throw new Error("Length not equal, expected: " + expectedLength + ", actual: " + data[len]);233		}234		export default function() {}235	`, binary, file, expectedLength)236	b, err := getSimpleBundle(t, "/path/to/script.js", data, fs)237	if !assert.NoError(t, err) {238		return nil, err239	}240	bi, err := b.Instantiate(testutils.NewLogger(t), 0)241	if !assert.NoError(t, err) {242		return nil, err243	}244	return bi, nil245}246func TestInitContextOpen(t *testing.T) {247	t.Parallel()248	testCases := []struct {249		content []byte250		file    string251		length  int252	}{253		{[]byte("hello world!"), "ascii", 12},254		{[]byte("?((¯°·._.⢠ţâ¬$ţɨɲǥ µɲɨȼà¹Äâ¬Î£SЫ ɨɲ Ð6 â¢._.·°¯))Øâ¢"), "utf", 47},255		{[]byte{0o44, 226, 130, 172}, "utf-8", 2}, // $â¬256		//{[]byte{00, 36, 32, 127}, "utf-16", 2},   // $â¬257	}258	for _, tc := range testCases {259		tc := tc260		t.Run(tc.file, func(t *testing.T) {261			t.Parallel()262			bi, err := createAndReadFile(t, tc.file, tc.content, tc.length, "")263			require.NoError(t, err)264			assert.Equal(t, string(tc.content), bi.Runtime.Get("data").Export())265		})266	}267	t.Run("Binary", func(t *testing.T) {268		t.Parallel()269		bi, err := createAndReadFile(t, "/path/to/file.bin", []byte("hi!\x0f\xff\x01"), 6, "b")270		require.NoError(t, err)271		buf := bi.Runtime.NewArrayBuffer([]byte{104, 105, 33, 15, 255, 1})272		assert.Equal(t, buf, bi.Runtime.Get("data").Export())273	})274	testdata := map[string]string{275		"Absolute": "/path/to/file",276		"Relative": "./file",277	}278	for name, loadPath := range testdata {279		loadPath := loadPath280		t.Run(name, func(t *testing.T) {281			t.Parallel()282			_, err := createAndReadFile(t, loadPath, []byte("content"), 7, "")283			require.NoError(t, err)284		})285	}286	t.Run("Nonexistent", func(t *testing.T) {287		t.Parallel()288		path := filepath.FromSlash("/nonexistent.txt")289		_, err := getSimpleBundle(t, "/script.js", `open("/nonexistent.txt"); export default function() {}`)290		assert.Contains(t, err.Error(), fmt.Sprintf("open %s: file does not exist", path))291	})292	t.Run("Directory", func(t *testing.T) {293		t.Parallel()294		path := filepath.FromSlash("/some/dir")295		fs := afero.NewMemMapFs()296		assert.NoError(t, fs.MkdirAll(path, 0o755))297		_, err := getSimpleBundle(t, "/script.js", `open("/some/dir"); export default function() {}`, fs)298		assert.Contains(t, err.Error(), fmt.Sprintf("open() can't be used with directories, path: %q", path))299	})300}301func TestRequestWithBinaryFile(t *testing.T) {302	t.Parallel()303	ch := make(chan bool, 1)304	h := func(w http.ResponseWriter, r *http.Request) {305		defer func() {306			ch <- true307		}()308		assert.NoError(t, r.ParseMultipartForm(32<<20))309		file, _, err := r.FormFile("file")310		assert.NoError(t, err)311		defer func() {312			assert.NoError(t, file.Close())313		}()314		bytes := make([]byte, 3)315		_, err = file.Read(bytes)316		assert.NoError(t, err)317		assert.Equal(t, []byte("hi!"), bytes)318		assert.Equal(t, "this is a standard form field", r.FormValue("field"))319	}320	srv := httptest.NewServer(http.HandlerFunc(h))321	defer srv.Close()322	fs := afero.NewMemMapFs()323	assert.NoError(t, fs.MkdirAll("/path/to", 0o755))324	assert.NoError(t, afero.WriteFile(fs, "/path/to/file.bin", []byte("hi!"), 0o644))325	b, err := getSimpleBundle(t, "/path/to/script.js",326		fmt.Sprintf(`327			import http from "k6/http";328			let binFile = open("/path/to/file.bin", "b");329			export default function() {330				var data = {331					field: "this is a standard form field",332					file: http.file(binFile, "test.bin")333				};334				var res = http.post("%s", data);335				return true;336			}337			`, srv.URL), fs)338	require.NoError(t, err)339	bi, err := b.Instantiate(testutils.NewLogger(t), 0)340	assert.NoError(t, err)341	root, err := lib.NewGroup("", nil)342	assert.NoError(t, err)343	logger := logrus.New()344	logger.Level = logrus.DebugLevel345	logger.Out = ioutil.Discard346	state := &lib.State{347		Options: lib.Options{},348		Logger:  logger,349		Group:   root,350		Transport: &http.Transport{351			DialContext: (netext.NewDialer(352				net.Dialer{353					Timeout:   10 * time.Second,354					KeepAlive: 60 * time.Second,355					DualStack: true,356				},357				netext.NewResolver(net.LookupIP, 0, types.DNSfirst, types.DNSpreferIPv4),358			)).DialContext,359		},360		BPool:   bpool.NewBufferPool(1),361		Samples: make(chan stats.SampleContainer, 500),362	}363	ctx := context.Background()364	ctx = lib.WithState(ctx, state)365	ctx = common.WithRuntime(ctx, bi.Runtime)366	*bi.Context = ctx367	v, err := bi.exports[consts.DefaultFn](goja.Undefined())368	assert.NoError(t, err)369	require.NotNil(t, v)370	assert.Equal(t, true, v.Export())371	<-ch372}373func TestRequestWithMultipleBinaryFiles(t *testing.T) {374	t.Parallel()375	ch := make(chan bool, 1)376	h := func(w http.ResponseWriter, r *http.Request) {377		defer func() {378			ch <- true379		}()380		require.NoError(t, r.ParseMultipartForm(32<<20))381		require.Len(t, r.MultipartForm.File["files"], 2)382		for i, fh := range r.MultipartForm.File["files"] {383			f, _ := fh.Open()384			defer func() { assert.NoError(t, f.Close()) }()385			bytes := make([]byte, 5)386			_, err := f.Read(bytes)387			assert.NoError(t, err)388			switch i {389			case 0:390				assert.Equal(t, []byte("file1"), bytes)391			case 1:392				assert.Equal(t, []byte("file2"), bytes)393			}394		}395	}396	srv := httptest.NewServer(http.HandlerFunc(h))397	defer srv.Close()398	fs := afero.NewMemMapFs()399	assert.NoError(t, fs.MkdirAll("/path/to", 0o755))400	assert.NoError(t, afero.WriteFile(fs, "/path/to/file1.bin", []byte("file1"), 0o644))401	assert.NoError(t, afero.WriteFile(fs, "/path/to/file2.bin", []byte("file2"), 0o644))402	b, err := getSimpleBundle(t, "/path/to/script.js",403		fmt.Sprintf(`404	import http from 'k6/http';405	function toByteArray(obj) {406		let arr = [];407		if (typeof obj === 'string') {408			for (let i=0; i < obj.length; i++) {409			  arr.push(obj.charCodeAt(i) & 0xff);410			}411		} else {412			obj = new Uint8Array(obj);413			for (let i=0; i < obj.byteLength; i++) {414			  arr.push(obj[i] & 0xff);415			}416		}417		return arr;418	}419	// A more robust version of this polyfill is available here:420	// https://jslib.k6.io/formdata/0.0.1/index.js421	function FormData() {422		this.boundary = '----boundary';423		this.files = [];424	}425	FormData.prototype.append = function(name, value, filename) {426		this.files.push({427			name: name,428			value: value,429			filename: filename,430		});431	}432	FormData.prototype.body = function(name, value, filename) {433		let body = [];434		let barr = toByteArray('--' + this.boundary + '\r\n');435		for (let i=0; i < this.files.length; i++) {436			body.push(...barr);437			let cdarr = toByteArray('Content-Disposition: form-data; name="'438							+ this.files[i].name + '"; filename="'439							+ this.files[i].filename440							+ '"\r\nContent-Type: application/octet-stream\r\n\r\n');441			body.push(...cdarr);442			body.push(...toByteArray(this.files[i].value));443			body.push(...toByteArray('\r\n'));444		}445		body.push(...toByteArray('--' + this.boundary + '--\r\n'));446		return new Uint8Array(body).buffer;447	}448	const file1 = open('/path/to/file1.bin', 'b');449	const file2 = open('/path/to/file2.bin', 'b');450	export default function () {451		const fd = new FormData();452		fd.append('files', file1, 'file1.bin');453		fd.append('files', file2, 'file2.bin');454		let res = http.post('%s', fd.body(),455				{ headers: { 'Content-Type': 'multipart/form-data; boundary=' + fd.boundary }});456		if (res.status !== 200) {457			throw new Error('Expected HTTP 200 response, received: ' + res.status);458		}459		return true;460	}461			`, srv.URL), fs)462	require.NoError(t, err)463	bi, err := b.Instantiate(testutils.NewLogger(t), 0)464	assert.NoError(t, err)465	root, err := lib.NewGroup("", nil)466	assert.NoError(t, err)467	logger := logrus.New()468	logger.Level = logrus.DebugLevel469	logger.Out = ioutil.Discard470	state := &lib.State{471		Options: lib.Options{},472		Logger:  logger,473		Group:   root,474		Transport: &http.Transport{475			DialContext: (netext.NewDialer(476				net.Dialer{477					Timeout:   10 * time.Second,478					KeepAlive: 60 * time.Second,479					DualStack: true,480				},481				netext.NewResolver(net.LookupIP, 0, types.DNSfirst, types.DNSpreferIPv4),482			)).DialContext,483		},484		BPool:   bpool.NewBufferPool(1),485		Samples: make(chan stats.SampleContainer, 500),486	}487	ctx := context.Background()488	ctx = lib.WithState(ctx, state)489	ctx = common.WithRuntime(ctx, bi.Runtime)490	*bi.Context = ctx491	v, err := bi.exports[consts.DefaultFn](goja.Undefined())492	assert.NoError(t, err)493	require.NotNil(t, v)494	assert.Equal(t, true, v.Export())495	<-ch496}497func TestInitContextVU(t *testing.T) {498	t.Parallel()499	b, err := getSimpleBundle(t, "/script.js", `500		let vu = __VU;501		export default function() { return vu; }502	`)503	require.NoError(t, err)504	bi, err := b.Instantiate(testutils.NewLogger(t), 5)505	require.NoError(t, err)506	v, err := bi.exports[consts.DefaultFn](goja.Undefined())507	require.NoError(t, err)508	assert.Equal(t, int64(5), v.Export())509}...initcontext.go
Source:initcontext.go  
1/*2 *3 * k6 - a next-generation load testing tool4 * Copyright (C) 2016 Load Impact5 *6 * This program is free software: you can redistribute it and/or modify7 * it under the terms of the GNU Affero General Public License as8 * published by the Free Software Foundation, either version 3 of the9 * License, or (at your option) any later version.10 *11 * This program is distributed in the hope that it will be useful,12 * but WITHOUT ANY WARRANTY; without even the implied warranty of13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the14 * GNU Affero General Public License for more details.15 *16 * You should have received a copy of the GNU Affero General Public License17 * along with this program.  If not, see <http://www.gnu.org/licenses/>.18 *19 */20package js21import (22	"context"23	"errors"24	"fmt"25	"net/url"26	"path/filepath"27	"runtime"28	"strings"29	"github.com/dop251/goja"30	"github.com/sirupsen/logrus"31	"github.com/spf13/afero"32	"go.k6.io/k6/js/common"33	"go.k6.io/k6/js/compiler"34	"go.k6.io/k6/js/modules"35	"go.k6.io/k6/lib"36	"go.k6.io/k6/loader"37)38type programWithSource struct {39	pgm    *goja.Program40	src    string41	module *goja.Object42}43const openCantBeUsedOutsideInitContextMsg = `The "open()" function is only available in the init stage ` +44	`(i.e. the global scope), see https://k6.io/docs/using-k6/test-life-cycle for more information`45// InitContext provides APIs for use in the init context.46//47// TODO: refactor most/all of this state away, use common.InitEnvironment instead48type InitContext struct {49	// Bound runtime; used to instantiate objects.50	runtime  *goja.Runtime51	compiler *compiler.Compiler52	// Pointer to a context that bridged modules are invoked with.53	ctxPtr *context.Context54	// Filesystem to load files and scripts from with the map key being the scheme55	filesystems map[string]afero.Fs56	pwd         *url.URL57	// Cache of loaded programs and files.58	programs map[string]programWithSource59	compatibilityMode lib.CompatibilityMode60	logger logrus.FieldLogger61	modules map[string]interface{}62}63// NewInitContext creates a new initcontext with the provided arguments64func NewInitContext(65	logger logrus.FieldLogger, rt *goja.Runtime, c *compiler.Compiler, compatMode lib.CompatibilityMode,66	ctxPtr *context.Context, filesystems map[string]afero.Fs, pwd *url.URL,67) *InitContext {68	return &InitContext{69		runtime:           rt,70		compiler:          c,71		ctxPtr:            ctxPtr,72		filesystems:       filesystems,73		pwd:               pwd,74		programs:          make(map[string]programWithSource),75		compatibilityMode: compatMode,76		logger:            logger,77		modules:           modules.GetJSModules(),78	}79}80func newBoundInitContext(base *InitContext, ctxPtr *context.Context, rt *goja.Runtime) *InitContext {81	// we don't copy the exports as otherwise they will be shared and we don't want this.82	// this means that all the files will be executed again but once again only once per compilation83	// of the main file.84	programs := make(map[string]programWithSource, len(base.programs))85	for key, program := range base.programs {86		programs[key] = programWithSource{87			src: program.src,88			pgm: program.pgm,89		}90	}91	return &InitContext{92		runtime: rt,93		ctxPtr:  ctxPtr,94		filesystems: base.filesystems,95		pwd:         base.pwd,96		compiler:    base.compiler,97		programs:          programs,98		compatibilityMode: base.compatibilityMode,99		logger:            base.logger,100		modules:           base.modules,101	}102}103// Require is called when a module/file needs to be loaded by a script104func (i *InitContext) Require(arg string) goja.Value {105	switch {106	case arg == "k6", strings.HasPrefix(arg, "k6/"):107		// Builtin or external modules ("k6", "k6/*", or "k6/x/*") are handled108		// specially, as they don't exist on the filesystem. This intentionally109		// shadows attempts to name your own modules this.110		v, err := i.requireModule(arg)111		if err != nil {112			common.Throw(i.runtime, err)113		}114		return v115	default:116		// Fall back to loading from the filesystem.117		v, err := i.requireFile(arg)118		if err != nil {119			common.Throw(i.runtime, err)120		}121		return v122	}123}124func (i *InitContext) requireModule(name string) (goja.Value, error) {125	mod, ok := i.modules[name]126	if !ok {127		return nil, fmt.Errorf("unknown module: %s", name)128	}129	if perInstance, ok := mod.(modules.HasModuleInstancePerVU); ok {130		mod = perInstance.NewModuleInstancePerVU()131	}132	return i.runtime.ToValue(common.Bind(i.runtime, mod, i.ctxPtr)), nil133}134func (i *InitContext) requireFile(name string) (goja.Value, error) {135	// Resolve the file path, push the target directory as pwd to make relative imports work.136	pwd := i.pwd137	fileURL, err := loader.Resolve(pwd, name)138	if err != nil {139		return nil, err140	}141	// First, check if we have a cached program already.142	pgm, ok := i.programs[fileURL.String()]143	if !ok || pgm.module == nil {144		if filepath.IsAbs(name) && runtime.GOOS == "windows" {145			i.logger.Warnf("'%s' was imported with an absolute path - this won't be cross-platform and won't work if"+146				" you move the script between machines or run it with `k6 cloud`; if absolute paths are required,"+147				" import them with the `file://` schema for slightly better compatibility",148				name)149		}150		i.pwd = loader.Dir(fileURL)151		defer func() { i.pwd = pwd }()152		exports := i.runtime.NewObject()153		pgm.module = i.runtime.NewObject()154		_ = pgm.module.Set("exports", exports)155		if pgm.pgm == nil {156			// Load the sources; the loader takes care of remote loading, etc.157			data, err := loader.Load(i.logger, i.filesystems, fileURL, name)158			if err != nil {159				return goja.Undefined(), err160			}161			pgm.src = string(data.Data)162			// Compile the sources; this handles ES5 vs ES6 automatically.163			pgm.pgm, err = i.compileImport(pgm.src, data.URL.String())164			if err != nil {165				return goja.Undefined(), err166			}167		}168		i.programs[fileURL.String()] = pgm169		// Run the program.170		f, err := i.runtime.RunProgram(pgm.pgm)171		if err != nil {172			delete(i.programs, fileURL.String())173			return goja.Undefined(), err174		}175		if call, ok := goja.AssertFunction(f); ok {176			if _, err = call(exports, pgm.module, exports); err != nil {177				return nil, err178			}179		}180	}181	return pgm.module.Get("exports"), nil182}183func (i *InitContext) compileImport(src, filename string) (*goja.Program, error) {184	pgm, _, err := i.compiler.Compile(src, filename,185		"(function(module, exports){\n", "\n})\n", true, i.compatibilityMode)186	return pgm, err187}188// Open implements open() in the init context and will read and return the189// contents of a file. If the second argument is "b" it returns an ArrayBuffer190// instance, otherwise a string representation.191func (i *InitContext) Open(ctx context.Context, filename string, args ...string) (goja.Value, error) {192	if lib.GetState(ctx) != nil {193		return nil, errors.New(openCantBeUsedOutsideInitContextMsg)194	}195	if filename == "" {196		return nil, errors.New("open() can't be used with an empty filename")197	}198	// Here IsAbs should be enough but unfortunately it doesn't handle absolute paths starting from199	// the current drive on windows like `\users\noname\...`. Also it makes it more easy to test and200	// will probably be need for archive execution under windows if always consider '/...' as an201	// absolute path.202	if filename[0] != '/' && filename[0] != '\\' && !filepath.IsAbs(filename) {203		filename = filepath.Join(i.pwd.Path, filename)204	}205	filename = filepath.Clean(filename)206	fs := i.filesystems["file"]207	if filename[0:1] != afero.FilePathSeparator {208		filename = afero.FilePathSeparator + filename209	}210	// Workaround for https://github.com/spf13/afero/issues/201211	if isDir, err := afero.IsDir(fs, filename); err != nil {212		return nil, err213	} else if isDir {214		return nil, fmt.Errorf("open() can't be used with directories, path: %q", filename)215	}216	data, err := afero.ReadFile(fs, filename)217	if err != nil {218		return nil, err219	}220	if len(args) > 0 && args[0] == "b" {221		ab := i.runtime.NewArrayBuffer(data)222		return i.runtime.ToValue(&ab), nil223	}224	return i.runtime.ToValue(string(data)), nil225}...Exports
Using AI Code Generation
1var k6 = require("k6");2var check = k6.check;3var fail = k6.fail;4var group = k6.group;5var sleep = k6.sleep;6var http = k6.http;7module.exports.default = function () {8  var params = {9    headers: {10    },11  };12  var response = http.get(url, params);13  check(response, {14    "status is 200": (r) => r.status === 200,15  });16};17var k6 = require("k6");18var check = k6.check;19var fail = k6.fail;20var group = k6.group;21var sleep = k6.sleep;22var http = k6.http;23module.exports.default = function () {24  var params = {25    headers: {26    },27  };28  var response = http.get(url, params);29  check(response, {30    "status is 200": (r) => r.status === 200,31  });32};33var k6 = require("k6");34var check = k6.check;35var fail = k6.fail;36var group = k6.group;37var sleep = k6.sleep;38var http = k6.http;39module.exports.default = function () {40  var params = {41    headers: {42    },43  };44  var response = http.get(url, params);45  check(response, {46    "status is 200": (r) => r.status === 200,47  });48};49var k6 = require("k6");50var check = k6.check;51var fail = k6.fail;52var group = k6.group;53var sleep = k6.sleep;54var http = k6.http;55module.exports.default = function () {Exports
Using AI Code Generation
1import (2func init() {3    modules.Register("k6/x/foobar", new(Foobar))4}5type Foobar struct{}6func (*Foobar) Exports() interface{} {7    return map[string]interface{}{8        "foo": func(ctx context.Context) {9        },10    }11}12import (13func init() {14    modules.Register("k6/x/foobar", new(Foobar))15}16type Foobar struct{}17func (*Foobar) Exports() interface{} {18    return map[string]interface{}{19        "foo": func(ctx context.Context) {20        },21    }22}23import (24func init() {25    modules.Register("k6/x/foobar", new(Foobar))26}27type Foobar struct{}28func (*Foobar) Exports() interface{} {29    return map[string]interface{}{30        "foo": func(ctx context.Context) {31        },32    }33}34import (35func init() {36    modules.Register("k6/x/foobar", new(Foobar))37}38type Foobar struct{}39func (*Foobar) Exports() interface{} {40    return map[string]interface{}{41        "foo": func(ctx context.Context) {42        },43    }44}45$ docker run -v $(pwd):/go/src/github.com/loadimpact/k6 -w /go/src/github.com/loadimpact/k6 loadimpact/k6 run /go/src/github.com/loadimpact/k6/examples/foobar.jsExports
Using AI Code Generation
1func main() {2    k6 := k6.New()3    k6.Exports()4}5func main() {6    k6 := k6.New()7    k6.Exports()8}9func main() {10    k6 := k6.New()11    k6.Exports()12}13func main() {14    k6 := k6.New()15    k6.Exports()16}17func main() {18    k6 := k6.New()19    k6.Exports()20}21func main() {22    k6 := k6.New()23    k6.Exports()24}25func main() {26    k6 := k6.New()27    k6.Exports()28}29func main() {30    k6 := k6.New()31    k6.Exports()32}33func main() {34    k6 := k6.New()35    k6.Exports()36}37func main() {38    k6 := k6.New()39    k6.Exports()40}41func main() {42    k6 := k6.New()43    k6.Exports()44}45func main() {46    k6 := k6.New()47    k6.Exports()48}49func main() {50    k6 := k6.New()51    k6.Exports()52}53func main() {Exports
Using AI Code Generation
1func Exports() map[string]interface{} {2    return map[string]interface{}{3    }4}5func Exports() map[string]interface{} {6    return map[string]interface{}{7    }8}9func Exports() map[string]interface{} {10    return map[string]interface{}{11    }12}13func Exports() map[string]interface{} {14    return map[string]interface{}{15    }16}17func Exports() map[string]interface{} {18    return map[string]interface{}{19    }20}21func Exports() map[string]interface{} {22    return map[string]interface{}{23    }24}25func Exports() map[string]interface{} {26    return map[string]interface{}{27    }28}29func Exports() map[string]interface{} {30    return map[string]interface{}{31    }32}33func Exports() map[string]interface{} {34    return map[string]interface{}{35    }36}37func Exports() map[string]interface{} {38    return map[string]interface{}{39    }40}41func Exports() map[string]interface{} {42    return map[string]interface{}{43    }44}45func Exports() map[string]interface{} {46    return map[string]interface{}{47    }48}Exports
Using AI Code Generation
1import (2func main() {3	fmt.Println(k6.Exports)4}5import (6func main() {7	fmt.Println(k6.Exports)8}9import (10func main() {11	fmt.Println(k6.Exports)12}13import (14func main() {15	fmt.Println(k6.Exports)16}17import (18func main() {19	fmt.Println(k6.Exports)20}21import (22func main() {23	fmt.Println(k6.Exports)24}25import (26func main() {27	fmt.Println(k6.Exports)28}29import (30func main() {31	fmt.Println(k6.Exports)32}33import (34func main() {35	fmt.Println(k6.Exports)36}37import (Exports
Using AI Code Generation
1import (2var (3	Options = lib.Options{}4func init() {5	modules.Register("k6/x/collector", new(Collector))6}7type Collector struct{}8func (*Collector) Exports() interface{} {9	return map[string]interface{}{10		"new": func() interface{} {11			return &Collector{}12		},13	}14}15func (*Collector) Init() error {16	fmt.Println("Init")17}18func (*Collector) AddMetricSamples(samples []stats.SampleContainer) {19	fmt.Println("AddMetricSamples")20}21func (*Collector) Link(*lib.VU) error {22	fmt.Println("Link")23}24func (*Collector) Run(ctx context.Context) {25	fmt.Println("Run")26}27func (*Collector) GetOptions() interface{} {28}29func (*Collector) SetOptions(opts interface{}) {30	fmt.Println("SetOptions")31}32func (*Collector) MakeRequestExports
Using AI Code Generation
1import k6 from 'k6';2export default function () {3    k6.Exports('test');4}5import k6 from 'k6';6export default function () {7    k6.Exports('test');8}9import k6 from 'k6';10export default function () {11    k6.Exports('test');12}13import k6 from 'k6';14export default function () {15    k6.Exports('test');16}17import k6 from 'k6';18export default function () {19    k6.Exports('test');20}21import k6 from 'k6';22export default function () {23    k6.Exports('test');24}25import k6 from 'k6';26export default function () {27    k6.Exports('test');28}29import k6 from 'k6';30export default function () {31    k6.Exports('test');32}33import k6 from 'k6';34export default function () {35    k6.Exports('test');36}37import k6 from 'k6';38export default function () {39    k6.Exports('test');40}41import k6 from 'k6';42export default function () {43    k6.Exports('test');44}45import k6 from 'k6';46export default function () {47    k6.Exports('test');48}49import k6 from 'kExports
Using AI Code Generation
1func main() {2    js.Global().Call("k6").Call("Exports", map[string]interface{}{3        "setup": func() {4            fmt.Println("Setup")5        },6        "teardown": func() {7            fmt.Println("Teardown")8        },9        "default": func() {10            fmt.Println("Default")11        },12    })13}14export default function() {15    console.log("Default")16}17export function setup() {18    console.log("Setup")19}20export function teardown() {21    console.log("Teardown")22}23export default function() {24    console.log("Default")25}26export function setup() {27    console.log("Setup")28}29export function teardown() {30    console.log("Teardown")31}32export default function() {33    console.log("Default")34}35export function setup() {36    console.log("Setup")37}38export function teardown() {39    console.log("Teardown")40}41export default function() {42    console.log("Default")43}44export function setup() {45    console.log("Setup")46}47export function teardown() {48    console.log("Teardown")49}50export default function() {51    console.log("Default")52}53export function setup() {54    console.log("Setup")55}56export function teardown() {57    console.log("Teardown")58}59export default function() {60    console.log("Default")61}62export function setup() {63    console.log("Setup")64}65export function teardown() {66    console.log("Teardown")67}68export default function() {69    console.log("Default")70}71export function setup() {72    console.log("Setup")73}74export function teardown() {75    console.log("Teardown")76}77export default function() {78    console.log("Default")79}80export function setup() {81    console.log("Setup")82}83export function teardown() {84    console.log("Teardown")85}86export default function() {87    console.log("Default")88}89export function setup() {90    console.log("Setup")91}92export function teardown() {93    console.log("Teardown")94}95export default function() {96    console.log("Default")97}98export function setup() {Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
