How to use Exports method of k6 Package

Best K6 code snippet using k6.Exports

runner_test.go

Source:runner_test.go Github

copy

Full Screen

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}...

Full Screen

Full Screen

initcontext_test.go

Source:initcontext_test.go Github

copy

Full Screen

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}...

Full Screen

Full Screen

initcontext.go

Source:initcontext.go Github

copy

Full Screen

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}...

Full Screen

Full Screen

Exports

Using AI Code Generation

copy

Full Screen

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 () {

Full Screen

Full Screen

Exports

Using AI Code Generation

copy

Full Screen

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.js

Full Screen

Full Screen

Exports

Using AI Code Generation

copy

Full Screen

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() {

Full Screen

Full Screen

Exports

Using AI Code Generation

copy

Full Screen

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}

Full Screen

Full Screen

Exports

Using AI Code Generation

copy

Full Screen

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 (

Full Screen

Full Screen

Exports

Using AI Code Generation

copy

Full Screen

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) MakeRequest

Full Screen

Full Screen

Exports

Using AI Code Generation

copy

Full Screen

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 'k

Full Screen

Full Screen

Exports

Using AI Code Generation

copy

Full Screen

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() {

Full Screen

Full Screen

Automation Testing Tutorials

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.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful