Skip to content

Commit

Permalink
feat: add support to MatchV2 structs generated by proto-gen-go
Browse files Browse the repository at this point in the history
allows for reuse of structs generated by the client code for reuse
with protobuf or json.   the protobuf portion of the struct def
was not compatible with the MatchV2 due to some field types not supported,
and in some cases the json information is only an attribute of protobuf tag.
this enhancement opts for 1. json tag. 2. protobuf tag. 3. skips field
  • Loading branch information
hborham committed Jan 20, 2023
1 parent 6e805a5 commit 0eb0a27
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 1 deletion.
24 changes: 23 additions & 1 deletion matchers/matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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.
Expand Down
26 changes: 26 additions & 0 deletions matchers/matcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 0eb0a27

Please sign in to comment.