Merge pull request #139 from lburgazzoli/matchers

Use github.com/lburgazzoli/gomega-matchers
This commit is contained in:
Luca Burgazzoli 2024-04-23 12:50:41 +02:00 committed by GitHub
commit 734b4bf507
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 33 additions and 266 deletions

3
go.mod
View File

@ -1,6 +1,6 @@
module github.com/dapr-sandbox/dapr-kubernetes-operator
go 1.22
go 1.22.2
require (
github.com/go-logr/logr v1.4.1
@ -90,6 +90,7 @@ require (
github.com/klauspost/compress v1.16.0 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/lburgazzoli/gomega-matchers v0.0.0-20240423093716-45891b1edc66 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.7.7 // indirect

4
go.sum
View File

@ -247,6 +247,10 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/lburgazzoli/gomega-matchers v0.0.0-20240423084142-42952d04d67a h1:qil/O2L2zJlmBzjutJ4XObs26fbjE8x/XbyI4afX9yA=
github.com/lburgazzoli/gomega-matchers v0.0.0-20240423084142-42952d04d67a/go.mod h1:wKad323qq77Y2PU1kwLcHx/uO3l8A1m5/fiCk8MFreE=
github.com/lburgazzoli/gomega-matchers v0.0.0-20240423093716-45891b1edc66 h1:KNUCtO+GgZ72eG9rS1dzINhmrz7a5xQ7iuuQSeTwJU0=
github.com/lburgazzoli/gomega-matchers v0.0.0-20240423093716-45891b1edc66/go.mod h1:wKad323qq77Y2PU1kwLcHx/uO3l8A1m5/fiCk8MFreE=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=

View File

@ -1,6 +1,7 @@
package common
import (
"encoding/json"
"fmt"
"net/http"
@ -9,7 +10,7 @@ import (
"github.com/dapr-sandbox/dapr-kubernetes-operator/test/support"
"github.com/dapr-sandbox/dapr-kubernetes-operator/test/support/dapr"
"github.com/dapr-sandbox/dapr-kubernetes-operator/test/support/matchers"
"github.com/lburgazzoli/gomega-matchers/pkg/matchers/jq"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
@ -31,7 +32,7 @@ func ValidateDaprApp(test support.Test, namespace string) {
"Failure checking for App Deployment",
)
test.Eventually(support.PodList(test, "app="+appName, namespace), support.TestTimeoutShort).Should(
gomega.WithTransform(matchers.AsJSON(), matchers.MatchJQ(".items[0].spec.containers | length == 2")),
gomega.WithTransform(json.Marshal, jq.Match(".items[0].spec.containers | length == 2")),
"Failure checking for App Pods (sidecar injection)",
)
@ -58,7 +59,7 @@ func ValidateDaprApp(test support.Test, namespace string) {
test.Eventually(dapr.GET(test, base+"/read"), support.TestTimeoutLong).Should(
gomega.And(
gomega.HaveHTTPStatus(http.StatusOK),
gomega.HaveHTTPBody(gomega.Not(matchers.MatchJQf(`.Values | any(. == "%s")`, value))),
gomega.HaveHTTPBody(gomega.Not(jq.Match(`.Values | any(. == "%s")`, value))),
),
"Failure to read initial values",
)
@ -73,7 +74,7 @@ func ValidateDaprApp(test support.Test, namespace string) {
test.Eventually(dapr.GET(test, base+"/read"), support.TestTimeoutLong).Should(
gomega.And(
gomega.HaveHTTPStatus(http.StatusOK),
gomega.HaveHTTPBody(matchers.MatchJQf(`.Values | any(. == "%s")`, value)),
gomega.HaveHTTPBody(jq.Match(`.Values | any(. == "%s")`, value)),
),
"Failure to read final values",
)

View File

@ -1,8 +1,11 @@
package operator
import (
"encoding/json"
"testing"
"github.com/lburgazzoli/gomega-matchers/pkg/matchers/jq"
"github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/conditions"
"github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/controller"
"github.com/dapr-sandbox/dapr-kubernetes-operator/test/support/dapr"
@ -13,7 +16,6 @@ import (
corev1 "k8s.io/api/core/v1"
. "github.com/dapr-sandbox/dapr-kubernetes-operator/test/support"
. "github.com/dapr-sandbox/dapr-kubernetes-operator/test/support/matchers"
. "github.com/onsi/gomega"
daprAc "github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/client/operator/applyconfiguration/operator/v1alpha1"
@ -43,10 +45,10 @@ func TestDaprInstanceDeployWithDefaults(t *testing.T) {
WithTransform(ConditionStatus(appsv1.DeploymentAvailable), Equal(corev1.ConditionTrue)))
test.Eventually(dapr.Instance(test, instance), TestTimeoutLong).Should(
WithTransform(AsJSON(), And(
MatchJQ(`.status.chart.name == "dapr"`),
MatchJQ(`.status.chart.repo == "embedded"`),
MatchJQ(`.status.chart.version == "1.13.2"`),
WithTransform(json.Marshal, And(
jq.Match(`.status.chart.name == "dapr"`),
jq.Match(`.status.chart.repo == "embedded"`),
jq.Match(`.status.chart.version == "1.13.2"`),
)),
)
}
@ -76,10 +78,10 @@ func TestDaprInstanceDeployWithCustomChart(t *testing.T) {
WithTransform(ConditionStatus(appsv1.DeploymentAvailable), Equal(corev1.ConditionTrue)))
test.Eventually(dapr.Instance(test, instance), TestTimeoutLong).Should(
WithTransform(AsJSON(), And(
MatchJQ(`.status.chart.name == "dapr"`),
MatchJQ(`.status.chart.repo == "https://dapr.github.io/helm-charts"`),
MatchJQ(`.status.chart.version == "1.13.0"`),
WithTransform(json.Marshal, And(
jq.Match(`.status.chart.name == "dapr"`),
jq.Match(`.status.chart.repo == "https://dapr.github.io/helm-charts"`),
jq.Match(`.status.chart.version == "1.13.0"`),
)),
)
}
@ -113,17 +115,17 @@ func TestDaprInstanceDeployWithCustomSidecarImage(t *testing.T) {
WithTransform(ConditionStatus(appsv1.DeploymentAvailable), Equal(corev1.ConditionTrue)))
test.Eventually(dapr.Instance(test, instance), TestTimeoutLong).Should(
WithTransform(AsJSON(), And(
MatchJQ(`.status.chart.name == "dapr"`),
MatchJQ(`.status.chart.repo == "embedded"`),
MatchJQ(`.status.chart.version == "1.13.2"`),
WithTransform(json.Marshal, And(
jq.Match(`.status.chart.name == "dapr"`),
jq.Match(`.status.chart.repo == "embedded"`),
jq.Match(`.status.chart.version == "1.13.2"`),
)),
)
test.Eventually(PodList(test, "app=dapr-sidecar-injector", instance.Namespace), TestTimeoutLong).Should(
WithTransform(AsJSON(), And(
MatchJQf(`.items[0].spec.containers[0].env[] | select(.name == "SIDECAR_IMAGE") | .value == "docker.io/daprio/daprd:%s"`, test.ID()),
MatchJQf(`.items[0].spec.containers[0].env[] | select(.name == "SIDECAR_IMAGE_PULL_POLICY") | .value == "%s"`, corev1.PullAlways),
WithTransform(json.Marshal, And(
jq.Match(`.items[0].spec.containers[0].env[] | select(.name == "SIDECAR_IMAGE") | .value == "docker.io/daprio/daprd:%s"`, test.ID()),
jq.Match(`.items[0].spec.containers[0].env[] | select(.name == "SIDECAR_IMAGE_PULL_POLICY") | .value == "%s"`, corev1.PullAlways),
)),
)
@ -159,10 +161,10 @@ func TestDaprInstanceDeployWithApp(t *testing.T) {
WithTransform(ConditionStatus(appsv1.DeploymentAvailable), Equal(corev1.ConditionTrue)))
test.Eventually(dapr.Instance(test, instance), TestTimeoutLong).Should(
WithTransform(AsJSON(), And(
MatchJQ(`.status.chart.name == "dapr"`),
MatchJQ(`.status.chart.repo == "embedded"`),
MatchJQ(`.status.chart.version == "1.13.2"`),
WithTransform(json.Marshal, And(
jq.Match(`.status.chart.name == "dapr"`),
jq.Match(`.status.chart.repo == "embedded"`),
jq.Match(`.status.chart.version == "1.13.2"`),
)),
)

View File

@ -1,89 +0,0 @@
package matchers
import (
"bytes"
"encoding/json"
"fmt"
"github.com/itchyny/gojq"
"github.com/onsi/gomega/format"
"github.com/onsi/gomega/types"
)
func MatchJQ(expression string) types.GomegaMatcher {
return &jqMatcher{
Expression: expression,
}
}
func MatchJQf(format string, args ...any) types.GomegaMatcher {
return &jqMatcher{
Expression: fmt.Sprintf(format, args...),
}
}
var _ types.GomegaMatcher = &jqMatcher{}
type jqMatcher struct {
Expression string
firstFailurePath []interface{}
}
func (matcher *jqMatcher) Match(actual interface{}) (bool, error) {
query, err := gojq.Parse(matcher.Expression)
if err != nil {
return false, err
}
actualString, ok := toString(actual)
if !ok {
return false, fmt.Errorf("MatchJQMatcher matcher requires a string, stringer, or []byte. Got actual:\n%s", format.Object(actual, 1))
}
data := make(map[string]interface{})
if err := json.Unmarshal([]byte(actualString), &data); err != nil {
return false, err
}
it := query.Run(data)
v, ok := it.Next()
if !ok {
return false, nil
}
if err, ok := v.(error); ok {
return false, err
}
if match, ok := v.(bool); ok {
return match, nil
}
return false, nil
}
func (matcher *jqMatcher) FailureMessage(actual interface{}) string {
actualString, expectedString, _ := matcher.prettyPrint(actual)
return formattedMessage(format.Message(actualString, "to match JQ Expression", expectedString), matcher.firstFailurePath)
}
func (matcher *jqMatcher) NegatedFailureMessage(actual interface{}) string {
actualString, expectedString, _ := matcher.prettyPrint(actual)
return formattedMessage(format.Message(actualString, "not to match JQ expression", expectedString), matcher.firstFailurePath)
}
func (matcher *jqMatcher) prettyPrint(actual interface{}) (string, string, error) {
actualString, ok := toString(actual)
if !ok {
return "", "", fmt.Errorf("the MatchJQMatcher matcher requires a string, stringer, or []byte. Got actual:\n%s", format.Object(actual, 1))
}
abuf := new(bytes.Buffer)
if err := json.Indent(abuf, []byte(actualString), "", " "); err != nil {
return "", "", fmt.Errorf("actual '%s' should match '%s', but it does not.\nUnderlying error: %w", actualString, matcher.Expression, err)
}
return abuf.String(), matcher.Expression, nil
}

View File

@ -1,17 +0,0 @@
package matchers
import (
"testing"
. "github.com/onsi/gomega"
)
func TestJQMatcher(t *testing.T) {
g := NewWithT(t)
g.Expect(`{"a":1}`).Should(MatchJQ(`.a == 1`))
g.Expect(`{"a":1}`).Should(Not(MatchJQ(`.a == 2`)))
g.Expect(`{"Values":[ "foo" ]}`).Should(MatchJQ(`.Values | if . then any(. == "foo") else false end`))
g.Expect(`{"Values":[ "foo" ]}`).Should(Not(MatchJQ(`.Values | if . then any(. == "bar") else false end`)))
g.Expect(`{"Values": null}`).Should(Not(MatchJQ(`.Values | if . then any(. == "foo") else false end`)))
}

View File

@ -1,57 +0,0 @@
package matchers
import (
"encoding/json"
"fmt"
"strings"
)
func formattedMessage(comparisonMessage string, failurePath []interface{}) string {
var diffMessage string
if len(failurePath) == 0 {
diffMessage = ""
} else {
diffMessage = "\n\nfirst mismatched key: " + formattedFailurePath(failurePath)
}
return fmt.Sprintf("%s%s", comparisonMessage, diffMessage)
}
func formattedFailurePath(failurePath []interface{}) string {
formattedPaths := []string{}
for i := len(failurePath) - 1; i >= 0; i-- {
switch p := failurePath[i].(type) {
case int:
formattedPaths = append(formattedPaths, fmt.Sprintf(`[%d]`, p))
default:
if i != len(failurePath)-1 {
formattedPaths = append(formattedPaths, ".")
}
formattedPaths = append(formattedPaths, fmt.Sprintf(`"%s"`, p))
}
}
return strings.Join(formattedPaths, "")
}
func toString(a interface{}) (string, bool) {
aString, isString := a.(string)
if isString {
return aString, true
}
aBytes, isBytes := a.([]byte)
if isBytes {
return string(aBytes), true
}
aStringer, isStringer := a.(fmt.Stringer)
if isStringer {
return aStringer.String(), true
}
aJSONRawMessage, isJSONRawMessage := a.(json.RawMessage)
if isJSONRawMessage {
return string(aJSONRawMessage), true
}
return "", false
}

View File

@ -1,62 +0,0 @@
package matchers
import (
"encoding/json"
"fmt"
"github.com/itchyny/gojq"
"github.com/onsi/gomega/format"
)
func ExtractJQ(expression string) func(in any) (any, error) {
return func(in any) (any, error) {
query, err := gojq.Parse(expression)
if err != nil {
return nil, err
}
actualString, ok := toString(in)
if !ok {
return false, fmt.Errorf("ExtractJQ requires a string, stringer, or []byte. Got actual:\n%s", format.Object(in, 1))
}
if len(actualString) == 0 {
return nil, nil
}
b := []byte(actualString)
var it gojq.Iter
// rough check for object vs array
switch b[0] {
case '{':
data := make(map[string]any)
if err := json.Unmarshal(b, &data); err != nil {
return false, err
}
it = query.Run(data)
case '[':
var data []any
if err := json.Unmarshal(b, &data); err != nil {
return false, err
}
it = query.Run(data)
default:
return false, fmt.Errorf("ExtractJQ requires a Json Array or Object. Got actual:\n%s", format.Object(in, 1))
}
v, ok := it.Next()
if !ok {
return false, nil
}
if err, ok := v.(error); ok {
return false, err
}
return v, nil
}
}

View File

@ -1,16 +0,0 @@
package matchers
import (
"encoding/json"
)
func AsJSON() func(in any) (any, error) {
return func(in any) (any, error) {
data, err := json.Marshal(in)
if err != nil {
return nil, err
}
return data, nil
}
}