From da3653ca92372b2303357c3990f7551e639b924f Mon Sep 17 00:00:00 2001 From: Alex Tsibulya Date: Tue, 27 Nov 2018 17:51:15 -0800 Subject: [PATCH] feat(api): rename Matcher and StringMatcher BREAKING CHANGE: the `StringMatcher` interface has been renamed to `Matcher`; the `Matcher` struct has been renamed to `StructMatcher` --- dsl/matcher.go | 50 ++++++++++++++++++++++----------------------- dsl/matcher_test.go | 22 ++++++++++---------- dsl/request.go | 10 ++++----- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/dsl/matcher.go b/dsl/matcher.go index 18f3d374f..a41c33eca 100644 --- a/dsl/matcher.go +++ b/dsl/matcher.go @@ -10,7 +10,7 @@ import ( "time" ) -// Matcher regexes +// Term Matcher regexes const ( hexadecimal = `[0-9a-fA-F]+` ipAddress = `(\d{1,3}\.)+\d{1,3}` @@ -97,7 +97,7 @@ type termMatcher struct { // EachLike specifies that a given element in a JSON body can be repeated // "minRequired" times. Number needs to be 1 or greater -func EachLike(content interface{}, minRequired int) StringMatcher { +func EachLike(content interface{}, minRequired int) Matcher { return eachLike{ Contents: content, Min: minRequired, @@ -106,7 +106,7 @@ func EachLike(content interface{}, minRequired int) StringMatcher { // Like specifies that the given content type should be matched based // on type (int, string etc.) instead of a verbatim match. -func Like(content interface{}) StringMatcher { +func Like(content interface{}) Matcher { return like{ Contents: content, } @@ -114,7 +114,7 @@ func Like(content interface{}) StringMatcher { // Term specifies that the matching should generate a value // and also match using a regular expression. -func Term(generate string, matcher string) StringMatcher { +func Term(generate string, matcher string) Matcher { return term{ Data: termData{ Generate: generate, @@ -128,12 +128,12 @@ func Term(generate string, matcher string) StringMatcher { } // HexValue defines a matcher that accepts hexidecimal values. -func HexValue() StringMatcher { +func HexValue() Matcher { return Regex("3F", hexadecimal) } // Identifier defines a matcher that accepts integer values. -func Identifier() StringMatcher { +func Identifier() Matcher { return Like(42) } @@ -141,7 +141,7 @@ func Identifier() StringMatcher { var Integer = Identifier // IPAddress defines a matcher that accepts valid IPv4 addresses. -func IPAddress() StringMatcher { +func IPAddress() Matcher { return Regex("127.0.0.1", ipAddress) } @@ -149,46 +149,46 @@ func IPAddress() StringMatcher { var IPv4Address = IPAddress // IPv6Address defines a matcher that accepts IP addresses. -func IPv6Address() StringMatcher { +func IPv6Address() Matcher { return Regex("::ffff:192.0.2.128", ipAddress) } // Decimal defines a matcher that accepts any decimal value. -func Decimal() StringMatcher { +func Decimal() Matcher { return Like(42.0) } // Timestamp matches a pattern corresponding to the ISO_DATETIME_FORMAT, which // is "yyyy-MM-dd'T'HH:mm:ss". The current date and time is used as the eaxmple. -func Timestamp() StringMatcher { +func Timestamp() Matcher { return Regex(timeExample.Format(time.RFC3339), timestamp) } // Date matches a pattern corresponding to the ISO_DATE_FORMAT, which // is "yyyy-MM-dd". The current date is used as the eaxmple. -func Date() StringMatcher { +func Date() Matcher { return Regex(timeExample.Format("2006-01-02"), date) } // Time matches a pattern corresponding to the ISO_DATE_FORMAT, which // is "'T'HH:mm:ss". The current tem is used as the eaxmple. -func Time() StringMatcher { +func Time() Matcher { return Regex(timeExample.Format("T15:04:05"), timeRegex) } // UUID defines a matcher that accepts UUIDs. Produces a v4 UUID as the example. -func UUID() StringMatcher { +func UUID() Matcher { return Regex("fc763eba-0905-41c5-a27f-3934ab26786c", uuid) } // Regex is a more appropriately named alias for the "Term" matcher var Regex = Term -// StringMatcher allows a string or Matcher to be provided in -// when matching with the DSL +// Matcher allows various implementations such String or StructMatcher +// to be provided in when matching with the DSL // We use the strategy outlined at http://www.jerf.org/iri/post/2917 // to create a "sum" or "union" type. -type StringMatcher interface { +type Matcher interface { // isMatcher is how we tell the compiler that strings // and other types are the same / allowed isMatcher() @@ -198,7 +198,7 @@ type StringMatcher interface { GetValue() interface{} } -// S is the string primitive wrapper (alias) for the StringMatcher type, +// S is the string primitive wrapper (alias) for the Matcher type, // it allows plain strings to be matched // To keep backwards compatible with previous versions // we aren't using an alias here @@ -224,21 +224,21 @@ func (s String) GetValue() interface{} { return s } -// Matcher matches a complex object structure, which may itself +// StructMatcher matches a complex object structure, which may itself // contain nested Matchers -type Matcher map[string]interface{} +type StructMatcher map[string]interface{} -func (m Matcher) isMatcher() {} +func (m StructMatcher) isMatcher() {} // GetValue returns the raw generated value for the matcher // without any of the matching detail context -func (m Matcher) GetValue() interface{} { +func (m StructMatcher) GetValue() interface{} { return nil } // MapMatcher allows a map[string]string-like object // to also contain complex matchers -type MapMatcher map[string]StringMatcher +type MapMatcher map[string]Matcher // Takes an object and converts it to a JSON representation func objectToString(obj interface{}) string { @@ -265,20 +265,20 @@ func objectToString(obj interface{}) string { // Supported Tag Formats // Minimum Slice Size: `pact:"min=2"` // String RegEx: `pact:"example=2000-01-01,regex=^\\d{4}-\\d{2}-\\d{2}$"` -func Match(src interface{}) StringMatcher { +func Match(src interface{}) Matcher { return match(reflect.TypeOf(src), getDefaults()) } // match recursively traverses the provided type and outputs a // matcher string for it that is compatible with the Pact dsl. -func match(srcType reflect.Type, params params) StringMatcher { +func match(srcType reflect.Type, params params) Matcher { switch kind := srcType.Kind(); kind { case reflect.Ptr: return match(srcType.Elem(), params) case reflect.Slice, reflect.Array: return EachLike(match(srcType.Elem(), getDefaults()), params.slice.min) case reflect.Struct: - result := Matcher{} + result := StructMatcher{} for i := 0; i < srcType.NumField(); i++ { field := srcType.Field(i) diff --git a/dsl/matcher_test.go b/dsl/matcher_test.go index f7a9c29cc..9bc6b1695 100644 --- a/dsl/matcher_test.go +++ b/dsl/matcher_test.go @@ -209,7 +209,7 @@ func TestMatcher_NestLikeInEachLike(t *testing.T) { "min": 1 }`) - match := formatJSON(EachLike(Matcher{ + match := formatJSON(EachLike(StructMatcher{ "id": Like(10), }, 1)) @@ -240,7 +240,7 @@ func TestMatcher_NestTermInEachLike(t *testing.T) { match := formatJSON( EachLike( - Matcher{ + StructMatcher{ "colour": Term("red", "red|green")}, 1)) @@ -315,10 +315,10 @@ func TestMatcher_NestAllTheThings(t *testing.T) { match := formatJSON( EachLike( EachLike( - Matcher{ + StructMatcher{ "colour": Term("red", "red|green|blue"), "size": Like(10), - "tag": EachLike([]StringMatcher{Like("jumper"), Like("shirt")}, 2), + "tag": EachLike([]Matcher{Like("jumper"), Like("shirt")}, 2), }, 1), 1)) @@ -344,7 +344,7 @@ func formatJSON(object interface{}) interface{} { return string(out.Bytes()) } -// Instrument the Matcher type to be able to assert the +// Instrument the StructMatcher type to be able to assert the // values and regexs contained within! func getMatcherValue(m interface{}) interface{} { mString := objectToString(m) @@ -369,7 +369,7 @@ func getMatcherValue(m interface{}) interface{} { func TestMatcher_SugarMatchers(t *testing.T) { type matcherTestCase struct { - matcher StringMatcher + matcher Matcher testCase func(val interface{}) error } matchers := map[string]matcherTestCase{ @@ -570,7 +570,7 @@ func TestMatch(t *testing.T) { tests := []struct { name string args args - want StringMatcher + want Matcher wantPanic bool }{ { @@ -599,7 +599,7 @@ func TestMatch(t *testing.T) { args: args{ src: wordDTO{}, }, - want: Matcher{ + want: StructMatcher{ "word": Like(`"string"`), "length": Like(1), }, @@ -609,7 +609,7 @@ func TestMatch(t *testing.T) { args: args{ src: dateDTO{}, }, - want: Matcher{ + want: StructMatcher{ "date": Term("2000-01-01", `^\\d{4}-\\d{2}-\\d{2}$`), }, }, @@ -618,7 +618,7 @@ func TestMatch(t *testing.T) { args: args{ src: wordsDTO{}, }, - want: Matcher{ + want: StructMatcher{ "words": EachLike(Like(`"string"`), 2), }, }, @@ -730,7 +730,7 @@ func TestMatch(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - var got StringMatcher + var got Matcher var didPanic bool defer func() { if rec := recover(); rec != nil { diff --git a/dsl/request.go b/dsl/request.go index 023ba0968..7776ea760 100644 --- a/dsl/request.go +++ b/dsl/request.go @@ -2,9 +2,9 @@ package dsl // Request is the default implementation of the Request interface. type Request struct { - Method string `json:"method"` - Path StringMatcher `json:"path"` - Query MapMatcher `json:"query,omitempty"` - Headers MapMatcher `json:"headers,omitempty"` - Body interface{} `json:"body,omitempty"` + Method string `json:"method"` + Path Matcher `json:"path"` + Query MapMatcher `json:"query,omitempty"` + Headers MapMatcher `json:"headers,omitempty"` + Body interface{} `json:"body,omitempty"` }