Skip to content

Commit

Permalink
feat(k6):Add remote test scripts (#2350)
Browse files Browse the repository at this point in the history
* Add remote test scripts

* Add basic auth

* Simplify error handling

* Use container reader API

* Use Typed URI + linting/formatting

* Add failing remote test + update docs/comments

* Rename struct + Docs

* Fix doc typo

---------

Co-authored-by: bstrausser <[email protected]>
  • Loading branch information
bearrito and bstrausser authored Apr 3, 2024
1 parent 697c264 commit 55ca42a
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 9 deletions.
16 changes: 15 additions & 1 deletion docs/modules/k6.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,26 @@ k6.RunContainer(ctx, WithCmdOptions("--vus=10", "--duration=30s"), k6.WithTestSc

#### WithTestScript

Use the `WithTestScript` option to specify the test script to run. The path to the script must be an absolute path. This option copies the script file to the container and pass it to k6's `run` command. At least one `WithTestScript` option must be specified.
Use the `WithTestScript` option to specify the test script to run. The path to the script must be an absolute path. This option copies the script file to the container and pass it to k6's `run` command. At least one `WithTestScript` or `WithRemoteTestScript` option must be specified.
```golang
k6.RunContainer(ctx, k6.WithTestScript("/tests/test.js"))
```
#### WithRemoteTestScript
Use the `WithRemoteTestScript` option to specify the remote test script to run. The path to the remote script must be a http or https url. Basic authentication is supported. This option performs a HTTP `GET` to copy the remote file locally then copies the script file to the container and pass it to k6's `run` command. The default timeout for the `GET` is 60 seconds. Only javascript, or more specifically `Content-Type:text/javascript` is supported. At least one `WithTestScript` or `WithRemoteTestScript` option must be specified.

```golang
scriptUrl:="https://raw.githubusercontent.com/testcontainers/testcontainers-go/main/modules/k6/scripts/pass.js"
uri, _ := url.Parse(scriptUrl)
desc := k6.DownloadableFile{Uri: *uri , DownloadDir: t.TempDir()}
options := k6.WithRemoteTestScript(desc)
k6.RunContainer(ctx, k6.WithCache(), options)
```

### Container Methods

The K6 container does not expose any method.
81 changes: 77 additions & 4 deletions modules/k6/k6.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ package k6
import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path"
"path/filepath"
"time"

"github.com/docker/docker/api/types/mount"

Expand All @@ -17,17 +22,73 @@ type K6Container struct {
testcontainers.Container
}

type DownloadableFile struct {
Uri url.URL
DownloadDir string
User string
Password string
}

func (d *DownloadableFile) getDownloadPath() string {
baseName := path.Base(d.Uri.Path)
return path.Join(d.DownloadDir, baseName)

}

func downloadFileFromDescription(d DownloadableFile) error {

client := http.Client{Timeout: time.Second * 60}
req, err := http.NewRequest(http.MethodGet, d.Uri.String(), nil)
if err != nil {
return err
}

req.Header.Set("Content-Type", "text/javascript")
// Set up HTTPS request with basic authorization.
if d.User != "" && d.Password != "" {
req.SetBasicAuth(d.User, d.Password)
}

resp, err := client.Do(req)
if err != nil {
return err
}

downloadedFile, err := os.Create(d.getDownloadPath())
if err != nil {
return err
}
defer downloadedFile.Close()

_, err = io.Copy(downloadedFile, resp.Body)
return err

}

// WithTestScript mounts the given script into the ./test directory in the container
// and passes it to k6 as the test to run.
// The path to the script must be an absolute path
func WithTestScript(scriptPath string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
script := filepath.Base(scriptPath)
target := "/home/k6x/" + script

scriptBaseName := filepath.Base(scriptPath)
f, err := os.Open(scriptPath)
if err != nil {
panic("Cannot create reader for test file ")
}
return WithTestScriptReader(f, scriptBaseName)

}

// WithTestScriptReader copies files into the Container using the Reader API
// The script base name is not a path, neither absolute or relative and should
// be just the file name of the script
func WithTestScriptReader(reader io.Reader, scriptBaseName string) testcontainers.CustomizeRequestOption {
opt := func(req *testcontainers.GenericContainerRequest) {
target := "/home/k6x/" + scriptBaseName
req.Files = append(
req.Files,
testcontainers.ContainerFile{
HostFilePath: scriptPath,
Reader: reader,
ContainerFilePath: target,
FileMode: 0o644,
},
Expand All @@ -36,6 +97,18 @@ func WithTestScript(scriptPath string) testcontainers.CustomizeRequestOption {
// add script to the k6 run command
req.Cmd = append(req.Cmd, target)
}
return opt
}

// WithRemoteTestScript takes a RemoteTestFileDescription and copies to container
func WithRemoteTestScript(d DownloadableFile) testcontainers.CustomizeRequestOption {

err := downloadFileFromDescription(d)
if err != nil {
panic("Not able to download required test script")
}

return WithTestScript(d.getDownloadPath())
}

// WithCmdOptions pass the given options to the k6 run command
Expand Down
34 changes: 30 additions & 4 deletions modules/k6/k6_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package k6_test

import (
"context"
"net/url"
"path/filepath"
"strings"
"testing"

"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/k6"
)

Expand All @@ -24,19 +27,42 @@ func TestK6(t *testing.T) {
script: "fail.js",
expect: 108,
},
{
title: "Passing remote test",
script: "https://raw.githubusercontent.com/testcontainers/testcontainers-go/main/modules/k6/scripts/pass.js",
expect: 0,
},
{
title: "Failing remote test",
script: "https://raw.githubusercontent.com/testcontainers/testcontainers-go/main/modules/k6/scripts/fail.js",
expect: 108,
},
}

for _, tc := range testCases {
tc := tc
t.Run(tc.title, func(t *testing.T) {
ctx := context.Background()

absPath, err := filepath.Abs(filepath.Join("scripts", tc.script))
if err != nil {
t.Fatal(err)
var options testcontainers.CustomizeRequestOption
if !strings.HasPrefix(tc.script, "http") {
absPath, err := filepath.Abs(filepath.Join("scripts", tc.script))
if err != nil {
t.Fatal(err)
}
options = k6.WithTestScript(absPath)
} else {

uri, err := url.Parse(tc.script)
if err != nil {
t.Fatal(err)
}

desc := k6.DownloadableFile{Uri: *uri, DownloadDir: t.TempDir()}
options = k6.WithRemoteTestScript(desc)
}

container, err := k6.RunContainer(ctx, k6.WithCache(), k6.WithTestScript(absPath))
container, err := k6.RunContainer(ctx, k6.WithCache(), options)
if err != nil {
t.Fatal(err)
}
Expand Down

0 comments on commit 55ca42a

Please sign in to comment.