change require.protoEquals to support compare options#9937
change require.protoEquals to support compare options#9937fretz12 merged 10 commits intotemporalio:mainfrom
Conversation
|
|
||
| // ProtoEqualIgnoreFields compares two proto messages for equality, ignoring the specified fields on the given message | ||
| // type. Fields are specified by their proto name (snake_case). | ||
| func ProtoEqualIgnoreFields(t require.TestingT, a proto.Message, b proto.Message, msgType proto.Message, fields ...protoreflect.Name) { |
There was a problem hiding this comment.
Food for thought.
There's some other interesting options we can potentially expose:
protocmp.SortRepeatedFields
protocmp.IgnoreEnums
cmpopts.EquateApproxTime
Also another approach is to make this a really generic function:
func ProtoEqualWithOptions(t assert.TestingT, a proto.Message, b proto.Message, opts ...cmp.Option) bool
and it can be used as such:
protorequire.ProtoEqualWithOptions(t, expected, actual, protocmp.IgnoreFields(&activitypb.ActivityExecutionInfo{}, "state_transition_count"), cmpopts.EquateApproxTime(5*time.Second), )
However, that leaks proto/cmp abstractions making the helper harder to use. I decided to make the helper focused on ignore fields, as that's our majority use case and the interface is very clean. I propose for other useful options, like EquateApproxTime, we create separate helpers. But open to thoughts
Replace the standalone ProtoEqualIgnoreFields function with an options pattern on ProtoEqual. The new IgnoreFields option infers the proto message type automatically, making call sites cleaner and the API extensible to future comparison options.
…nto fredtzeng/proto-equals-ignore
| } | ||
|
|
||
| // Option configures how proto comparison behaves. | ||
| type Option func(msg proto.Message, cfg *config) |
There was a problem hiding this comment.
By not exporting config all of the future Option definitions have to be here, which removes the ability to have a one-off Option for a specific test. Thoughts on exporting?
There was a problem hiding this comment.
I could see that case. Ideally, I don't want to leak these configs. What if we do an exported config helper:
func WithCmpOptions(opts ...cmp.Option) Option {
return func(_ proto.Message, cfg *config) {
cfg.cmpOpts = append(cfg.cmpOpts, opts...)
}
}
And then call it like:
protorequire.ProtoEqual(t, a, b,
protorequire.WithCmpOptions(protocmp.SortRepeatedFields(msg, "items")),
)
wdyt?
There was a problem hiding this comment.
+1 on leaking less than more, I think it's also ok to punt on how to expose one-off option to the future as well when there's a use case
There was a problem hiding this comment.
Agree with @long-nt-tran , we can defer to when we have a usecase
| t.Fatal("expected comparison to fail when not all differing fields are ignored") | ||
| } | ||
| }) | ||
| } |
There was a problem hiding this comment.
Looks like the negative test's expected failure is leaking as the suite failure. We might need a mock for require.testingT interface, i.e.,
type mockT struct {
failed bool
}
func (m *mockT) Errorf(format string, args ...interface{}) {
m.failed = true
}
func (m *mockT) FailNow() {
m.failed = true
}
then this test can be
t.Run("partial ignore still fails", func(t *testing.T) {
mt := &mockT{}
protorequire.ProtoEqual(mt, a, b,
protorequire.IgnoreFields(
"status",
),
)
if !mt.failed {
t.Fatal("expected comparison to fail when not all differing fields are ignored")
}
})
There was a problem hiding this comment.
good call.. changed
long-nt-tran
left a comment
There was a problem hiding this comment.
LGTM, thanks for adding this!
| } | ||
|
|
||
| // Option configures how proto comparison behaves. | ||
| type Option func(msg proto.Message, cfg *config) |
There was a problem hiding this comment.
+1 on leaking less than more, I think it's also ok to punt on how to expose one-off option to the future as well when there's a use case
What changed?
Added ProtoEqualIgnoreFields to protoassert and protorequire packages. Refactored 3 call sites in standalone_activity_test.go to use the new helper.
Why?
Comparing proto messages while ignoring specific fields required a verbose pattern (cmp.Diff + protocmp.Transform() + protocmp.IgnoreFields() + require.Empty). The new option extends ProtoEqual with a clean call site that infers the message type automatically. The Option func type and config struct are designed to support additional comparison options in the future without changing the ProtoEqual signature.
How did you test it?