Skip to content

Commit 8f49ebc

Browse files
committed
Adding unit tests
1 parent 0052e5e commit 8f49ebc

1 file changed

Lines changed: 272 additions & 0 deletions

File tree

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
package io.temporal.client.nexus;
2+
3+
import com.google.protobuf.ByteString;
4+
import io.nexusrpc.handler.OperationHandler;
5+
import io.nexusrpc.handler.OperationImpl;
6+
import io.nexusrpc.handler.ServiceImpl;
7+
import io.temporal.api.common.v1.Payload;
8+
import io.temporal.api.nexus.v1.Endpoint;
9+
import io.temporal.api.nexus.v1.EndpointSpec;
10+
import io.temporal.api.nexus.v1.EndpointTarget;
11+
import io.temporal.api.operatorservice.v1.CreateNexusEndpointRequest;
12+
import io.temporal.api.operatorservice.v1.CreateNexusEndpointResponse;
13+
import io.temporal.api.operatorservice.v1.DeleteNexusEndpointRequest;
14+
import io.temporal.client.NexusClient;
15+
import io.temporal.client.NexusClientHandle;
16+
import io.temporal.client.NexusClientImpl;
17+
import io.temporal.client.NexusClientInterceptor;
18+
import io.temporal.client.NexusClientOperationExecutionDescription;
19+
import io.temporal.client.NexusClientOperationOptions;
20+
import io.temporal.testing.internal.SDKTestWorkflowRule;
21+
import io.temporal.workflow.shared.TestNexusServices;
22+
import io.temporal.workflow.shared.TestWorkflows;
23+
import java.time.Duration;
24+
import java.util.UUID;
25+
import org.junit.Assert;
26+
import org.junit.Rule;
27+
import org.junit.Test;
28+
29+
/**
30+
* Tests for {@link NexusClientHandle} per-execution lifecycle methods: {@code describe()}, {@code
31+
* cancel()}/{@code cancel(reason)}, and {@code terminate()}/{@code terminate(reason)}.
32+
*/
33+
public class NexusClientHandleTest {
34+
35+
@Rule
36+
public SDKTestWorkflowRule testWorkflowRule =
37+
SDKTestWorkflowRule.newBuilder()
38+
.setWorkflowTypes(PlaceholderWorkflowImpl.class)
39+
.setNexusServiceImplementation(new TestNexusServiceImpl())
40+
// Default is 10s; standalone Nexus dispatch + worker poll can take longer.
41+
.setTestTimeoutSeconds(120)
42+
.build();
43+
44+
private NexusClient createNexusClient() {
45+
return NexusClientImpl.newInstance(
46+
testWorkflowRule.getWorkflowServiceStubs(),
47+
NexusClientOperationOptions.newBuilder()
48+
.setNamespace(testWorkflowRule.getWorkflowClient().getOptions().getNamespace())
49+
.build());
50+
}
51+
52+
@Test
53+
public void describeReturnsDescriptionForStartedOperation() {
54+
StartedOperation started = startOperation();
55+
try {
56+
NexusClientHandle handle =
57+
started.client.getHandle(started.operationId, started.startOutput.getRunId());
58+
59+
NexusClientOperationExecutionDescription description = handle.describe();
60+
61+
Assert.assertNotNull(description);
62+
Assert.assertNotNull(description.getRunId());
63+
Assert.assertEquals(started.startOutput.getRunId(), description.getRunId());
64+
Assert.assertNotNull(description.getRawResponse());
65+
} finally {
66+
cleanup(started);
67+
}
68+
}
69+
70+
@Test
71+
public void describeWithoutRunIdTargetsLatest() {
72+
StartedOperation started = startOperation();
73+
try {
74+
// Handle with no pinned run ID — server should resolve to the latest run.
75+
NexusClientHandle handle = started.client.getHandle(started.operationId);
76+
77+
NexusClientOperationExecutionDescription description = handle.describe();
78+
79+
Assert.assertNotNull(description);
80+
Assert.assertEquals(started.startOutput.getRunId(), description.getRunId());
81+
} finally {
82+
cleanup(started);
83+
}
84+
}
85+
86+
@Test
87+
public void cancelSucceedsForStartedOperation() {
88+
StartedOperation started = startOperation();
89+
try {
90+
NexusClientHandle handle =
91+
started.client.getHandle(started.operationId, started.startOutput.getRunId());
92+
93+
handle.cancel();
94+
// No exception — server accepted the cancel request.
95+
} finally {
96+
cleanup(started);
97+
}
98+
}
99+
100+
@Test
101+
public void cancelWithReasonSucceedsForStartedOperation() {
102+
StartedOperation started = startOperation();
103+
try {
104+
NexusClientHandle handle =
105+
started.client.getHandle(started.operationId, started.startOutput.getRunId());
106+
107+
handle.cancel("test-cancel-reason");
108+
} finally {
109+
cleanup(started);
110+
}
111+
}
112+
113+
@Test
114+
public void cancelWithNullReasonSucceeds() {
115+
StartedOperation started = startOperation();
116+
try {
117+
NexusClientHandle handle =
118+
started.client.getHandle(started.operationId, started.startOutput.getRunId());
119+
120+
handle.cancel(null);
121+
} finally {
122+
cleanup(started);
123+
}
124+
}
125+
126+
@Test
127+
public void terminateSucceedsForStartedOperation() {
128+
StartedOperation started = startOperation();
129+
try {
130+
NexusClientHandle handle =
131+
started.client.getHandle(started.operationId, started.startOutput.getRunId());
132+
133+
handle.terminate();
134+
} finally {
135+
cleanup(started);
136+
}
137+
}
138+
139+
@Test
140+
public void terminateWithReasonSucceedsForStartedOperation() {
141+
StartedOperation started = startOperation();
142+
try {
143+
NexusClientHandle handle =
144+
started.client.getHandle(started.operationId, started.startOutput.getRunId());
145+
146+
handle.terminate("test-terminate-reason");
147+
} finally {
148+
cleanup(started);
149+
}
150+
}
151+
152+
@Test
153+
public void terminateWithNullReasonSucceeds() {
154+
StartedOperation started = startOperation();
155+
try {
156+
NexusClientHandle handle =
157+
started.client.getHandle(started.operationId, started.startOutput.getRunId());
158+
159+
handle.terminate(null);
160+
} finally {
161+
cleanup(started);
162+
}
163+
}
164+
165+
/** Holder for state used to drive a single test against one started operation. */
166+
private static final class StartedOperation {
167+
final NexusClient client;
168+
final Endpoint endpoint;
169+
final String operationId;
170+
final NexusClientInterceptor.StartNexusOperationExecutionOutput startOutput;
171+
172+
StartedOperation(
173+
NexusClient client,
174+
Endpoint endpoint,
175+
String operationId,
176+
NexusClientInterceptor.StartNexusOperationExecutionOutput startOutput) {
177+
this.client = client;
178+
this.endpoint = endpoint;
179+
this.operationId = operationId;
180+
this.startOutput = startOutput;
181+
}
182+
}
183+
184+
private StartedOperation startOperation() {
185+
NexusClient client = createNexusClient();
186+
Endpoint endpoint = createEndpoint("test-endpoint-" + testWorkflowRule.getTaskQueue());
187+
String operationId = "nexus-handle-test-" + UUID.randomUUID();
188+
189+
Payload inputPayload =
190+
testWorkflowRule
191+
.getWorkflowClient()
192+
.getOptions()
193+
.getDataConverter()
194+
.toPayload("ping-" + operationId)
195+
.orElseThrow(() -> new AssertionError("DataConverter returned no payload"));
196+
197+
NexusClientInterceptor.StartNexusOperationExecutionOutput startOutput =
198+
client.startNexusOperationExecution(
199+
new NexusClientInterceptor.StartNexusOperationExecutionInput(
200+
operationId,
201+
endpoint.getSpec().getName(),
202+
TestNexusServices.TestNexusService1.class.getSimpleName(),
203+
"operation",
204+
Duration.ofSeconds(30),
205+
inputPayload,
206+
/* searchAttributes= */ null,
207+
/* nexusHeader= */ null));
208+
209+
Assert.assertNotNull("expected start to return a run ID", startOutput.getRunId());
210+
return new StartedOperation(client, endpoint, operationId, startOutput);
211+
}
212+
213+
private void cleanup(StartedOperation started) {
214+
// Best-effort: server may reject delete depending on operation state.
215+
try {
216+
started.client.getHandle(started.operationId, started.startOutput.getRunId()).delete();
217+
} catch (RuntimeException ignored) {
218+
// ignored
219+
}
220+
deleteEndpoint(started.endpoint);
221+
}
222+
223+
private Endpoint createEndpoint(String name) {
224+
EndpointSpec spec =
225+
EndpointSpec.newBuilder()
226+
.setName(name)
227+
.setDescription(
228+
Payload.newBuilder().setData(ByteString.copyFromUtf8("test endpoint")).build())
229+
.setTarget(
230+
EndpointTarget.newBuilder()
231+
.setWorker(
232+
EndpointTarget.Worker.newBuilder()
233+
.setNamespace(testWorkflowRule.getTestEnvironment().getNamespace())
234+
.setTaskQueue(testWorkflowRule.getTaskQueue())))
235+
.build();
236+
CreateNexusEndpointResponse resp =
237+
testWorkflowRule
238+
.getTestEnvironment()
239+
.getOperatorServiceStubs()
240+
.blockingStub()
241+
.createNexusEndpoint(CreateNexusEndpointRequest.newBuilder().setSpec(spec).build());
242+
return resp.getEndpoint();
243+
}
244+
245+
private void deleteEndpoint(Endpoint endpoint) {
246+
testWorkflowRule
247+
.getTestEnvironment()
248+
.getOperatorServiceStubs()
249+
.blockingStub()
250+
.deleteNexusEndpoint(
251+
DeleteNexusEndpointRequest.newBuilder()
252+
.setId(endpoint.getId())
253+
.setVersion(endpoint.getVersion())
254+
.build());
255+
}
256+
257+
public static class PlaceholderWorkflowImpl implements TestWorkflows.TestWorkflow1 {
258+
@Override
259+
public String execute(String input) {
260+
return input;
261+
}
262+
}
263+
264+
@ServiceImpl(service = TestNexusServices.TestNexusService1.class)
265+
public static class TestNexusServiceImpl {
266+
@OperationImpl
267+
public OperationHandler<String, String> operation() {
268+
return OperationHandler.sync(
269+
(context, details, input) -> "echo:" + (input == null ? "<null>" : input));
270+
}
271+
}
272+
}

0 commit comments

Comments
 (0)