-
Notifications
You must be signed in to change notification settings - Fork 220
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for mocking nexus operations #1666
Conversation
This PR could use some tests and there would be an example in the documentation showing how this may be used. I want to understand the experience you had in mind better before diving deeper into the review. |
panic(fmt.Sprintf("mock of ExecuteNexusOperation failed to deserialize input")) | ||
} | ||
|
||
// rebuild the input as *nexus.LazyValue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because, after I call input.Consume
on L3208, it closed the reader, so it can't call again, which is needed if the user didn't mock the operation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, actually, I can see that the reader
isn't really being used at this moment, so maybe I could just skip this...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, you can avoid this for now since we control both implementations. Might want to just put a comment here but either way it's not critical.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather leave as it to avoid forgetting about this later if things change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, but would wait until @Quinn-With-Two-Ns takes a look
internal/workflow_testsuite.go
Outdated
// t.OnNexusOperation( | ||
// service, | ||
// HelloOperation, | ||
// HelloInput{Message: "Temporal"}, | ||
// mock.Anything, | ||
// ).Return( | ||
// &nexus.HandlerStartOperationResultAsync{ | ||
// OperationID: "hello-operation-id", | ||
// }, | ||
// nil, | ||
// ) | ||
// | ||
// t.RegisterNexusAsyncOperationCompletion( | ||
// service, | ||
// HelloOperation.Name(), | ||
// "hello-operation-id", | ||
// HelloOutput{Message: "Hello Temporal"}, | ||
// nil, | ||
// 1*time.Second, | ||
// ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition to this, maybe we could have helper functions to mock sync and async operations. Example: for async operation, we could have something like this:
func (e *TestWorkflowEnvironment) MockNexusAsyncOperation(
service any,
operation any,
input any,
options any,
result any,
err error,
delay time.Duration,
) *MockCallWrapper {
operationID := uuid.New() // maybe also input?
m := e.OnNexusOperation(service, operation, input, options).Return(
&nexus.HandlerStartOperationResultAsync{
OperationID: operationID,
},
nil,
)
e.RegisterNexusAsyncOperationCompletion(service, operation, operationID, result, err, delay)
return m
}
And we could also have something similar for the sync which basically abstract the returned value type nexus.HandlerStartOperationResultSync
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think this would be a more friendly interface. But I'm okay merging what we have for flexibility and opening an issue to track adding a variant as you suggested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, if a user sets After
on the returned MockCallWrapper
, it sort of makes the operation async already (with the exception of generating a separate started event).
Can you config After
worker with the MockCallWrapper
returned here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you can, but as you said, it would not represent the actual behavior of async operation.
I added some unit tests to check the After
is doing its job.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall LGTM. There's just one conversation to resolve and this would be good to merge AFAIC.
internal/workflow_testsuite.go
Outdated
// t.OnNexusOperation( | ||
// service, | ||
// HelloOperation, | ||
// HelloInput{Message: "Temporal"}, | ||
// mock.Anything, | ||
// ).Return( | ||
// &nexus.HandlerStartOperationResultAsync{ | ||
// OperationID: "hello-operation-id", | ||
// }, | ||
// nil, | ||
// ) | ||
// | ||
// t.RegisterNexusAsyncOperationCompletion( | ||
// service, | ||
// HelloOperation.Name(), | ||
// "hello-operation-id", | ||
// HelloOutput{Message: "Hello Temporal"}, | ||
// nil, | ||
// 1*time.Second, | ||
// ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think this would be a more friendly interface. But I'm okay merging what we have for flexibility and opening an issue to track adding a variant as you suggested.
internal/workflow_testsuite.go
Outdated
// t.OnNexusOperation( | ||
// service, | ||
// HelloOperation, | ||
// HelloInput{Message: "Temporal"}, | ||
// mock.Anything, | ||
// ).Return( | ||
// &nexus.HandlerStartOperationResultAsync{ | ||
// OperationID: "hello-operation-id", | ||
// }, | ||
// nil, | ||
// ) | ||
// | ||
// t.RegisterNexusAsyncOperationCompletion( | ||
// service, | ||
// HelloOperation.Name(), | ||
// "hello-operation-id", | ||
// HelloOutput{Message: "Hello Temporal"}, | ||
// nil, | ||
// 1*time.Second, | ||
// ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, if a user sets After
on the returned MockCallWrapper
, it sort of makes the operation async already (with the exception of generating a separate started event).
Can you config After
worker with the MockCallWrapper
returned here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this PR handles expectation failure , If I am wrong then could we add a test to verify.
@Quinn-With-Two-Ns I added |
if !(e.workflowMock.AssertCalled(dummyT, methodName, arguments...) || e.activityMock.AssertCalled(dummyT, methodName, arguments...)) { | ||
return e.workflowMock.AssertCalled(t, methodName, arguments...) && e.activityMock.AssertCalled(t, methodName, arguments...) | ||
} | ||
return true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this code is wrong, as well as AssertNotCalled
and AssertNumberOfCalls
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
L1003 checks if both workflowMock
and activityMock
was called. I think it was supposed to be if either of them was called. The other functions have similar mistakes.
return e | ||
} | ||
|
||
func (e *TestWorkflowEnvironment) SetOnNexusOperationCanceledListener( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: do we have a test for SetOnNexusOperationCanceledListener
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I'm not sure how to test it since the SDK itself doesn't support canceling a Nexus operation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can cancel a nexus operation from the SDK, the SDK can send a cancelation command from a workflow task.
@@ -1184,6 +1186,229 @@ func TestWorkflowTestSuite_NexusSyncOperation_ClientMethods_Panic(t *testing.T) | |||
require.Equal(t, "not implemented in the test environment", panicReason) | |||
} | |||
|
|||
func TestWorkflowTestSuite_MockNexusOperation(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I don't think we test mocking cancel?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! few pieces of test coverage missing but not blocking address if you can. Please point at a released version of the nexus Go SDK before you merge
} | ||
return true | ||
return e.AssertWorkflowCalled(dummyT, methodName, arguments...) || | ||
e.AssertActivityCalled(dummyT, methodName, arguments...) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are some of these called with dummyT
and some t
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a great question, and I have no idea. I just copied the existing calls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can just leave it as is, I will note I plan to deprecate these functions in the next minor release because they cause a lot of confusion for users since they awkwards combine workflow and activities so I wouldn't spend a lot of time fussing over them.
f6df7a0
to
3bb0bb9
Compare
* Support mocking Nexus operation with operation reference * address comments * address comments
606be16
to
509813d
Compare
* Support for mocking nexus operations * address comments * add tests * add nexus events listeners * add test for nexus listeners * address comments * add assert nexus calls methods * address static checks * Support mocking Nexus operation with operation reference (temporalio#1683) * Support mocking Nexus operation with operation reference * address comments * address comments
What was changed
Support for mocking nexus operations in the test framework.
The user can mock using either the
nexus.Operation
itself, or usingnexus.OperationReference
through thenexus.NewOperationReference
helper. See the docs in theOnNexusOperation
for description and example.Why?
Be able to mock nexus operation without needing to have access to the operation implementation itself or creating dummy operations.
Checklist
Closes
How was this tested: