Skip to content

Commit

Permalink
Disable async function as group and check callback
Browse files Browse the repository at this point in the history
The general inconsistencies between group as an idea and
asynchronous code makes it not great fit and such we will try to
discourage users to mix them.

The same is done to check for consistency and as otherwise an async
function will always give back true as it returns a promise which is
cast to boolean.

Updates #2728
  • Loading branch information
mstoykov committed Jan 26, 2023
1 parent 1d99b0b commit 5b483aa
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 3 deletions.
27 changes: 24 additions & 3 deletions js/modules/k6/k6.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package k6

import (
"errors"
"fmt"
"math/rand"
"sync/atomic"
"time"
Expand All @@ -22,6 +23,9 @@ var (
ErrCheckInInitContext = common.NewInitContextError("Using check() in the init context is not supported")
)

const asyncFunctionNotSupportedMsg = "%s: async functions are not supported as arguments, " +
"please see https://github.com/grafana/k6/issues/2728#issuecomment-1404747660 for more info"

type (
// RootModule is the global module instance that will create module
// instances for each VU.
Expand Down Expand Up @@ -85,16 +89,24 @@ func (mi *K6) RandomSeed(seed int64) {
}

// Group wraps a function call and executes it within the provided group name.
func (mi *K6) Group(name string, fn goja.Callable) (goja.Value, error) {
func (mi *K6) Group(name string, val goja.Value) (goja.Value, error) {
state := mi.vu.State()
if state == nil {
return nil, ErrGroupInInitContext
}

if fn == nil {
if val == nil || goja.IsNull(val) {
return nil, errors.New("group() requires a callback as a second argument")
}

fn, ok := goja.AssertFunction(val)
if !ok {
return nil, errors.New("group() requires a callback as a second argument")
}
rt := mi.vu.Runtime()
o := val.ToObject(rt)
if o.ClassName() == "AsyncFunction" {
return goja.Undefined(), fmt.Errorf(asyncFunctionNotSupportedMsg, "group")
}
g, err := state.Group.Group(name)
if err != nil {
return goja.Undefined(), err
Expand Down Expand Up @@ -137,7 +149,12 @@ func (mi *K6) Group(name string, fn goja.Callable) (goja.Value, error) {
return ret, err
}

func isNullish(val goja.Value) bool {
return val == nil || goja.IsNull(val) || goja.IsUndefined(val)
}

// Check will emit check metrics for the provided checks.
//
//nolint:cyclop
func (mi *K6) Check(arg0, checks goja.Value, extras ...goja.Value) (bool, error) {
state := mi.vu.State()
Expand Down Expand Up @@ -176,6 +193,10 @@ func (mi *K6) Check(arg0, checks goja.Value, extras ...goja.Value) (bool, error)
tags = tags.With("check", check.Name)
}

if !isNullish(val) && val.ToObject(rt).ClassName() == "AsyncFunction" {
return false, fmt.Errorf(asyncFunctionNotSupportedMsg, "check")
}

// Resolve callables into values.
fn, ok := goja.AssertFunction(val)
if ok {
Expand Down
28 changes: 28 additions & 0 deletions js/modules/k6/k6_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,20 @@ func TestGroup(t *testing.T) {
_, err := rt.RunString(`k6.group("::", function() { throw new Error("nooo") })`)
assert.Contains(t, err.Error(), "group and check names may not contain '::'")
})

t.Run("async function", func(t *testing.T) {
t.Parallel()
rt, _, _ := setupGroupTest()
_, err := rt.RunString(`k6.group("something", async function() { })`)
assert.ErrorContains(t, err, "group: async functions are not supported as arguments")
})

t.Run("async lambda", func(t *testing.T) {
t.Parallel()
rt, _, _ := setupGroupTest()
_, err := rt.RunString(`k6.group("something", async () => { })`)
assert.ErrorContains(t, err, "group: async functions are not supported as arguments")
})
}

func checkTestRuntime(t testing.TB) (*goja.Runtime, chan metrics.SampleContainer, *metrics.BuiltinMetrics) {
Expand Down Expand Up @@ -271,6 +285,20 @@ func TestCheckObject(t *testing.T) {
_, err := rt.RunString(`k6.check(null, { "::": true })`)
assert.Contains(t, err.Error(), "group and check names may not contain '::'")
})

t.Run("async function", func(t *testing.T) {
t.Parallel()
rt, _, _ := checkTestRuntime(t)
_, err := rt.RunString(`k6.check("something", {"async": async function() { }})`)
assert.ErrorContains(t, err, "check: async functions are not supported as arguments")
})

t.Run("async lambda", func(t *testing.T) {
t.Parallel()
rt, _, _ := checkTestRuntime(t)
_, err := rt.RunString(`k6.check("something", {"async": async () =>{ }})`)
assert.ErrorContains(t, err, "check: async functions are not supported as arguments")
})
}

func TestCheckArray(t *testing.T) {
Expand Down

0 comments on commit 5b483aa

Please sign in to comment.