From 508612563148416dcb1ac76079b60b42a875b479 Mon Sep 17 00:00:00 2001 From: Jay Pipes Date: Mon, 19 Oct 2026 15:17:17 -0400 Subject: [PATCH] add api.ExpectContains struct Adds a new common `api.ExpectContains` struct that replaces the `plugin/exec.PipeExpect` struct and can be used in other gdt plugins. For example, the kube plugin has been modified to use this new `ExpectContains` struct instead of a plain string field for its `Expect.Error` field. Also updates the documentation and parsing/tests for the `require` object compared to the `assert` object. Signed-off-by: Jay Pipes --- .github/workflows/fmtcheck.yml | 19 ++++-- .github/workflows/lint.yml | 23 ++++--- .github/workflows/test.yml | 22 ++++--- README.md | 43 ++++++------- api/error.go | 19 ++++++ api/expect.go | 88 ++++++++++++++++++++++++++ api/flexstrings.go | 19 ++++++ go.mod | 7 +- go.sum | 18 +++--- plugin/exec/assertions.go | 33 +++------- plugin/exec/parse.go | 54 +--------------- plugin/exec/testdata/stop-on-fail.yaml | 3 +- 12 files changed, 214 insertions(+), 134 deletions(-) create mode 100644 api/expect.go diff --git a/.github/workflows/fmtcheck.yml b/.github/workflows/fmtcheck.yml index 11cafcb..a03c016 100644 --- a/.github/workflows/fmtcheck.yml +++ b/.github/workflows/fmtcheck.yml @@ -27,11 +27,18 @@ jobs: objects.githubusercontent.com:443 proxy.golang.org:443 blob.core.windows.net:443 - - name: checkout code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - name: setup go - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 - with: - go-version: 1.21 + + - name: checkout-code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + + - name: setup-go + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version-file: 'go.mod' + check-latest: true + cache: true + - name: check fmt run: 'bash -c "diff -u <(echo -n) <(gofmt -d .)"' diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 470f56e..9c318af 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -19,15 +19,22 @@ jobs: with: egress-policy: audit disable-sudo: true - - name: checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: setup go - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 - with: - go-version: 1.24 + + - name: checkout-code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + + - name: setup-go + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version-file: 'go.mod' + check-latest: true + cache: true + - name: lint - uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0 + uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0 with: - version: v2.2.0 + version: v2.15.0 args: --timeout=5m0s --verbose only-new-issues: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8d49011..b6d635b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,7 @@ jobs: test: strategy: matrix: - go: ['1.21', '1.22', '1.23'] + go: ['1.23', '1.24', '1.25'] os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: @@ -30,11 +30,17 @@ jobs: objects.githubusercontent.com:443 proxy.golang.org:443 blob.core.windows.net:443 - - name: checkout code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - name: setup go - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 - with: - go-version: ${{ matrix.go }} - check-latest: true + + - name: checkout-code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + + - name: setup-go + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version: ${{ matrix.go }} + check-latest: true + cache: true + - run: go test -timeout 10s -v ./... diff --git a/README.md b/README.md index 051e278..5ab83db 100644 --- a/README.md +++ b/README.md @@ -153,38 +153,31 @@ test spec also contains these fields: `stderr` and `returncode` refer to the corresponding stdout, stderr and return/exitcode values. All other string values for `var.from` indicate the name of the environment variable to read into the named variable. -* `assert`: (optional) an object describing the conditions that will be - asserted about the test action. -* `assert.require`: (optional) a boolean indicating whether a failed assertion - will cause the test scenario's execution to stop. The default behaviour of - `gdt` is to continue execution of subsequent test specs in a test scenario when - an assertion fails. +* `assert`: (optional) object containing assertions to make about the action + performed by the test. Failed assertions will not cause the test scenario's + execution to stop. To have failed assertions stop the test scenario's + execution, use the `require` object. * `assert.exit-code`: (optional) an integer with the expected exit code from the executed command. The default successful exit code is 0 and therefore you do not need to specify this if you expect a successful exit code. -* `assert.out`: (optional) a [`PipeExpect`][pipeexpect] object containing +* `assert.out`: (optional) an [`ExpectContains`][expectcontains] object containing assertions about content in `stdout`. -* `assert.out.is`: (optional) a string with the exact contents of `stdout` you expect - to get. -* `assert.out.all`: (optional) a string or list of strings that *all* must be - present in `stdout`. -* `assert.out.any`: (optional) a string or list of strings of which *at - least one* must be present in `stdout`. -* `assert.out.none`: (optional) a string or list of strings of which *none - should be present* in `stdout`. -* `assert.err`: (optional) a [`PipeAssertions`][pipeexpect] object containing +* `assert.err`: (optional) an [`ExpectContains`][expectcontains] object containing + assertions about content in `stderr`. +* `require`: (optional) object containing assertions to make about the action + performed by the test. Failed assertions will cause the test scenario's + execution to stop. To have failed assertions not stop the test scenario's + execution, use the `assert` object. +* `require.exit-code`: (optional) an integer with the expected exit code from the + executed command. The default successful exit code is 0 and therefore you do + not need to specify this if you expect a successful exit code. +* `require.out`: (optional) an [`ExpectContains`][expectcontains] object containing + assertions about content in `stdout`. +* `require.err`: (optional) an [`ExpectContains`][expectcontains] object containing assertions about content in `stderr`. -* `assert.err.is`: (optional) a string with the exact contents of `stderr` you expect - to get. -* `assert.err.all`: (optional) a string or list of strings that *all* must be - present in `stderr`. -* `assert.err.any`: (optional) a string or list of strings of which *at - least one* must be present in `stderr`. -* `assert.err.none`: (optional) a string or list of strings of which *none - should be present* in `stderr`. [execspec]: https://github.com/gdt-dev/core/blob/2791e11105fd3c36d1f11a7d111e089be7cdc84c/exec/spec.go#L11-L34 -[pipeexpect]: https://github.com/gdt-dev/core/blob/2791e11105fd3c36d1f11a7d111e089be7cdc84c/exec/assertions.go#L15-L26 +[expectcontains]: https://github.com/gdt-dev/core/blob/main/api/expect.go ### Marking a program or package dependency for a test scenario diff --git a/api/error.go b/api/error.go index 5ff9444..f8479a8 100644 --- a/api/error.go +++ b/api/error.go @@ -36,6 +36,8 @@ var ( // ErrUnexpectedError is an ErrFailure when an unexpected error has // occurred. ErrUnexpectedError = fmt.Errorf("%w: unexpected error", ErrFailure) + // ErrExpectedError is an ErrFailure when an expected error did not occur. + ErrExpectedError = fmt.Errorf("%w: unexpected error", ErrFailure) ) // TimeoutExceeded returns an ErrTimeoutExceeded when a test's execution @@ -98,6 +100,23 @@ func UnexpectedError(err error) error { return fmt.Errorf("%w: %s", ErrUnexpectedError, err) } +// ExpectedError returns an ErrExpectedError when the supplied error did not +// occur or the supplied ExpectContains did not match. +func ExpectedError(subject any) error { + switch subject := subject.(type) { + case error: + return fmt.Errorf("%w: %s", ErrExpectedError, subject) + case ExpectContains: + expectMatch := subject.String() + return fmt.Errorf("%w: %s", ErrExpectedError, expectMatch) + default: + msg := fmt.Sprintf( + "unhandled subject type %T supplied to ExpectedError", subject, + ) + panic(msg) + } +} + var ( // ErrUnknownSourceType indicates that a From() function was called with an // unknown source parameter type. diff --git a/api/expect.go b/api/expect.go new file mode 100644 index 0000000..16ab57f --- /dev/null +++ b/api/expect.go @@ -0,0 +1,88 @@ +package api + +import ( + "fmt" + "strings" + + "gopkg.in/yaml.v3" + + "github.com/gdt-dev/core/parse" +) + +// ExpectContains is a common struct for assertions about strings or +// collections of strings. +type ExpectContains struct { + // ContainsAll is one or more strings that *all* must be present in the + // subject contents + ContainsAll *FlexStrings `yaml:"contains,omitempty"` + // ContainsNone is one or more strings, *none of which* should be present in + // the subject contents + ContainsNone *FlexStrings `yaml:"contains-none-of,omitempty"` + // ContainsOneOf is one or more strings of which *at least one* must be + // present in the subject contents + ContainsAny *FlexStrings `yaml:"contains-one-of,omitempty"` +} + +// String returns a string representation of the ExpectContains' matching +// conditions. +func (e ExpectContains) String() string { + parts := []string{} + if e.ContainsAll != nil { + parts = append(parts, fmt.Sprintf("contains-all: %s", e.ContainsAll)) + } + if e.ContainsNone != nil { + parts = append(parts, fmt.Sprintf("contains-none: %s", e.ContainsNone)) + } + if e.ContainsAny != nil { + parts = append(parts, fmt.Sprintf("contains-any: %s", e.ContainsAny)) + } + return strings.Join(parts, " ") +} + +func (e *ExpectContains) UnmarshalYAML(node *yaml.Node) error { + if node.Kind != yaml.MappingNode { + return parse.ExpectedMapAt(node) + } + // maps/structs are stored in a top-level Node.Content field which is a + // concatenated slice of Node pointers in pairs of key/values. + for i := 0; i < len(node.Content); i += 2 { + keyNode := node.Content[i] + if keyNode.Kind != yaml.ScalarNode { + return parse.ExpectedScalarAt(keyNode) + } + key := keyNode.Value + valNode := node.Content[i+1] + switch key { + case "all", "is", "contains", "contains-all", "contains_all": + if valNode.Kind != yaml.ScalarNode && valNode.Kind != yaml.SequenceNode { + return parse.ExpectedScalarOrSequenceAt(valNode) + } + var v FlexStrings + if err := valNode.Decode(&v); err != nil { + return err + } + e.ContainsAll = &v + case "any", "contains-one-of", "contains-any", "contains_one_of", "contains_any": + if valNode.Kind != yaml.ScalarNode && valNode.Kind != yaml.SequenceNode { + return parse.ExpectedScalarOrSequenceAt(valNode) + } + var v FlexStrings + if err := valNode.Decode(&v); err != nil { + return err + } + e.ContainsAny = &v + case "none", "none-of", "contains-none-of", "contains-none", "none_of", "contains_none_of", "contains_none": + if valNode.Kind != yaml.ScalarNode && valNode.Kind != yaml.SequenceNode { + return parse.ExpectedScalarOrSequenceAt(valNode) + } + var v FlexStrings + if err := valNode.Decode(&v); err != nil { + return err + } + e.ContainsNone = &v + default: + return parse.UnknownFieldAt(key, keyNode) + } + } + return nil +} diff --git a/api/flexstrings.go b/api/flexstrings.go index 20b45c0..9c63dea 100644 --- a/api/flexstrings.go +++ b/api/flexstrings.go @@ -5,6 +5,8 @@ package api import ( + "strings" + "gopkg.in/yaml.v3" "github.com/gdt-dev/core/parse" @@ -21,6 +23,23 @@ func (f *FlexStrings) Values() []string { return f.values } +// NewFlexStrings returns a new FlexStrings from zero or more string args. +func NewFlexStrings(args ...string) *FlexStrings { + return &FlexStrings{values: args} +} + +// String returns a string representation of the FlexStrings +func (f FlexStrings) String() string { + switch len(f.values) { + case 0: + return "" + case 1: + return f.values[0] + default: + return "[" + strings.Join(f.values, ",") + "]" + } +} + // UnmarshalYAML is a custom unmarshaler that understands that the value of the // FlexStrings can be either a string or a slice of strings. func (f *FlexStrings) UnmarshalYAML(node *yaml.Node) error { diff --git a/go.mod b/go.mod index edb4793..2c55bbb 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/cenkalti/backoff v2.2.1+incompatible github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/uuid v1.6.0 - github.com/samber/lo v1.51.0 + github.com/samber/lo v1.52.0 github.com/stretchr/testify v1.11.1 github.com/theory/jsonpath v0.10.1 github.com/xeipuuv/gojsonschema v1.2.0 @@ -16,9 +16,10 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/kr/text v0.2.0 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - golang.org/x/text v0.22.0 // indirect + golang.org/x/text v0.23.0 // indirect ) diff --git a/go.sum b/go.sum index 6bc3590..ba65fa4 100644 --- a/go.sum +++ b/go.sum @@ -10,16 +10,18 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI= -github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw= +github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= @@ -32,8 +34,8 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/plugin/exec/assertions.go b/plugin/exec/assertions.go index f66ee98..e7239ca 100644 --- a/plugin/exec/assertions.go +++ b/plugin/exec/assertions.go @@ -20,33 +20,20 @@ import ( type Expect struct { // Require indicates that any failed assertion should stop the execution of // the test scenario in which the test spec is contained. - Require bool `yaml:"require,omitempty"` + Require bool `yaml:"-"` // ExitCode is the expected exit code for the executed command. The default // (0) is the universal successful exit code, so you only need to set this // if you expect a non-successful result from executing the command. ExitCode int `yaml:"exit-code,omitempty"` // Out has things that are expected in the stdout response - Out *PipeExpect `yaml:"out,omitempty"` + Out *api.ExpectContains `yaml:"out,omitempty"` // Err has things that are expected in the stderr response - Err *PipeExpect `yaml:"err,omitempty"` -} - -// PipeExpect contains assertions about the contents of a pipe -type PipeExpect struct { - // ContainsAll is one or more strings that *all* must be present in the - // contents of the pipe - ContainsAll *api.FlexStrings `yaml:"contains,omitempty"` - // ContainsNone is one or more strings, *none of which* should be present in - // the contents of the pipe - ContainsNone *api.FlexStrings `yaml:"contains-none-of,omitempty"` - // ContainsOneOf is one or more strings of which *at least one* must be - // present in the contents of the pipe - ContainsAny *api.FlexStrings `yaml:"contains-one-of,omitempty"` + Err *api.ExpectContains `yaml:"err,omitempty"` } // pipeAssertions contains assertions about the contents of a pipe type pipeAssertions struct { - PipeExpect + api.ExpectContains // pipe is the contents of the pipe that we will evaluate. pipe *bytes.Buffer // name is the string name of the pipe. @@ -214,16 +201,16 @@ func newAssertions( if e != nil { if e.Out != nil { a.expOutPipe = &pipeAssertions{ - PipeExpect: *e.Out, - name: "stdout", - pipe: outPipe, + ExpectContains: *e.Out, + name: "stdout", + pipe: outPipe, } } if e.Err != nil { a.expErrPipe = &pipeAssertions{ - PipeExpect: *e.Err, - name: "stderr", - pipe: errPipe, + ExpectContains: *e.Err, + name: "stderr", + pipe: errPipe, } } } diff --git a/plugin/exec/parse.go b/plugin/exec/parse.go index 48075b5..136bf30 100644 --- a/plugin/exec/parse.go +++ b/plugin/exec/parse.go @@ -178,7 +178,7 @@ func (e *Expect) UnmarshalYAML(node *yaml.Node) error { key := keyNode.Value valNode := node.Content[i+1] switch key { - case "require", "stop-on-fail", "stop_on_fail", "stop.on.fail", + case "stop-on-fail", "stop_on_fail", "stop.on.fail", "fail-stop", "fail.stop", "fail_stop": if valNode.Kind != yaml.ScalarNode { return parse.ExpectedScalarAt(valNode) @@ -201,7 +201,7 @@ func (e *Expect) UnmarshalYAML(node *yaml.Node) error { if valNode.Kind != yaml.MappingNode { return parse.ExpectedMapAt(valNode) } - var pe *PipeExpect + var pe *api.ExpectContains if err := valNode.Decode(&pe); err != nil { return err } @@ -210,7 +210,7 @@ func (e *Expect) UnmarshalYAML(node *yaml.Node) error { if valNode.Kind != yaml.MappingNode { return parse.ExpectedMapAt(valNode) } - var pe *PipeExpect + var pe *api.ExpectContains if err := valNode.Decode(&pe); err != nil { return err } @@ -221,51 +221,3 @@ func (e *Expect) UnmarshalYAML(node *yaml.Node) error { } return nil } - -func (e *PipeExpect) UnmarshalYAML(node *yaml.Node) error { - if node.Kind != yaml.MappingNode { - return parse.ExpectedMapAt(node) - } - // maps/structs are stored in a top-level Node.Content field which is a - // concatenated slice of Node pointers in pairs of key/values. - for i := 0; i < len(node.Content); i += 2 { - keyNode := node.Content[i] - if keyNode.Kind != yaml.ScalarNode { - return parse.ExpectedScalarAt(keyNode) - } - key := keyNode.Value - valNode := node.Content[i+1] - switch key { - case "all", "is", "contains", "contains-all", "contains_all": - if valNode.Kind != yaml.ScalarNode && valNode.Kind != yaml.SequenceNode { - return parse.ExpectedScalarOrSequenceAt(valNode) - } - var v api.FlexStrings - if err := valNode.Decode(&v); err != nil { - return err - } - e.ContainsAll = &v - case "any", "contains-one-of", "contains-any", "contains_one_of", "contains_any": - if valNode.Kind != yaml.ScalarNode && valNode.Kind != yaml.SequenceNode { - return parse.ExpectedScalarOrSequenceAt(valNode) - } - var v api.FlexStrings - if err := valNode.Decode(&v); err != nil { - return err - } - e.ContainsAny = &v - case "none", "none-of", "contains-none-of", "contains-none", "none_of", "contains_none_of", "contains_none": - if valNode.Kind != yaml.ScalarNode && valNode.Kind != yaml.SequenceNode { - return parse.ExpectedScalarOrSequenceAt(valNode) - } - var v api.FlexStrings - if err := valNode.Decode(&v); err != nil { - return err - } - e.ContainsNone = &v - default: - return parse.UnknownFieldAt(key, keyNode) - } - } - return nil -} diff --git a/plugin/exec/testdata/stop-on-fail.yaml b/plugin/exec/testdata/stop-on-fail.yaml index dadb564..7fa8168 100644 --- a/plugin/exec/testdata/stop-on-fail.yaml +++ b/plugin/exec/testdata/stop-on-fail.yaml @@ -9,8 +9,7 @@ tests: is: 1234 # This test should be executed because we do not use the require: true. - exec: echo 42 - assert: - require: true + require: out: is: 24 # This test should not be executed because of the use of require: true above