Repository: Unleash/unleash-client-go
Pull request #100 (2022-03-09 12:16:05)

fix: use strict semver parsing


Patch:
diff --git a/go.mod b/go.mod
index ef912ea..b9c32bc 100644
--- a/go.mod
+++ b/go.mod
@@ -1,7 +1,7 @@
 module github.com/Unleash/unleash-client-go/v3
 
 require (
-	github.com/Masterminds/semver v1.5.0
+	github.com/Masterminds/semver/v3 v3.1.1
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
diff --git a/go.sum b/go.sum
index 239337f..66e2db8 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,5 @@
-github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
-github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
+github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
+github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
diff --git a/internal/constraints/operator_semver.go b/internal/constraints/operator_semver.go
index d71008d..cee20ab 100644
--- a/internal/constraints/operator_semver.go
+++ b/internal/constraints/operator_semver.go
@@ -1,7 +1,7 @@
 package constraints
 
 import (
-	"github.com/Masterminds/semver"
+	"github.com/Masterminds/semver/v3"
 	"github.com/Unleash/unleash-client-go/v3/api"
 	"github.com/Unleash/unleash-client-go/v3/context"
 )
@@ -30,8 +30,8 @@ func operatorSemver(
 	check func(context *semver.Version, constraint *semver.Version) bool,
 ) (bool, error) {
 	contextValue := ctx.Field(constraint.ContextName)
-	contextParsed, contextErr := semver.NewVersion(contextValue)
-	constraintParsed, constraintErr := semver.NewVersion(constraint.Value)
+	contextParsed, contextErr := semver.StrictNewVersion(contextValue)
+	constraintParsed, constraintErr := semver.StrictNewVersion(constraint.Value)
 
 	if contextErr != nil {
 		return false, contextErr


Test patch:
diff --git a/internal/constraints/check_test.go b/internal/constraints/check_test.go
index 67c4e7f..1105d9a 100644
--- a/internal/constraints/check_test.go
+++ b/internal/constraints/check_test.go
@@ -12,6 +12,7 @@ type checkTestCase struct {
 	ctx         *context.Context
 	constraints []api.Constraint
 	expected    bool
+	error       string
 }
 
 func TestCheck(t *testing.T) {
diff --git a/internal/constraints/operator_semver_test.go b/internal/constraints/operator_semver_test.go
index 79d9a4a..f11745e 100644
--- a/internal/constraints/operator_semver_test.go
+++ b/internal/constraints/operator_semver_test.go
@@ -10,60 +10,50 @@ import (
 func TestOperatorSemver(t *testing.T) {
 	testCases := []checkTestCase{
 		{
-			ctx:         &context.Context{Properties: map[string]string{"v": "1.2.3"}},
-			constraints: []api.Constraint{{ContextName: "v", Operator: "SEMVER_EQ", Value: "3.2.1"}},
+			ctx:         &context.Context{Properties: map[string]string{"x": "1.2.3"}},
+			constraints: []api.Constraint{{ContextName: "x", Operator: "SEMVER_EQ", Value: "3.2.1"}},
 			expected:    false,
 		},
 		{
-			ctx:         &context.Context{Properties: map[string]string{"v": "1.2.3"}},
-			constraints: []api.Constraint{{ContextName: "v", Operator: "SEMVER_EQ", Value: "1.2.3"}},
-			expected:    true,
+			ctx:         &context.Context{Properties: map[string]string{"x": "3.2.1"}},
+			constraints: []api.Constraint{{ContextName: "x", Operator: "SEMVER_EQ", Value: "1.2.3"}},
+			expected:    false,
 		},
 		{
-			ctx:         &context.Context{Properties: map[string]string{"v": "1.2.3"}},
-			constraints: []api.Constraint{{ContextName: "v", Operator: "SEMVER_EQ", Value: "v1.2.3"}},
+			ctx:         &context.Context{Properties: map[string]string{"x": "1.2.3"}},
+			constraints: []api.Constraint{{ContextName: "x", Operator: "SEMVER_EQ", Value: "1.2.3"}},
 			expected:    true,
 		},
 		{
-			ctx:         &context.Context{Properties: map[string]string{"v": "v1.2.3"}},
-			constraints: []api.Constraint{{ContextName: "v", Operator: "SEMVER_EQ", Value: "v3.2.1"}},
+			ctx:         &context.Context{Properties: map[string]string{"x": "1.2.3"}},
+			constraints: []api.Constraint{{ContextName: "x", Operator: "SEMVER_LT", Value: "1.2.3"}},
 			expected:    false,
 		},
 		{
-			ctx:         &context.Context{Properties: map[string]string{"v": "v1.2.3"}},
-			constraints: []api.Constraint{{ContextName: "v", Operator: "SEMVER_EQ", Value: "v1.2.3"}},
-			expected:    true,
+			ctx:         &context.Context{Properties: map[string]string{"x": "3.2.1"}},
+			constraints: []api.Constraint{{ContextName: "x", Operator: "SEMVER_LT", Value: "1.2.3"}},
+			expected:    false,
 		},
 		{
-			ctx:         &context.Context{Properties: map[string]string{"v": "v1.2.3"}},
-			constraints: []api.Constraint{{ContextName: "v", Operator: "SEMVER_EQ", Value: "v1.2.3"}},
+			ctx:         &context.Context{Properties: map[string]string{"x": "1.2.3"}},
+			constraints: []api.Constraint{{ContextName: "x", Operator: "SEMVER_LT", Value: "3.2.1"}},
 			expected:    true,
 		},
 		{
-			ctx:         &context.Context{Properties: map[string]string{"v": "v1.2.3"}},
-			constraints: []api.Constraint{{ContextName: "v", Operator: "SEMVER_LT", Value: "v1.2.3"}},
+			ctx:         &context.Context{Properties: map[string]string{"x": "1.2.3"}},
+			constraints: []api.Constraint{{ContextName: "x", Operator: "SEMVER_GT", Value: "1.2.3"}},
 			expected:    false,
 		},
 		{
-			ctx:         &context.Context{Properties: map[string]string{"v": "v3.2.1"}},
-			constraints: []api.Constraint{{ContextName: "v", Operator: "SEMVER_LT", Value: "v1.2.3"}},
+			ctx:         &context.Context{Properties: map[string]string{"x": "1.2.3"}},
+			constraints: []api.Constraint{{ContextName: "x", Operator: "SEMVER_GT", Value: "3.2.1"}},
 			expected:    false,
 		},
 		{
-			ctx:         &context.Context{Properties: map[string]string{"v": "v1.2.3"}},
-			constraints: []api.Constraint{{ContextName: "v", Operator: "SEMVER_LT", Value: "v3.2.1"}},
+			ctx:         &context.Context{Properties: map[string]string{"x": "3.2.1"}},
+			constraints: []api.Constraint{{ContextName: "x", Operator: "SEMVER_GT", Value: "1.2.3"}},
 			expected:    true,
 		},
-		{
-			ctx:         &context.Context{Properties: map[string]string{"v": "v3.2.1"}},
-			constraints: []api.Constraint{{ContextName: "v", Operator: "SEMVER_GT", Value: "v1.2.3"}},
-			expected:    true,
-		},
-		{
-			ctx:         &context.Context{Properties: map[string]string{"v": "v1.2.3"}},
-			constraints: []api.Constraint{{ContextName: "v", Operator: "SEMVER_GT", Value: "v3.2.1"}},
-			expected:    false,
-		},
 	}
 
 	for _, tc := range testCases {
@@ -74,3 +64,55 @@ func TestOperatorSemver(t *testing.T) {
 		}
 	}
 }
+
+func TestOperatorSemverErrors(t *testing.T) {
+	testCases := []checkTestCase{
+		{
+			ctx:         &context.Context{Properties: map[string]string{"x": "v1.2.3"}},
+			constraints: []api.Constraint{{ContextName: "x", Operator: "SEMVER_EQ", Value: "1.2.3"}},
+			error:       "Invalid characters in version",
+		},
+		{
+			ctx:         &context.Context{Properties: map[string]string{"x": "1.2.3"}},
+			constraints: []api.Constraint{{ContextName: "x", Operator: "SEMVER_EQ", Value: "v1.2.3"}},
+			error:       "Invalid characters in version",
+		},
+		{
+			ctx:         &context.Context{Properties: map[string]string{"x": "v1.2.3"}},
+			constraints: []api.Constraint{{ContextName: "x", Operator: "SEMVER_LT", Value: "3.2.1"}},
+			error:       "Invalid characters in version",
+		},
+		{
+			ctx:         &context.Context{Properties: map[string]string{"x": "1.2.3"}},
+			constraints: []api.Constraint{{ContextName: "x", Operator: "SEMVER_LT", Value: "v3.2.1"}},
+			error:       "Invalid characters in version",
+		},
+		{
+			ctx:         &context.Context{Properties: map[string]string{"x": "v3.2.1"}},
+			constraints: []api.Constraint{{ContextName: "x", Operator: "SEMVER_GT", Value: "1.2.3"}},
+			error:       "Invalid characters in version",
+		},
+		{
+			ctx:         &context.Context{Properties: map[string]string{"x": "3.2.1"}},
+			constraints: []api.Constraint{{ContextName: "x", Operator: "SEMVER_GT", Value: "v1.2.3"}},
+			error:       "Invalid characters in version",
+		},
+		{
+			ctx:         &context.Context{Properties: map[string]string{"x": "1.0"}},
+			constraints: []api.Constraint{{ContextName: "x", Operator: "SEMVER_EQ", Value: "1.0.0"}},
+			error:       "Invalid Semantic Version",
+		},
+		{
+			ctx:         &context.Context{Properties: map[string]string{"x": "1.0.0"}},
+			constraints: []api.Constraint{{ContextName: "x", Operator: "SEMVER_EQ", Value: "1.0"}},
+			error:       "Invalid Semantic Version",
+		},
+	}
+
+	for _, tc := range testCases {
+		ok, err := Check(tc.ctx, tc.constraints)
+		assert.Equal(t, false, ok)
+		assert.Error(t, err)
+		assert.Equal(t, tc.error, err.Error())
+	}
+}