diff --git a/matchers/matcher.go b/matchers/matcher.go index c550ee5e2..f7a49455c 100644 --- a/matchers/matcher.go +++ b/matchers/matcher.go @@ -316,7 +316,11 @@ func match(srcType reflect.Type, params params) Matcher { for i := 0; i < srcType.NumField(); i++ { field := srcType.Field(i) - result[strings.Split(field.Tag.Get("json"), ",")[0]] = match(field.Type, pluckParams(field.Type, field.Tag.Get("pact"))) + fieldName := fieldNameByTagStrategy(field) + if fieldName == "" { + continue + } + result[fieldName] = match(field.Type, pluckParams(field.Type, field.Tag.Get("pact"))) } return result case reflect.String: @@ -349,6 +353,24 @@ func match(srcType reflect.Type, params params) Matcher { } } +func fieldNameByTagStrategy(field reflect.StructField) string { + var v string + var ok bool + if v, ok = field.Tag.Lookup("json"); ok { + return strings.Split(v, ",")[0] + } else if v, ok = field.Tag.Lookup("protobuf"); ok { + // parsing tag value like such. need to find spec for protobuf field tag + // "varint,1,opt,name=proto_with_json_tag,json=protoWithJsonTag,proto3" + arr := strings.Split(v, ",") + for i := 0; i < len(arr); i++ { + if strings.HasPrefix(arr[i], "json=") { + return strings.Split(arr[i], "=")[1] + } + } + } + return "" +} + // params are plucked from 'pact' struct tags as match() traverses // struct fields. They are passed back into match() along with their // associated type to serve as parameters for the dsl functions. diff --git a/matchers/matcher_test.go b/matchers/matcher_test.go index 923881522..de229315d 100644 --- a/matchers/matcher_test.go +++ b/matchers/matcher_test.go @@ -582,6 +582,21 @@ func TestMatch(t *testing.T) { Integer int `json:"integer" pact:"example=42"` Float float32 `json:"float" pact:"example=6.66"` } + // mixedDTO in order to reuse protoc-gen-go where structs are compatible with protobuf and json + type mixedDTO struct { + // has tag and should be in output + OnlyJsonTag string `json:"onlyJsonTag"` + // no tag, skip + NoTagString string + // no tag, skip - this covers case of proto compatible structs that contain func fields + NoTagFunc func() + BothUseJsonTag int32 `protobuf:"varint,1,opt,name=both_use_json_tag,json=bothNameFromProtobufTag,proto3" json:"bothNameFromJsonTag,omitempty"` + ProtoWithoutJsonTag *struct { + OnlyJsonTag string `json:"onlyJsonTagNested"` + // no tag, skip + NoTag func() + } `protobuf:"bytes,7,opt,name=proto_without_json_tag,json=onlyProtobufTag,proto3,oneof"` + } str := "str" type args struct { src interface{} @@ -774,6 +789,17 @@ func TestMatch(t *testing.T) { }, wantPanic: true, }, + { + name: "structs mixed for compatibility with proto3 and json types", + args: args{ + src: mixedDTO{}, + }, + want: StructMatcher{ + "onlyJsonTag": Like("string"), + "bothNameFromJsonTag": Like(1), + "onlyProtobufTag": StructMatcher{"onlyJsonTagNested": Like("string")}, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {