Skip to content

Commit

Permalink
add secrets field to define credential variables (#414)
Browse files Browse the repository at this point in the history
* feat(assert): double-quote strings in error messages

* feat: add secrets field to define credential variables

* feat(plugin): drop toolchain directive

* chore(tool): bump golangci-lint to v1.57.2

* chore(deps): bump indirect module versions
  • Loading branch information
zoncoen committed Apr 25, 2024
1 parent 2ecf8f1 commit 2ca90ab
Show file tree
Hide file tree
Showing 78 changed files with 1,685 additions and 283 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ $(GOCREDITS): | $(BIN_DIR)
@$(GO) install github.com/Songmu/gocredits/cmd/[email protected]

GOLANGCI_LINT := $(BIN_DIR)/golangci-lint
GOLANGCI_LINT_VERSION := 1.52.2
GOLANGCI_LINT_VERSION := 1.57.2
GOLANGCI_LINT_OS_ARCH := $(shell echo golangci-lint-$(GOLANGCI_LINT_VERSION)-$(GOOS)-$(GOARCH))
GOLANGCI_LINT_GZIP := $(GOLANGCI_LINT_OS_ARCH).tar.gz
$(GOLANGCI_LINT): | $(BIN_DIR)
Expand Down
83 changes: 83 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,89 @@ steps:
text: '{{request.body.text}}'
```

You can also define global variables in the `scenarigo.yaml`. The defined variables can be used from all test scenarios.

```yaml
schemaVersion: config/v1
vars:
name: zoncoen
```

### Secrets

The `secrets` field allows defining variables like the `vars` field. Besides, the values defined by the `secrets` field are masked in the outputs.

```yaml
schemaVersion: scenario/v1
plugins:
plugin: plugin.so
vars:
clientId: abcdef
secrets:
clientSecret: XXXXX
title: get user profile
steps:
- title: get access token
protocol: http
request:
method: POST
url: 'http://example.com/oauth/token'
header:
Content-Type: application/x-www-form-urlencoded
body:
grant_type: client_credentials
client_id: '{{vars.clientId}}'
client_secret: '{{secrets.clientSecret}}'
expect:
code: OK
body:
access_token: '{{$ != ""}}'
token_type: Bearer
bind:
secrets:
accessToken: '{{response.body.access_token}}'
- title: get user profile
protocol: http
request:
method: GET
url: 'http://example.com/users/zoncoen'
header:
Authorization: 'Bearer {{secrets.accessToken}}'
expect:
code: OK
body:
name: zoncoen
```

```shell
...
--- PASS: scenarios/get-profile.yaml/get_user_profile/get_access_token (0.00s)
request:
method: POST
url: http://example.com/oauth/token
header:
Content-Type:
- application/x-www-form-urlencoded
body:
client_id: abcdef
client_secret: {{secrets.clientSecret}}
grant_type: client_credentials
response:
...
body:
access_token: {{secrets.accessToken}}
token_type: Bearer
elapsed time: 0.001743 sec
--- PASS: scenarios/get-profile.yaml/get_user_profile/get_user_profile (0.00s)
request:
method: GET
url: http://example.com/users/zoncoen
header:
Authorization:
- Bearer {{secrets.accessToken}}
...
```

### Timeout/Retry

You can set timeout and retry policy for each step.
Expand Down
2 changes: 1 addition & 1 deletion assert/assertion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ deps:
}
if err := assertion.Assert("foo"); err == nil {
t.Error("no error")
} else if got, expect := err.Error(), ".'{{call <-}}': expected FOO but got foo"; got != expect {
} else if got, expect := err.Error(), `.'{{call <-}}': expected "FOO" but got "foo"`; got != expect {
t.Errorf("expect %q but got %q", expect, got)
}
})
Expand Down
2 changes: 2 additions & 0 deletions assert/equal.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ func Equal(expected interface{}, customEqs ...Equaler) Assertion {
}
}
return errors.Errorf("expected %T (%+v) but got %T (%+v)", expected, expected, v, v)
} else if t.Kind() == reflect.String {
return errors.Errorf("expected %q but got %q", expected, v)
}
return errors.Errorf("expected %+v but got %+v", expected, v)
})
Expand Down
18 changes: 10 additions & 8 deletions assert/operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func TestAnd(t *testing.T) {
},
ok: "one",
ng: "1",
expectError: "[0]: expected one but got 1",
expectError: `[0]: expected "one" but got "1"`,
},
"two": {
assertions: []Assertion{
Expand All @@ -35,16 +35,17 @@ func TestAnd(t *testing.T) {
},
ok: "one",
ng: "1",
expectError: "[0]: expected one but got 1",
expectError: `[0]: expected "one" but got "1"`,
},
"multi error": {
assertions: []Assertion{
Equal("one"),
Equal("un"),
NotZero(),
},
ng: "1",
expectError: "2 errors occurred: [0]: expected one but got 1\n[1]: expected un but got 1",
ng: "1",
expectError: `2 errors occurred: [0]: expected "one" but got "1"
[1]: expected "un" but got "1"`,
},
}
for _, test := range tests {
Expand Down Expand Up @@ -85,16 +86,17 @@ func TestOr(t *testing.T) {
},
ok: "two",
ng: "2",
expectError: "[0]: all assertions failed: expected two but got 2",
expectError: `[0]: all assertions failed: expected "two" but got "2"`,
},
"two": {
assertions: []Assertion{
Equal("one"),
Equal("two"),
},
ok: "two",
ng: "2",
expectError: "2 errors occurred: [0]: all assertions failed: expected one but got 2\n[1]: all assertions failed: expected two but got 2",
ok: "two",
ng: "2",
expectError: `2 errors occurred: [0]: all assertions failed: expected "one" but got "2"
[1]: all assertions failed: expected "two" but got "2"`,
},
}
for _, test := range tests {
Expand Down
7 changes: 7 additions & 0 deletions cmd/scenarigo/cmd/plugin/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,13 @@ func executeWithEnvs(ctx context.Context, envs []string, wd, name string, args .
}

func updateGoMod(cmd *cobra.Command, goCmd, name, gomodPath string, overrideKeys []string, overrides map[string]*overrideModule) error {
if err := editGoMod(cmd, goCmd, gomodPath, func(gomod *modfile.File) error {
gomod.DropToolchainStmt()
return nil
}); err != nil {
return fmt.Errorf("failed to edit toolchain directive: %w", err)
}

initialRequires, initialReplaces, err := updateRequireDirectives(cmd, goCmd, gomodPath, overrides)
if err != nil {
return err
Expand Down
17 changes: 8 additions & 9 deletions cmd/scenarigo/cmd/plugin/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1287,8 +1287,7 @@ replace github.com/zoncoen/scenarigo v0.11.2 => github.com/zoncoen/scenarigo v0.
go 1.21
`,
expectStdout: `WARN: test.so: remove require github.com/zoncoen/scenarigo v0.11.2
WARN: test.so: remove replace github.com/zoncoen/scenarigo v0.11.2 => github.com/zoncoen/scenarigo v0.11.0
expectStdout: `WARN: test.so: remove replace github.com/zoncoen/scenarigo v0.11.2 => github.com/zoncoen/scenarigo v0.11.0
`,
},
"add require": {
Expand Down Expand Up @@ -1320,15 +1319,15 @@ go 1.21
require google.golang.org/grpc v1.37.1
require (
github.com/golang/protobuf v1.4.2 // indirect
golang.org/x/net v0.0.0-20190311183353-d8887717615a // indirect
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a // indirect
golang.org/x/text v0.3.0 // indirect
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
google.golang.org/protobuf v1.25.0 // indirect
github.com/golang/protobuf v1.5.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/protobuf v1.33.0 // indirect
)
`,
expectStdout: `WARN: test.so: add require google.golang.org/grpc v1.37.1 by test
expectStdout: `WARN: test.so: change require google.golang.org/grpc v1.63.2 ==> v1.37.1 by test
`,
},
"overwrite require by require": {
Expand Down
19 changes: 6 additions & 13 deletions cmd/scenarigo/cmd/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@ package cmd
import (
"bytes"
"errors"
"fmt"
"io"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"

Expand All @@ -33,11 +31,6 @@ func TestRun(t *testing.T) {

t.Setenv("TEST_ADDR", srv.URL)

wd, err := os.Getwd()
if err != nil {
t.Fatalf("failed to get current directory: %s", err)
}

tests := map[string]struct {
args []string
config string
Expand Down Expand Up @@ -80,7 +73,7 @@ ok testdata/scenarios/pass.yaml 0.000s
body:
message: request
elapsed time: 0.000000 sec
expected response but got request
expected "response" but got "request"
12 | expect:
13 | code: 200
14 | body:
Expand All @@ -103,14 +96,14 @@ ok scenarios/pass.yaml 0.000s
config: "./testdata/scenarigo-plugin-not-found.yaml",
args: []string{"testdata/scenarios/pass.yaml"},
expectError: ErrTestFailed.Error(),
expectOutput: strings.TrimPrefix(fmt.Sprintf(`
expectOutput: strings.TrimPrefix(`
--- FAIL: setup (0.00s)
--- FAIL: setup/plugin.so (0.00s)
failed to open plugin: plugin.Open("%s"): realpath failed
failed to open plugin: plugin.Open("/go/src/github.com/zoncoen/scenarigo/cmd/testdata/plugin.so"): realpath failed
FAIL
FAIL setup 0.000s
FAIL
`, filepath.Join(wd, "testdata", "plugin.so")), "\n"),
`, "\n"),
},
"print summary": {
args: []string{},
Expand Down Expand Up @@ -141,7 +134,7 @@ FAIL
body:
message: request
elapsed time: 0.000000 sec
expected response but got request
expected "response" but got "request"
12 | expect:
13 | code: 200
14 | body:
Expand Down Expand Up @@ -188,7 +181,7 @@ Failed tests:
body:
message: request
elapsed time: 0.000000 sec
expected response but got request
expected "response" but got "request"
12 | expect:
13 | code: 200
14 | body:
Expand Down
28 changes: 28 additions & 0 deletions context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type (
keyPluginDir struct{}
keyPlugins struct{}
keyVars struct{}
keySecrets struct{}
keySteps struct{}
keyRequest struct{}
keyResponse struct{}
Expand Down Expand Up @@ -64,6 +65,9 @@ func (c *Context) RequestContext() context.Context {

// WithReporter returns a copy of c with new test reporter.
func (c *Context) WithReporter(r reporter.Reporter) *Context {
if s := c.Secrets(); s != nil {
reporter.SetLogReplacer(c.reporter, s)
}
return newContext(c.ctx, c.reqCtx, r)
}

Expand Down Expand Up @@ -158,6 +162,30 @@ func (c *Context) Vars() Vars {
return nil
}

// WithSecrets returns a copy of c with v.
func (c *Context) WithSecrets(s any) *Context {
if s == nil {
return c
}
secrets, _ := c.ctx.Value(keySecrets{}).(*Secrets)
secrets = secrets.Append(s)
reporter.SetLogReplacer(c.reporter, secrets)
return newContext(
context.WithValue(c.ctx, keySecrets{}, secrets),
c.reqCtx,
c.reporter,
)
}

// Secrets returns the context secrets.
func (c *Context) Secrets() *Secrets {
secrets, ok := c.ctx.Value(keySecrets{}).(*Secrets)
if ok {
return secrets
}
return nil
}

// WithSteps returns a copy of c with steps.
func (c *Context) WithSteps(steps *Steps) *Context {
if steps == nil {
Expand Down
6 changes: 6 additions & 0 deletions context/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const (
nameContext = "ctx"
namePlugins = "plugins"
nameVars = "vars"
nameSecrets = "secrets"
nameSteps = "steps"
nameRequest = "request"
nameResponse = "response"
Expand All @@ -26,6 +27,11 @@ func (c *Context) ExtractByKey(key string) (interface{}, bool) {
if v != nil {
return v, true
}
case nameSecrets:
v := c.Secrets()
if v != nil {
return v, true
}
case nameSteps:
v := c.Steps()
if v != nil {
Expand Down
7 changes: 7 additions & 0 deletions context/extract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ func TestContext_ExtractKey(t *testing.T) {
query: "vars.foo",
expect: "bar",
},
"secrets": {
ctx: func(ctx *Context) *Context {
return ctx.WithSecrets(vars)
},
query: "secrets.foo",
expect: "bar",
},
"steps": {
ctx: func(ctx *Context) *Context {
steps := NewSteps()
Expand Down
Loading

0 comments on commit 2ca90ab

Please sign in to comment.