diff --git a/README.md b/README.md index 3cbf1389..f4785083 100644 --- a/README.md +++ b/README.md @@ -638,6 +638,7 @@ Builtin variables: * ShouldHappenOnOrAfter - [example](https://github.com/ovh/venom/tree/master/tests/assertions/ShouldHappenOnOrAfter.yml) * ShouldHappenBetween - [example](https://github.com/ovh/venom/tree/master/tests/assertions/ShouldHappenBetween.yml) * ShouldTimeEqual - [example](https://github.com/ovh/venom/tree/master/tests/assertions/ShouldTimeEqual.yml) +* ShouldMatchRegex - [example](https://github.com/ovh/venom/tree/master/tests/assertions/ShouldMatchRegex.yml) #### `Must` keywords diff --git a/assertions/assertions.go b/assertions/assertions.go index 8bd5373a..10d46c96 100644 --- a/assertions/assertions.go +++ b/assertions/assertions.go @@ -5,6 +5,7 @@ import ( "fmt" "math" "reflect" + "regexp" "strings" "time" @@ -60,6 +61,7 @@ var assertMap = map[string]AssertFunc{ "ShouldTimeEqual": ShouldTimeEqual, "ShouldBeArray": ShouldBeArray, "ShouldBeMap": ShouldBeMap, + "ShouldMatchRegex": ShouldMatchRegex, } func Get(s string) (AssertFunc, bool) { @@ -100,14 +102,13 @@ func ShouldBeMap(actual interface{}, expected ...interface{}) error { // // Example of testsuite file: // -// name: Assertions testsuite -// testcases: -// - name: test assertion -// steps: -// - script: echo 'foo' -// assertions: -// - result.code ShouldEqual 0 -// +// name: Assertions testsuite +// testcases: +// - name: test assertion +// steps: +// - script: echo 'foo' +// assertions: +// - result.code ShouldEqual 0 func ShouldEqual(actual interface{}, expected ...interface{}) error { // if expected is an array, we consider that this array is an array of string // so, we concat all values before doing the comparison @@ -135,6 +136,29 @@ func ShouldEqual(actual interface{}, expected ...interface{}) error { return fmt.Errorf("expected: %v got: %v", expected[0], actual) } +// ShouldMatchRegex receives exactly two parameters and does a regex match check. +func ShouldMatchRegex(actual interface{}, expected ...interface{}) error { + if need(1, expected) != nil { + return fmt.Errorf("expected one regex pattern") + } + expectedAsString, regexNotString := cast.ToStringE(expected[0]) + if regexNotString != nil { + return fmt.Errorf("expected a string for regex pattern") + } + reg, err := regexp.Compile(expectedAsString) + if err != nil { + return err + } + actualAsString, castingErr := cast.ToStringE(actual) + if castingErr != nil { + return castingErr + } + if !reg.MatchString(actualAsString) { + return fmt.Errorf("value %v not matching pattern : %v", actual, expected[0]) + } + return nil +} + // ShouldNotEqual receives exactly two parameters and does an inequality check. func ShouldNotEqual(actual interface{}, expected ...interface{}) error { if err := ShouldEqual(actual, expected...); err == nil { @@ -625,14 +649,13 @@ func ShouldNotContainKey(actual interface{}, expected ...interface{}) error { // // Example of testsuite file: // -// name: Assertions testsuite -// testcases: -// - name: ShouldBeIn -// steps: -// - script: echo 1 -// assertions: -// - result.systemoutjson ShouldBeIn 1 2 -// +// name: Assertions testsuite +// testcases: +// - name: ShouldBeIn +// steps: +// - script: echo 1 +// assertions: +// - result.systemoutjson ShouldBeIn 1 2 func ShouldBeIn(actual interface{}, expected ...interface{}) error { if err := atLeast(1, expected); err != nil { return err @@ -657,14 +680,13 @@ func ShouldBeIn(actual interface{}, expected ...interface{}) error { // // Example of testsuite file: // -// name: Assertions testsuite -// testcases: -// - name: ShouldNotBeIn -// steps: -// - script: echo 3 -// assertions: -// - result.systemoutjson ShouldNotBeIn 1 2 -// +// name: Assertions testsuite +// testcases: +// - name: ShouldNotBeIn +// steps: +// - script: echo 3 +// assertions: +// - result.systemoutjson ShouldNotBeIn 1 2 func ShouldNotBeIn(actual interface{}, expected ...interface{}) error { if err := atLeast(1, expected); err != nil { return err @@ -936,17 +958,17 @@ func ShouldEqualTrimSpace(actual interface{}, expected ...interface{}) error { // // Example of testsuite file: // -// name: test ShouldHappenBefore -// vars: -// time: 2006-01-02T15:04:05+07:00 -// time_with_5s_after: 2006-01-02T15:04:10+07:00 -// testcases: -// - name: test assertion -// steps: -// - type: exec -// script: "echo {{.time}}" -// assertions: -// - result.systemout ShouldHappenBefore "{{.time_with_5s_after}}" +// name: test ShouldHappenBefore +// vars: +// time: 2006-01-02T15:04:05+07:00 +// time_with_5s_after: 2006-01-02T15:04:10+07:00 +// testcases: +// - name: test assertion +// steps: +// - type: exec +// script: "echo {{.time}}" +// assertions: +// - result.systemout ShouldHappenBefore "{{.time_with_5s_after}}" func ShouldHappenBefore(actual interface{}, expected ...interface{}) error { if err := need(1, expected); err != nil { return err @@ -973,17 +995,17 @@ func ShouldHappenBefore(actual interface{}, expected ...interface{}) error { // // Example of testsuite file: // -// name: test ShouldHappenOnOrBefore -// vars: -// time: 2006-01-02T15:04:05+07:00 -// time_with_5s_after: 2006-01-02T15:04:10+07:00 -// testcases: -// - name: test assertion -// steps: -// - type: exec -// script: "echo {{.time}}" -// assertions: -// - result.systemout ShouldHappenOnOrBefore "{{.time_with_5s_after}}" +// name: test ShouldHappenOnOrBefore +// vars: +// time: 2006-01-02T15:04:05+07:00 +// time_with_5s_after: 2006-01-02T15:04:10+07:00 +// testcases: +// - name: test assertion +// steps: +// - type: exec +// script: "echo {{.time}}" +// assertions: +// - result.systemout ShouldHappenOnOrBefore "{{.time_with_5s_after}}" func ShouldHappenOnOrBefore(actual interface{}, expected ...interface{}) error { if err := need(1, expected); err != nil { return err @@ -1009,17 +1031,17 @@ func ShouldHappenOnOrBefore(actual interface{}, expected ...interface{}) error { // // Example of testsuite file: // -// name: test ShouldHappenAfter -// vars: -// time_with_5s_before: 2006-01-02T15:04:00+07:00 -// time: 2006-01-02T15:04:05+07:00 -// testcases: -// - name: test assertion -// steps: -// - type: exec -// script: "echo {{.time}}" -// assertions: -// - result.systemout ShouldHappenAfter "{{.time_with_5s_before}}" +// name: test ShouldHappenAfter +// vars: +// time_with_5s_before: 2006-01-02T15:04:00+07:00 +// time: 2006-01-02T15:04:05+07:00 +// testcases: +// - name: test assertion +// steps: +// - type: exec +// script: "echo {{.time}}" +// assertions: +// - result.systemout ShouldHappenAfter "{{.time_with_5s_before}}" func ShouldHappenAfter(actual interface{}, expected ...interface{}) error { if err := need(1, expected); err != nil { return err @@ -1045,17 +1067,17 @@ func ShouldHappenAfter(actual interface{}, expected ...interface{}) error { // // Example of testsuite file: // -// name: test ShouldHappenOnOrAfter -// vars: -// time_with_5s_before: 2006-01-02T15:04:00+07:00 -// time: 2006-01-02T15:04:05+07:00 -// testcases: -// - name: test assertion -// steps: -// - type: exec -// script: "echo {{.time}}" -// assertions: -// - result.systemout ShouldHappenOnOrAfter "{{.time_with_5s_before}}" +// name: test ShouldHappenOnOrAfter +// vars: +// time_with_5s_before: 2006-01-02T15:04:00+07:00 +// time: 2006-01-02T15:04:05+07:00 +// testcases: +// - name: test assertion +// steps: +// - type: exec +// script: "echo {{.time}}" +// assertions: +// - result.systemout ShouldHappenOnOrAfter "{{.time_with_5s_before}}" func ShouldHappenOnOrAfter(actual interface{}, expected ...interface{}) error { if err := need(1, expected); err != nil { return err @@ -1081,18 +1103,18 @@ func ShouldHappenOnOrAfter(actual interface{}, expected ...interface{}) error { // // Example of testsuite file: // -// name: test ShouldHappenBetween -// vars: -// time_with_5s_before: 2006-01-02T15:04:00+07:00 -// time: 2006-01-02T15:04:05+07:00 -// time_with_5s_after: 2006-01-02T15:04:10+07:00 -// testcases: -// - name: test assertion -// steps: -// - type: exec -// script: "echo {{.time}}" -// assertions: -// - result.systemout ShouldHappenBetween "{{.time_with_5s_before}}" "{{.time_with_5s_after}}" +// name: test ShouldHappenBetween +// vars: +// time_with_5s_before: 2006-01-02T15:04:00+07:00 +// time: 2006-01-02T15:04:05+07:00 +// time_with_5s_after: 2006-01-02T15:04:10+07:00 +// testcases: +// - name: test assertion +// steps: +// - type: exec +// script: "echo {{.time}}" +// assertions: +// - result.systemout ShouldHappenBetween "{{.time_with_5s_before}}" "{{.time_with_5s_after}}" func ShouldHappenBetween(actual interface{}, expected ...interface{}) error { if err := need(2, expected); err != nil { return err @@ -1122,17 +1144,17 @@ func ShouldHappenBetween(actual interface{}, expected ...interface{}) error { // // Example of testsuite file: // -// name: test ShouldTimeEqual -// vars: -// time_expected: 2006-01-02T13:04:00Z -// time: 2006-01-02T15:04:00+02:00 -// testcases: -// - name: test assertion -// steps: -// - type: exec -// script: "echo {{.time}}" -// assertions: -// - result.systemout ShouldTimeEqual "{{.time_expected}}" +// name: test ShouldTimeEqual +// vars: +// time_expected: 2006-01-02T13:04:00Z +// time: 2006-01-02T15:04:00+02:00 +// testcases: +// - name: test assertion +// steps: +// - type: exec +// script: "echo {{.time}}" +// assertions: +// - result.systemout ShouldTimeEqual "{{.time_expected}}" func ShouldTimeEqual(actual interface{}, expected ...interface{}) error { if err := need(1, expected); err != nil { return err diff --git a/assertions/assertions_test.go b/assertions/assertions_test.go index f454d7bd..ff4c3a19 100644 --- a/assertions/assertions_test.go +++ b/assertions/assertions_test.go @@ -1,6 +1,8 @@ package assertions import ( + "fmt" + "github.com/stretchr/testify/assert" "testing" "time" ) @@ -1405,3 +1407,54 @@ func TestShouldTimeEqual(t *testing.T) { }) } } + +func TestShouldMatchRegex(t *testing.T) { + type args struct { + actual interface{} + expected []interface{} + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "with string", + args: args{ + actual: `a`, + expected: []interface{}{`a`}, + }, + }, + { + name: "with string regex", + args: args{ + actual: `abc`, + expected: []interface{}{`a.*c$`}, + }, + }, + { + name: "with number regex", + args: args{ + actual: `abc-123`, + expected: []interface{}{`.*[0-9]{3}$`}, + }, + }, + { + name: "with regex throwing error", + args: args{ + actual: `abc-123`, + expected: []interface{}{`.*[0-9]{6}$`}, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := ShouldMatchRegex(tt.args.actual, tt.args.expected...); (err != nil) != tt.wantErr { + msg := fmt.Sprintf("value %v not matching pattern : %v", tt.args.actual, tt.args.expected[0]) + assert.ErrorContainsf(t, err, msg, "Contains message") + t.Errorf("ShouldMatchRegex() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/tests/assertions/ShouldMatchRegex.yml b/tests/assertions/ShouldMatchRegex.yml new file mode 100644 index 00000000..4d253ac3 --- /dev/null +++ b/tests/assertions/ShouldMatchRegex.yml @@ -0,0 +1,22 @@ +name: Assertions only tests suite + +vars: + fiveARegex: "a{5}" + customVariable : '123456-abcd-end' + +testcases: + - name: ShouldMatchRegex on global variable + steps: + - type: exec + script: | + echo "aaaaa" + assertions: + - result.systemout ShouldMatchRegex '{{.fiveARegex}}' + - script: echo '{{.customVariable}}' + assertion: + - result.systemout ShouldMatchRegex '^[0-9]{2,}-[a-z]{4}-end$' + - type: exec + script: | + echo "{\"key\":\"testing\"}" + assertions: + - result.systemoutjson.key ShouldMatchRegex [a-z]{6,} diff --git a/tests/failing/assertions_regex.yml b/tests/failing/assertions_regex.yml new file mode 100644 index 00000000..f3f85858 --- /dev/null +++ b/tests/failing/assertions_regex.yml @@ -0,0 +1,15 @@ +name: Assertions only tests suite + +vars: + fiveARegex: "a{5}" + +testcases: + - name: ShouldMatchRegex on global variable + steps: + - type: exec + script: | + echo "1234" + assertions: + - result.systemout ShouldMatchRegex '{{.fiveARegex}}' + +