Skip to content

Commit

Permalink
Move must and match packages out of internal; add generic must functions
Browse files Browse the repository at this point in the history
  • Loading branch information
kegsay committed Oct 4, 2023
1 parent 765746a commit cc1aaaa
Show file tree
Hide file tree
Showing 75 changed files with 346 additions and 368 deletions.
38 changes: 26 additions & 12 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,32 +1,46 @@
module github.com/matrix-org/complement

go 1.16
go 1.18

require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/docker v20.10.24+incompatible
github.com/docker/go-connections v0.4.0
github.com/docker/go-units v0.4.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/gorilla/mux v1.8.0
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530
github.com/matrix-org/gomatrixserverlib v0.0.0-20230921171121-0466775328c7
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66
github.com/sirupsen/logrus v1.9.3
github.com/tidwall/gjson v1.16.0
github.com/tidwall/sjson v1.2.5
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
gonum.org/v1/plot v0.11.0
maunium.net/go/mautrix v0.11.0
)

require (
git.sr.ht/~sbinet/gg v0.3.1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/go-fonts/liberation v0.2.0 // indirect
github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81 // indirect
github.com/go-pdf/fpdf v0.6.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
github.com/sirupsen/logrus v1.9.3
github.com/tidwall/gjson v1.16.0
github.com/pkg/errors v0.9.1 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 // indirect
golang.org/x/image v0.5.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
gonum.org/v1/plot v0.11.0
gotest.tools/v3 v3.0.3 // indirect
maunium.net/go/mautrix v0.11.0
)
125 changes: 4 additions & 121 deletions go.sum

Large diffs are not rendered by default.

File renamed without changes.
81 changes: 52 additions & 29 deletions internal/match/json.go → match/json.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
// package match contains matchers for HTTP and JSON data.
//
// Matchers are composable functions which check for the data specified, returning a golang error if a matcher fails.
// They are typically used with the 'must' package in the following way:
//
// res := user.Do(t, "GET", []string{"_matrix", "client", "v3", "rooms", roomID, "state", "m.room.server_acl"})
// must.MatchResponse(t, res, match.HTTPResponse{
// StatusCode: 200,
// JSON: []match.JSON{
// match.JSONKeyEqual("allow", []string{"*"}),
// match.JSONKeyEqual("deny", []string{"hs2"}),
// match.JSONKeyEqual("allow_ip_literals", true),
// },
// })
//
// Matchers have no concept of tests, and do not automatically fail tests if the match fails. This can be useful
// when you want to repeatedly perform a check until it succeeds (e.g from /sync). If you want matches to fail a test,
// you can use the 'must' package.
package match

import (
Expand All @@ -23,28 +41,16 @@ func JSONKeyEqual(wantKey string, wantValue interface{}) JSON {
return fmt.Errorf("key '%s' missing", wantKey)
}
gotValue := res.Value()
if !JSONDeepEqual([]byte(res.Raw), wantValue) {
if !jsonDeepEqual([]byte(res.Raw), wantValue) {
return fmt.Errorf(
"key '%s' got '%v' (type %T) want '%v' (type %T)",
wantKey, gotValue, gotValue, wantValue, wantValue,
)
} else {
return nil
}
return nil
}
}

// JSONDeepEqual compares raw json with a json-serializable value, seeing if they're equal.
func JSONDeepEqual(gotJson []byte, wantValue interface{}) bool {
// marshal what the test gave us
wantBytes, _ := json.Marshal(wantValue)
// re-marshal what the network gave us to acount for key ordering
var gotVal interface{}
_ = json.Unmarshal(gotJson, &gotVal)
gotBytes, _ := json.Marshal(gotVal)
return bytes.Equal(gotBytes, wantBytes)
}

