Skip to content
Permalink
Browse files

[#355] Add Custom Version Matcher (#508)

* [#355] Add Custom Version Matcher 

Add Custom Version Matcher 
Add Unit Tests
Add Integration Tests
Update Documentation

* [#355] Pivot to pure semver Matcher

* [#355] Add negation Integration Test as per PR feedback
  • Loading branch information
pedroMMM authored and aelsabbahy committed Dec 18, 2019
1 parent 2134993 commit 566b1b71effcb40b6afe54d210290324406b0b3a
@@ -852,9 +852,21 @@ package:
contain-element: "4.1.0"
```
Custom semver matcher is available under `semver-constraint`:
```yaml
example:
content:
- 1.0.1
- 1.9.9
matches:
semver-constraint: ">1.0.0 <2.0.0 !=1.5.0"
```
For more information see:
* [gomega_test.go](https://github.com/aelsabbahy/goss/blob/master/resource/gomega_test.go) - For a complete set of supported json -> Gomega mapping
* [gomega](https://onsi.github.io/gomega/) - Gomega matchers reference
* [semver](https://github.com/blang/semver#ranges) - Semver constraint (or range) syntax
## Templates
3 go.mod
@@ -4,6 +4,7 @@ require (
github.com/achanda/go-sysctl v0.0.0-20160222034550-6be7678c45d2
github.com/aelsabbahy/GOnetstat v0.0.0-20160428114218-edf89f784e08
github.com/aelsabbahy/go-ps v0.0.0-20170721000941-443386855ca1
github.com/blang/semver v3.5.1+incompatible
github.com/cheekybits/genny v0.0.0-20160824153601-e8e29e67948b
github.com/docker/docker v0.0.0-20161109014415-383a2f046b16
github.com/fatih/color v0.0.0-20161025120501-bf82308e8c85
@@ -19,7 +20,7 @@ require (
github.com/onsi/gomega v0.0.0-20161031154339-ff4bc6b6f9f5
github.com/opencontainers/runc v0.0.0-20161107232042-8779fa57eb4a
github.com/patrickmn/go-cache v2.0.0+incompatible
github.com/stretchr/testify v1.4.0 // indirect
github.com/stretchr/testify v1.4.0
github.com/urfave/cli v0.0.0-20161102131801-d86a009f5e13
golang.org/x/sys v0.0.0-20181210030007-2a47403f2ae5 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
2 go.sum
@@ -4,6 +4,8 @@ github.com/aelsabbahy/GOnetstat v0.0.0-20160428114218-edf89f784e08 h1:oD15ssIOuF
github.com/aelsabbahy/GOnetstat v0.0.0-20160428114218-edf89f784e08/go.mod h1:FETZSu2VGNDJbGfeRExaz/SNbX0TTaqJEMo1yvsKoZ8=
github.com/aelsabbahy/go-ps v0.0.0-20170721000941-443386855ca1 h1:s4dvLggvQOov0YFdv8XQvX+72TAFzfJg+6SgoXiIaq4=
github.com/aelsabbahy/go-ps v0.0.0-20170721000941-443386855ca1/go.mod h1:70tSBushy/POz6cCR294bKno4BNAC7XWVdkkxWQ1N6E=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/cheekybits/genny v0.0.0-20160824153601-e8e29e67948b h1:EaV7ZKUbpQK3eErRkV5GKl7s6SZU30dEB6gimH5BLLk=
github.com/cheekybits/genny v0.0.0-20160824153601-e8e29e67948b/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
@@ -211,3 +211,17 @@ matching:
- have-key-with-value:
foo: bar
- have-key: baz
semver:
content:
- 1.0.1
- 1.9.9
matches:
semver-constraint: ">1.0.0 <2.0.0 !=1.5.0"
semver2:
content:
- 1.0.1
- 1.5.0
- 1.9.9
matches:
not:
semver-constraint: ">1.0.0 <2.0.0 !=1.5.0"
@@ -44,9 +44,9 @@ out=$(docker_exec "/goss/$os/goss-linux-$arch" --vars "/goss/vars.yaml" -g "/gos
echo "$out"

if [[ $os == "arch" ]]; then
egrep -q 'Count: 81, Failed: 0, Skipped: 3' <<<"$out"
egrep -q 'Count: 83, Failed: 0, Skipped: 3' <<<"$out"
else
egrep -q 'Count: 97, Failed: 0, Skipped: 5' <<<"$out"
egrep -q 'Count: 99, Failed: 0, Skipped: 5' <<<"$out"
fi

if [[ ! $os == "arch" ]]; then
@@ -0,0 +1,104 @@
package matchers

import (
"fmt"
"reflect"

"github.com/blang/semver"
"github.com/onsi/gomega/format"
"github.com/onsi/gomega/types"
)

func BeSemverConstraint(constraint interface{}) types.GomegaMatcher {
return &BeSemverConstraintMatcher{
Constraint: constraint,
}
}

type BeSemverConstraintMatcher struct {
Constraint interface{}
}

func (matcher *BeSemverConstraintMatcher) Match(actual interface{}) (success bool, err error) {
constraint, ok := toConstraint(matcher.Constraint)
if !ok {
return false, fmt.Errorf("Expected a valid semver constraint. Got:\n%s", format.Object(matcher.Constraint, 1))
}

actualSlice, ok := toVersions(actual)
if !ok {
return false, fmt.Errorf("Expected a single or list of semver valid version(s). Got:\n%s", format.Object(actual, 1))
}

for _, v := range actualSlice {
if !constraint(*v) {
return false, nil
}
}

return true, nil
}

func (matcher *BeSemverConstraintMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, fmt.Sprintf("to be %s", matcher.Constraint))
}

func (matcher *BeSemverConstraintMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, fmt.Sprintf("not to be %s", matcher.Constraint))
}

func toConstraint(in interface{}) (semver.Range, bool) {
str, ok := in.(string)
if !ok {
return nil, false
}

out, err := semver.ParseRange(str)
return out, err == nil
}

func toVersion(in interface{}) (*semver.Version, bool) {
str, ok := in.(string)
if !ok {
return nil, false
}

v, err := semver.Parse(str)
if err != nil {
return nil, false
}

return &v, true
}

func toVersions(in interface{}) ([]*semver.Version, bool) {
if v, ok := toVersion(in); ok {
return []*semver.Version{v}, ok
}

if reflect.ValueOf(in).Kind() != reflect.Slice {
return nil, false
}

out := make([]*semver.Version, 0)

if slice, ok := in.([]interface{}); ok {
for _, ele := range slice {
if v, ok := toVersion(ele); ok {
out = append(out, v)
} else {
return nil, false
}
}
} else if slice, ok := in.([]string); ok {
for _, ele := range slice {
if v, ok := toVersion(ele); ok {
out = append(out, v)
} else {
return nil, false
}
}
}

return out, len(out) > 0
}

0 comments on commit 566b1b7

Please sign in to comment.
You can’t perform that action at this time.