// JSONKeyPresent returns a matcher which will check that `wantKey` is present in the JSON object.
// `wantKey` can be nested, see https://godoc.org/github.com/tidwall/gjson#Get for details.
func JSONKeyPresent(wantKey string) JSON {
Expand Down Expand Up @@ -130,7 +136,7 @@ func jsonCheckOffInternal(wantKey string, wantItems []interface{}, allowUnwanted
want := -1
for i, w := range wantItems {
wBytes, _ := json.Marshal(w)
if JSONDeepEqual(wBytes, item) {
if jsonDeepEqual(wBytes, item) {
want = i
break
}
Expand Down Expand Up @@ -166,6 +172,7 @@ func jsonCheckOffInternal(wantKey string, wantItems []interface{}, allowUnwanted
}
}

// EXPERIMENTAL
// JSONCheckOffAllowUnwanted returns a matcher which will loop over `wantKey` and ensure that the items
// (which can be array elements or object keys)
// are present exactly once in any order in `wantItems`. Allows unexpected items or items
Expand All @@ -177,17 +184,19 @@ func jsonCheckOffInternal(wantKey string, wantItems []interface{}, allowUnwanted
// it's an object).
//
// Usage: (ensures `events` has these events in any order, with the right event type)
// JSONCheckOffAllowUnwanted("events", []interface{}{"$foo:bar", "$baz:quuz"}, func(r gjson.Result) interface{} {
// return r.Get("event_id").Str
// }, func(eventID interface{}, eventBody gjson.Result) error {
// if eventBody.Get("type").Str != "m.room.message" {
// return fmt.Errorf("expected event to be 'm.room.message'")
// }
// })
//
// JSONCheckOffAllowUnwanted("events", []interface{}{"$foo:bar", "$baz:quuz"}, func(r gjson.Result) interface{} {
// return r.Get("event_id").Str
// }, func(eventID interface{}, eventBody gjson.Result) error {
// if eventBody.Get("type").Str != "m.room.message" {
// return fmt.Errorf("expected event to be 'm.room.message'")
// }
// })
func JSONCheckOffAllowUnwanted(wantKey string, wantItems []interface{}, mapper func(gjson.Result) interface{}, fn func(interface{}, gjson.Result) error) JSON {
return jsonCheckOffInternal(wantKey, wantItems, true, mapper, fn)
}

// EXPERIMENTAL
// JSONCheckOff returns a matcher which will loop over `wantKey` and ensure that the items
// (which can be array elements or object keys)
// are present exactly once in any order in `wantItems`. If there are unexpected items or items
Expand All @@ -199,13 +208,14 @@ func JSONCheckOffAllowUnwanted(wantKey string, wantItems []interface{}, mapper f
// it's an object).
//
// Usage: (ensures `events` has these events in any order, with the right event type)
// JSONCheckOff("events", []interface{}{"$foo:bar", "$baz:quuz"}, func(r gjson.Result) interface{} {
// return r.Get("event_id").Str
// }, func(eventID interface{}, eventBody gjson.Result) error {
// if eventBody.Get("type").Str != "m.room.message" {
// return fmt.Errorf("expected event to be 'm.room.message'")
// }
// })
//
// JSONCheckOff("events", []interface{}{"$foo:bar", "$baz:quuz"}, func(r gjson.Result) interface{} {
// return r.Get("event_id").Str
// }, func(eventID interface{}, eventBody gjson.Result) error {
// if eventBody.Get("type").Str != "m.room.message" {
// return fmt.Errorf("expected event to be 'm.room.message'")
// }
// })
func JSONCheckOff(wantKey string, wantItems []interface{}, mapper func(gjson.Result) interface{}, fn func(interface{}, gjson.Result) error) JSON {
return jsonCheckOffInternal(wantKey, wantItems, false, mapper, fn)
}
Expand Down Expand Up @@ -256,6 +266,7 @@ func JSONMapEach(wantKey string, fn func(k, v gjson.Result) error) JSON {
}
}

// EXPERIMENTAL
// AnyOf takes 1 or more `checkers`, and builds a new checker which accepts a given
// json body iff it's accepted by at least one of the original `checkers`.
func AnyOf(checkers ...JSON) JSON {
Expand All @@ -281,3 +292,15 @@ func AnyOf(checkers ...JSON) JSON {
return fmt.Errorf(builder.String())
}
}

// jsonDeepEqual compares raw json with a json-serializable value, seeing if they're equal.
// It forces `gotJson` through a JSON parser to ensure keys/whitespace are identical to the marshalled form of `wantValue`.
func jsonDeepEqual(gotJson []byte, wantValue interface{}) bool {
// marshal what the test gave us
wantBytes, _ := json.Marshal(wantValue)
// re-marshal what the network gave us to acount for key ordering
var gotVal interface{}
_ = json.Unmarshal(gotJson, &gotVal)
gotBytes, _ := json.Marshal(gotVal)
return bytes.Equal(gotBytes, wantBytes)
}
Loading

0 comments on commit cc1aaaa

Please sign in to comment.