From 47da04ec770dac61fb1c808acf822de1f160b5a2 Mon Sep 17 00:00:00 2001 From: Evan Reynolds Date: Wed, 18 Mar 2026 15:53:37 -0700 Subject: [PATCH 1/7] Code samples for testing and mocking Nexus --- .../samples/nexus/handler/EchoHandler.java | 7 ++ .../nexus/handler/EchoHandlerImpl.java | 12 +++ .../nexus/handler/NexusServiceImpl.java | 26 ++++-- .../caller/CallerWorkflowJunit5MockTest.java | 74 ++++++++++++++++ .../caller/CallerWorkflowJunit5Test.java | 53 +++++++++++ .../nexus/caller/CallerWorkflowMockTest.java | 88 +++++++++++++++++++ .../nexus/caller/CallerWorkflowTest.java | 40 ++++----- 7 files changed, 274 insertions(+), 26 deletions(-) create mode 100644 core/src/main/java/io/temporal/samples/nexus/handler/EchoHandler.java create mode 100644 core/src/main/java/io/temporal/samples/nexus/handler/EchoHandlerImpl.java create mode 100644 core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java create mode 100644 core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5Test.java create mode 100644 core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowMockTest.java diff --git a/core/src/main/java/io/temporal/samples/nexus/handler/EchoHandler.java b/core/src/main/java/io/temporal/samples/nexus/handler/EchoHandler.java new file mode 100644 index 000000000..eee147cc5 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus/handler/EchoHandler.java @@ -0,0 +1,7 @@ +package io.temporal.samples.nexus.handler; + +import io.temporal.samples.nexus.service.NexusService; + +public interface EchoHandler { + NexusService.EchoOutput echo(NexusService.EchoInput input); +} diff --git a/core/src/main/java/io/temporal/samples/nexus/handler/EchoHandlerImpl.java b/core/src/main/java/io/temporal/samples/nexus/handler/EchoHandlerImpl.java new file mode 100644 index 000000000..191309b09 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus/handler/EchoHandlerImpl.java @@ -0,0 +1,12 @@ +package io.temporal.samples.nexus.handler; + +import io.temporal.samples.nexus.service.NexusService; + +// Note that this is a class, not a Temporal worker. This is to demonstrate that Nexus services can +// simply call a class instead of a worker for fast operations that don't need retry handling. +public class EchoHandlerImpl implements EchoHandler { + @Override + public NexusService.EchoOutput echo(NexusService.EchoInput input) { + return new NexusService.EchoOutput(input.getMessage()); + } +} diff --git a/core/src/main/java/io/temporal/samples/nexus/handler/NexusServiceImpl.java b/core/src/main/java/io/temporal/samples/nexus/handler/NexusServiceImpl.java index 2344f27ec..8bb17d215 100644 --- a/core/src/main/java/io/temporal/samples/nexus/handler/NexusServiceImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus/handler/NexusServiceImpl.java @@ -13,16 +13,32 @@ // return OperationHandler that correspond to the operations defined in the service interface. @ServiceImpl(service = NexusService.class) public class NexusServiceImpl { + private final EchoHandler echoHandler; + + // The injected EchoHandler makes this class unit-testable. + // The no-arg constructor provides a default; the second allows tests to inject a mock. + // If you are not using the sync call or do not need to mock a handler, then you will not + // need this constructor pairing. + public NexusServiceImpl() { + this(new EchoHandlerImpl()); + } + + public NexusServiceImpl(EchoHandler echoHandler) { + this.echoHandler = echoHandler; + } + + // The Echo Nexus Service exemplifies making a synchronous call using OperationHandler.sync. + // In this case, it is calling the EchoHandler class - not a workflow - and simply returning the + // result. @OperationImpl public OperationHandler echo() { - // OperationHandler.sync is a meant for exposing simple RPC handlers. return OperationHandler.sync( // The method is for making arbitrary short calls to other services or databases, or // perform simple computations such as this one. Users can also access a workflow client by // calling // Nexus.getOperationContext().getWorkflowClient(ctx) to make arbitrary calls such as // signaling, querying, or listing workflows. - (ctx, details, input) -> new NexusService.EchoOutput(input.getMessage())); + (ctx, details, input) -> echoHandler.echo(input)); } @OperationImpl @@ -39,10 +55,8 @@ public OperationHandler hello .newWorkflowStub( HelloHandlerWorkflow.class, // Workflow IDs should typically be business meaningful IDs and are used to - // dedupe workflow starts. - // For this example, we're using the request ID allocated by Temporal when - // the - // caller workflow schedules + // dedupe workflow starts. For this example, we're using the request ID + // allocated by Temporal when the caller workflow schedules // the operation, this ID is guaranteed to be stable across retries of this // operation. // diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java new file mode 100644 index 000000000..6fc3691f5 --- /dev/null +++ b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java @@ -0,0 +1,74 @@ +package io.temporal.samples.nexus.caller; + +import io.temporal.samples.nexus.handler.EchoHandler; +import io.temporal.samples.nexus.handler.HelloHandlerWorkflow; +import io.temporal.samples.nexus.handler.NexusServiceImpl; +import io.temporal.samples.nexus.service.NexusService; +import io.temporal.testing.TestWorkflowEnvironment; +import io.temporal.testing.TestWorkflowExtension; +import io.temporal.worker.Worker; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +public class CallerWorkflowJunit5MockTest { + + // Sync Nexus operations run inline in the handler thread β€” there is no backing workflow to + // register a factory for. To mock one, inject a mock dependency into the service implementation. + private static final EchoHandler mockEchoHandler = mock(EchoHandler.class); + + @RegisterExtension + public static final TestWorkflowExtension testWorkflowExtension = + TestWorkflowExtension.newBuilder() + // If a Nexus service is registered as part of the test as in the following line of code, + // the TestWorkflowExtension will, by default, automatically create a Nexus service endpoint + // and workflows registered as part of the TestWorkflowExtension will + // automatically inherit the endpoint if none is set. + .setNexusServiceImplementation(new NexusServiceImpl(mockEchoHandler)) + // The Echo Nexus handler service just makes a call to a class, so no extra setup is + // needed. But the Hello Nexus service needs a worker for both the caller and handler + // in order to run, and the Echo Nexus caller service needs a worker. + // + // registerWorkflowImplementationTypes will take the classes given and create workers for them, + // enabling workflows to run. + .registerWorkflowImplementationTypes( + HelloCallerWorkflowImpl.class, EchoCallerWorkflowImpl.class) + .setDoNotStart(true) + .build(); + + @Test + public void testHelloWorkflow(TestWorkflowEnvironment testEnv, Worker worker, HelloCallerWorkflow workflow) { + // Workflows started by a Nexus service can be mocked just like any other workflow + worker.registerWorkflowImplementationFactory( + HelloHandlerWorkflow.class, + () -> { + HelloHandlerWorkflow mockHandler = mock(HelloHandlerWorkflow.class); + when(mockHandler.hello(any())) + .thenReturn(new NexusService.HelloOutput("Hello Mock World πŸ‘‹")); + return mockHandler; + }); + testEnv.start(); + + // Execute a workflow waiting for it to complete. + String greeting = workflow.hello("World", NexusService.Language.EN); + assertEquals("Hello Mock World πŸ‘‹", greeting); + + testEnv.shutdown(); + } + + @Test + public void testEchoWorkflow(TestWorkflowEnvironment testEnv, Worker worker, EchoCallerWorkflow workflow) { + // Sync Nexus operations run inline in the handler thread β€” there is no backing workflow to + // register a factory for. Instead, stub the injected EchoHandler dependency directly. + when(mockEchoHandler.echo(any())).thenReturn(new NexusService.EchoOutput("mocked echo")); + testEnv.start(); + + // Execute a workflow waiting for it to complete. + String greeting = workflow.echo("Hello"); + assertEquals("mocked echo", greeting); + + testEnv.shutdown(); + } +} diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5Test.java b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5Test.java new file mode 100644 index 000000000..1dae575c8 --- /dev/null +++ b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5Test.java @@ -0,0 +1,53 @@ +package io.temporal.samples.nexus.caller; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import io.temporal.samples.nexus.handler.HelloHandlerWorkflowImpl; +import io.temporal.samples.nexus.handler.NexusServiceImpl; +import io.temporal.samples.nexus.service.NexusService; +import io.temporal.testing.TestWorkflowEnvironment; +import io.temporal.testing.TestWorkflowExtension; +import io.temporal.worker.Worker; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class CallerWorkflowJunit5Test { + + @RegisterExtension + public static final TestWorkflowExtension testWorkflowExtension = + TestWorkflowExtension.newBuilder() + // If a Nexus service is registered as part of the test as in the following line of code, + // the TestWorkflowExtension will, by default, automatically create a Nexus service endpoint + // and workflows registered as part of the TestWorkflowExtension will + // automatically inherit the endpoint if none is set. + .setNexusServiceImplementation(new NexusServiceImpl()) + // The Echo Nexus handler service just makes a call to a class, so no extra setup is + // needed. But the Hello Nexus service needs a worker for both the caller and handler + // in order to run, and the Echo Nexus caller service needs a worker. + // + // registerWorkflowImplementationTypes will take the classes given and create workers for them, + // enabling workflows to run. + .registerWorkflowImplementationTypes( + HelloCallerWorkflowImpl.class, + HelloHandlerWorkflowImpl.class, + EchoCallerWorkflowImpl.class) + // The workflow will start before each test, and will shut down after each test. + // See CallerWorkflowTest for an example of how to control this differently if needed. + .build(); + + // The TestWorkflowExtension extension in the Temporal testing library creates the + // arguments to the test cases and initializes them from the extension setup call above. + @Test + public void testHelloWorkflow(TestWorkflowEnvironment testEnv, Worker worker, HelloCallerWorkflow workflow) { + // Execute a workflow waiting for it to complete. + String greeting = workflow.hello("World", NexusService.Language.EN); + assertEquals("Hello World πŸ‘‹", greeting); + } + + @Test + public void testEchoWorkflow(TestWorkflowEnvironment testEnv, Worker worker, EchoCallerWorkflow workflow) { + // Execute a workflow waiting for it to complete. + String greeting = workflow.echo("Hello"); + assertEquals("Hello", greeting); + } +} diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowMockTest.java b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowMockTest.java new file mode 100644 index 000000000..405b48f77 --- /dev/null +++ b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowMockTest.java @@ -0,0 +1,88 @@ +package io.temporal.samples.nexus.caller; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.temporal.client.WorkflowOptions; +import io.temporal.samples.nexus.handler.EchoHandler; +import io.temporal.samples.nexus.handler.HelloHandlerWorkflow; +import io.temporal.samples.nexus.handler.NexusServiceImpl; +import io.temporal.samples.nexus.service.NexusService; +import io.temporal.testing.TestWorkflowRule; +import org.junit.Rule; +import org.junit.Test; + +public class CallerWorkflowMockTest { + + // Inject a mock EchoHandler so sync Nexus operations can be stubbed per test. + // JUnit 4 creates a new test class instance per test method, so this mock is fresh each time. + private final EchoHandler mockEchoHandler = mock(EchoHandler.class); + + @Rule + public TestWorkflowRule testWorkflowRule = + TestWorkflowRule.newBuilder() + // If a Nexus service is registered as part of the test as in the following line of code, + // the TestWorkflowRule will, by default, automatically create a Nexus service endpoint + // and workflows registered as part of the TestWorkflowRule + // will automatically inherit the endpoint if none is set. + .setNexusServiceImplementation(new NexusServiceImpl(mockEchoHandler)) + // The Echo Nexus handler service just makes a call to a class, so no extra setup is + // needed. But the Hello Nexus service needs a worker for both the caller and handler + // in order to run. + // setWorkflowTypes will take the classes given and create workers for them, enabling + // workflows to run. This creates caller workflows, the handler workflows + // will be mocked in the test methods. + .setWorkflowTypes(HelloCallerWorkflowImpl.class, EchoCallerWorkflowImpl.class) + // Disable automatic worker startup as we are going to register some workflows manually + // per test + .setDoNotStart(true) + .build(); + + @Test + public void testHelloWorkflow() { + testWorkflowRule + .getWorker() + // Workflows started by a Nexus service can be mocked just like any other workflow + .registerWorkflowImplementationFactory( + HelloHandlerWorkflow.class, + () -> { + HelloHandlerWorkflow wf = mock(HelloHandlerWorkflow.class); + when(wf.hello(any())).thenReturn(new NexusService.HelloOutput("Hello Mock World πŸ‘‹")); + return wf; + }); + testWorkflowRule.getTestEnvironment().start(); + + // Now create the caller workflow + HelloCallerWorkflow workflow = + testWorkflowRule + .getWorkflowClient() + .newWorkflowStub( + HelloCallerWorkflow.class, + WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build()); + String greeting = workflow.hello("World", NexusService.Language.EN); + assertEquals("Hello Mock World πŸ‘‹", greeting); + + testWorkflowRule.getTestEnvironment().shutdown(); + } + + @Test + public void testEchoWorkflow() { + // Sync Nexus operations run inline in the handler thread β€” there is no backing workflow to + // register a factory for. Instead, stub the injected EchoHandler dependency directly. + when(mockEchoHandler.echo(any())).thenReturn(new NexusService.EchoOutput("mocked echo")); + testWorkflowRule.getTestEnvironment().start(); + + EchoCallerWorkflow workflow = + testWorkflowRule + .getWorkflowClient() + .newWorkflowStub( + EchoCallerWorkflow.class, + WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build()); + String greeting = workflow.echo("Hello"); + assertEquals("mocked echo", greeting); + + testWorkflowRule.getTestEnvironment().shutdown(); + } +} diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowTest.java b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowTest.java index 2d3a6e42a..683aff32f 100644 --- a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowTest.java +++ b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowTest.java @@ -1,12 +1,9 @@ package io.temporal.samples.nexus.caller; import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import io.temporal.client.WorkflowOptions; -import io.temporal.samples.nexus.handler.HelloHandlerWorkflow; +import io.temporal.samples.nexus.handler.HelloHandlerWorkflowImpl; import io.temporal.samples.nexus.handler.NexusServiceImpl; import io.temporal.samples.nexus.service.NexusService; import io.temporal.testing.TestWorkflowRule; @@ -21,11 +18,19 @@ public class CallerWorkflowTest { @Rule public TestWorkflowRule testWorkflowRule = TestWorkflowRule.newBuilder() - // If a Nexus service is registered as part of the test, the TestWorkflowRule will ,by - // default, automatically create a Nexus service endpoint and workflows registered as part - // of the TestWorkflowRule will automatically inherit the endpoint if none is set. + // If a Nexus service is registered as part of the test as in the following line of code, + // the TestWorkflowRule will, by default, automatically create a Nexus service endpoint + // and workflows registered as part of the TestWorkflowRule + // will automatically inherit the endpoint if none is set. .setNexusServiceImplementation(new NexusServiceImpl()) - .setWorkflowTypes(HelloCallerWorkflowImpl.class) + // The Echo Nexus handler service just makes a call to a class, so no extra setup is + // needed. But the Hello Nexus service needs a worker for both the caller and handler + // in order to run. + // setWorkflowTypes will take the classes given and create workers for them, enabling + // workflows to run. This is not adding an EchoCallerWorkflow though - + // see the testEchoWorkflow test method below for an example of an alternate way + // to supply a worker that gives you more flexibility if needed. + .setWorkflowTypes(HelloCallerWorkflowImpl.class, HelloHandlerWorkflowImpl.class) // Disable automatic worker startup as we are going to register some workflows manually // per test .setDoNotStart(true) @@ -33,16 +38,6 @@ public class CallerWorkflowTest { @Test public void testHelloWorkflow() { - testWorkflowRule - .getWorker() - // Workflows started by a Nexus service can be mocked just like any other workflow - .registerWorkflowImplementationFactory( - HelloHandlerWorkflow.class, - () -> { - HelloHandlerWorkflow wf = mock(HelloHandlerWorkflow.class); - when(wf.hello(any())).thenReturn(new NexusService.HelloOutput("Hello World πŸ‘‹")); - return wf; - }); testWorkflowRule.getTestEnvironment().start(); HelloCallerWorkflow workflow = @@ -61,8 +56,13 @@ public void testHelloWorkflow() { public void testEchoWorkflow() { // If Workflows are registered later than the endpoint can be set manually // either by setting the endpoint in the NexusServiceOptions in the Workflow implementation or - // by setting the NexusServiceOptions on the WorkflowImplementationOptions when registering the - // Workflow. + // by setting the NexusServiceOptions on the WorkflowImplementationOptions when registering + // the Workflow. To demonstrate, this is creating the Nexus service for Echo, + // and registering a EchoCallerWorkflowImpl worker. + // + // It is much simpler to use the setWorkflowTypes in the rule definition above - and as + // this isn't easily do-able in JUnit5 (the nexus endpoint isn't exposed) should be + // used with caution. testWorkflowRule .getWorker() .registerWorkflowImplementationTypes( From 70c21bbb122a929819251082131e6f80ab0e95ba Mon Sep 17 00:00:00 2001 From: Evan Reynolds Date: Thu, 19 Mar 2026 15:30:21 -0700 Subject: [PATCH 2/7] Formatting changes from Spotless --- .../caller/CallerWorkflowJunit5MockTest.java | 20 ++++++++++--------- .../caller/CallerWorkflowJunit5Test.java | 14 +++++++------ 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java index 6fc3691f5..f19a4fe65 100644 --- a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java +++ b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java @@ -1,5 +1,8 @@ package io.temporal.samples.nexus.caller; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + import io.temporal.samples.nexus.handler.EchoHandler; import io.temporal.samples.nexus.handler.HelloHandlerWorkflow; import io.temporal.samples.nexus.handler.NexusServiceImpl; @@ -10,9 +13,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.*; - public class CallerWorkflowJunit5MockTest { // Sync Nexus operations run inline in the handler thread β€” there is no backing workflow to @@ -23,23 +23,24 @@ public class CallerWorkflowJunit5MockTest { public static final TestWorkflowExtension testWorkflowExtension = TestWorkflowExtension.newBuilder() // If a Nexus service is registered as part of the test as in the following line of code, - // the TestWorkflowExtension will, by default, automatically create a Nexus service endpoint - // and workflows registered as part of the TestWorkflowExtension will + // the TestWorkflowExtension will, by default, automatically create a Nexus service + // endpoint and workflows registered as part of the TestWorkflowExtension will // automatically inherit the endpoint if none is set. .setNexusServiceImplementation(new NexusServiceImpl(mockEchoHandler)) // The Echo Nexus handler service just makes a call to a class, so no extra setup is // needed. But the Hello Nexus service needs a worker for both the caller and handler // in order to run, and the Echo Nexus caller service needs a worker. // - // registerWorkflowImplementationTypes will take the classes given and create workers for them, - // enabling workflows to run. + // registerWorkflowImplementationTypes will take the classes given and create workers for + // them, enabling workflows to run. .registerWorkflowImplementationTypes( HelloCallerWorkflowImpl.class, EchoCallerWorkflowImpl.class) .setDoNotStart(true) .build(); @Test - public void testHelloWorkflow(TestWorkflowEnvironment testEnv, Worker worker, HelloCallerWorkflow workflow) { + public void testHelloWorkflow( + TestWorkflowEnvironment testEnv, Worker worker, HelloCallerWorkflow workflow) { // Workflows started by a Nexus service can be mocked just like any other workflow worker.registerWorkflowImplementationFactory( HelloHandlerWorkflow.class, @@ -59,7 +60,8 @@ public void testHelloWorkflow(TestWorkflowEnvironment testEnv, Worker worker, He } @Test - public void testEchoWorkflow(TestWorkflowEnvironment testEnv, Worker worker, EchoCallerWorkflow workflow) { + public void testEchoWorkflow( + TestWorkflowEnvironment testEnv, Worker worker, EchoCallerWorkflow workflow) { // Sync Nexus operations run inline in the handler thread β€” there is no backing workflow to // register a factory for. Instead, stub the injected EchoHandler dependency directly. when(mockEchoHandler.echo(any())).thenReturn(new NexusService.EchoOutput("mocked echo")); diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5Test.java b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5Test.java index 1dae575c8..c7e32335a 100644 --- a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5Test.java +++ b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5Test.java @@ -17,16 +17,16 @@ public class CallerWorkflowJunit5Test { public static final TestWorkflowExtension testWorkflowExtension = TestWorkflowExtension.newBuilder() // If a Nexus service is registered as part of the test as in the following line of code, - // the TestWorkflowExtension will, by default, automatically create a Nexus service endpoint - // and workflows registered as part of the TestWorkflowExtension will + // the TestWorkflowExtension will, by default, automatically create a Nexus service + // endpoint and workflows registered as part of the TestWorkflowExtension will // automatically inherit the endpoint if none is set. .setNexusServiceImplementation(new NexusServiceImpl()) // The Echo Nexus handler service just makes a call to a class, so no extra setup is // needed. But the Hello Nexus service needs a worker for both the caller and handler // in order to run, and the Echo Nexus caller service needs a worker. // - // registerWorkflowImplementationTypes will take the classes given and create workers for them, - // enabling workflows to run. + // registerWorkflowImplementationTypes will take the classes given and create workers for + // them, enabling workflows to run. .registerWorkflowImplementationTypes( HelloCallerWorkflowImpl.class, HelloHandlerWorkflowImpl.class, @@ -38,14 +38,16 @@ public class CallerWorkflowJunit5Test { // The TestWorkflowExtension extension in the Temporal testing library creates the // arguments to the test cases and initializes them from the extension setup call above. @Test - public void testHelloWorkflow(TestWorkflowEnvironment testEnv, Worker worker, HelloCallerWorkflow workflow) { + public void testHelloWorkflow( + TestWorkflowEnvironment testEnv, Worker worker, HelloCallerWorkflow workflow) { // Execute a workflow waiting for it to complete. String greeting = workflow.hello("World", NexusService.Language.EN); assertEquals("Hello World πŸ‘‹", greeting); } @Test - public void testEchoWorkflow(TestWorkflowEnvironment testEnv, Worker worker, EchoCallerWorkflow workflow) { + public void testEchoWorkflow( + TestWorkflowEnvironment testEnv, Worker worker, EchoCallerWorkflow workflow) { // Execute a workflow waiting for it to complete. String greeting = workflow.echo("Hello"); assertEquals("Hello", greeting); From 66487a90a7ff48ee0551c669aa3f58d501f7f28b Mon Sep 17 00:00:00 2001 From: Evan Reynolds Date: Mon, 23 Mar 2026 11:12:00 -0700 Subject: [PATCH 3/7] Added two more classes that mock the Nexus Service itself --- .../caller/CallerWorkflowJunit5MockTest.java | 3 + .../caller/CallerWorkflowJunit5Test.java | 3 + .../nexus/caller/CallerWorkflowMockTest.java | 3 + .../nexus/caller/CallerWorkflowTest.java | 3 + .../nexus/caller/NexusServiceJunit5Test.java | 99 +++++++++++++++ .../nexus/caller/NexusServiceMockTest.java | 115 ++++++++++++++++++ 6 files changed, 226 insertions(+) create mode 100644 core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceJunit5Test.java create mode 100644 core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceMockTest.java diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java index f19a4fe65..423e0ee16 100644 --- a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java +++ b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java @@ -13,6 +13,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +// This is an example of how to unit test Nexus services in JUnit5. The handlers are mocked, +// so that the caller classes interact with the mocks and not the handler classes themselves. + public class CallerWorkflowJunit5MockTest { // Sync Nexus operations run inline in the handler thread β€” there is no backing workflow to diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5Test.java b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5Test.java index c7e32335a..dc1ee0647 100644 --- a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5Test.java +++ b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5Test.java @@ -11,6 +11,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +// This is an example of how to unit test Nexus services in JUnit5. The handlers are not mocked, +// but are actually called by the testing framework by the caller classes. + public class CallerWorkflowJunit5Test { @RegisterExtension diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowMockTest.java b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowMockTest.java index 405b48f77..83520c13c 100644 --- a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowMockTest.java +++ b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowMockTest.java @@ -14,6 +14,9 @@ import org.junit.Rule; import org.junit.Test; +// This is an example of how to unit test Nexus services in JUnit4. The handlers are mocked, +// so that the caller classes interact with the mocks and not the handler classes themselves. + public class CallerWorkflowMockTest { // Inject a mock EchoHandler so sync Nexus operations can be stubbed per test. diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowTest.java b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowTest.java index 683aff32f..6023462df 100644 --- a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowTest.java +++ b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowTest.java @@ -13,6 +13,9 @@ import org.junit.Rule; import org.junit.Test; +// This is an example of how to unit test Nexus services in JUnit4. The handlers are not mocked, +// but are actually called by the testing framework by the caller classes. + public class CallerWorkflowTest { @Rule diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceJunit5Test.java b/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceJunit5Test.java new file mode 100644 index 000000000..8a1861031 --- /dev/null +++ b/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceJunit5Test.java @@ -0,0 +1,99 @@ +package io.temporal.samples.nexus.caller; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.nexusrpc.handler.OperationHandler; +import io.temporal.samples.nexus.handler.NexusServiceImpl; +import io.temporal.samples.nexus.service.NexusService; +import io.temporal.testing.TestWorkflowEnvironment; +import io.temporal.testing.TestWorkflowExtension; +import io.temporal.worker.Worker; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +// This unit test example shows how to mock the Nexus service itself in JUnit4. +// In this example since the NexusService itself is mocked, no handlers need to be set up or mocked. + +public class NexusServiceJunit5Test { + + private final NexusServiceImpl mockNexusService = createMockNexusService(); + + // Mutable fields β€” set these in each test method before starting the environment + // to mock the return values for the Nexus service. + // Instance fields (not static) so each test gets its own copy; safe for parallel execution. + private NexusService.EchoOutput echoResult = new NexusService.EchoOutput("default"); + private NexusService.HelloOutput helloResult = new NexusService.HelloOutput("default"); + + private NexusServiceImpl createMockNexusService() { + NexusServiceImpl mock = mock(NexusServiceImpl.class); + // Using OperationHandler.sync for both operations bypasses the need for a backing workflow, + // returning results inline just like a synchronous call. Mocks need to be done before the rule + // is defined, as creating the rule will fail if either call is still null. + // + // The following is the simplest - just mock the services to return a value. But then these + // values cannot change per test case, so this will not always suffice. + // when(mock.echo()) + // .thenReturn( + // OperationHandler.sync( + // (ctx, details, input) -> new NexusService.EchoOutput("mocked echo"))); + // when(mock.hello()) + // .thenReturn( + // OperationHandler.sync( + // (ctx, details, input) -> new NexusService.HelloOutput("Hello Mock World + // πŸ‘‹"))); + // + // An alternative approach is to create the mocks but set them to return a value that is set in + // the unit + // test class above. That allows you to change the return value in each test. + when(mock.echo()).thenReturn(OperationHandler.sync((ctx, details, input) -> echoResult)); + when(mock.hello()).thenReturn(OperationHandler.sync((ctx, details, input) -> helloResult)); + + return mock; + } + + @RegisterExtension + public final TestWorkflowExtension testWorkflowExtension = + TestWorkflowExtension.newBuilder() + // If a Nexus service is registered as part of the test as in the following line of code, + // the TestWorkflowExtension will, by default, automatically create a Nexus service + // endpoint and workflows registered as part of the TestWorkflowExtension will + // automatically inherit the endpoint if none is set. + .setNexusServiceImplementation(mockNexusService) + // The Echo Nexus handler service just makes a call to a class, so no extra setup is + // needed. But the Hello Nexus service needs a worker for both the caller and handler + // in order to run, and the Echo Nexus caller service needs a worker. + // + // registerWorkflowImplementationTypes will take the classes given and create workers for + // them, enabling workflows to run. + // Since both operations are mocked with OperationHandler.sync, no backing workflow is + // needed for hello β€” only the caller workflow types need to be registered. + .registerWorkflowImplementationTypes( + HelloCallerWorkflowImpl.class, EchoCallerWorkflowImpl.class) + // The workflow will start before each test, and will shut down after each test. + // See CallerWorkflowTest for an example of how to control this differently if needed. + .build(); + + // The TestWorkflowExtension extension in the Temporal testing library creates the + // arguments to the test cases and initializes them from the extension setup call above. + @Test + public void testHelloWorkflow( + TestWorkflowEnvironment testEnv, Worker worker, HelloCallerWorkflow workflow) { + // Set the mock value to return + helloResult = new NexusService.HelloOutput("Hello Mock World πŸ‘‹"); + // Execute a workflow waiting for it to complete. + String greeting = workflow.hello("World", NexusService.Language.EN); + assertEquals("Hello Mock World πŸ‘‹", greeting); + } + + @Test + public void testEchoWorkflow( + TestWorkflowEnvironment testEnv, Worker worker, EchoCallerWorkflow workflow) { + // Set the mock value to return + echoResult = new NexusService.EchoOutput("mocked echo"); + // Execute a workflow waiting for it to complete. + String greeting = workflow.echo("Hello"); + assertEquals("mocked echo", greeting); + } +} diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceMockTest.java b/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceMockTest.java new file mode 100644 index 000000000..6090a3fc9 --- /dev/null +++ b/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceMockTest.java @@ -0,0 +1,115 @@ +package io.temporal.samples.nexus.caller; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.nexusrpc.handler.OperationHandler; +import io.temporal.client.WorkflowOptions; +import io.temporal.samples.nexus.handler.NexusServiceImpl; +import io.temporal.samples.nexus.service.NexusService; +import io.temporal.testing.TestWorkflowRule; +import org.junit.Rule; +import org.junit.Test; + +// This unit test example shows how to mock the Nexus service itself in JUnit4. +// In this example since the NexusService itself is mocked, no handlers need to be set up or mocked. + +public class NexusServiceMockTest { + + // Stubs must be set up before TestWorkflowRule initializes, because ServiceImplInstance calls + // each @OperationImpl method on the mock during TestWorkflowRule.init() to capture the + // OperationHandler objects. Any stub set up after that point has no effect. + // JUnit 4 creates a new test class instance per test method, so this mock is fresh each time. + private final NexusServiceImpl mockNexusService = createMockNexusService(); + + // Mutable fields β€” set these in each test method before starting the environment + // to mock the return values for the Nexus service. + // Instance fields (not static) so each test gets its own copy; safe for parallel execution. + private NexusService.EchoOutput echoResult = new NexusService.EchoOutput("default"); + private NexusService.HelloOutput helloResult = new NexusService.HelloOutput("default"); + + private NexusServiceImpl createMockNexusService() { + NexusServiceImpl mock = mock(NexusServiceImpl.class); + + // Using OperationHandler.sync for both operations bypasses the need for a backing workflow, + // returning results inline just like a synchronous call. Mocks need to be done before the rule + // is defined, as creating the rule will fail if either call is still null. + // + // The following is the simplest - just mock the services to return a value. But then these + // values cannot change per test case, so this will not always suffice. + // when(mock.echo()) + // .thenReturn( + // OperationHandler.sync( + // (ctx, details, input) -> new NexusService.EchoOutput("mocked echo"))); + // when(mock.hello()) + // .thenReturn( + // OperationHandler.sync( + // (ctx, details, input) -> new NexusService.HelloOutput("Hello Mock World + // πŸ‘‹"))); + // + // An alternative approach is to create the mocks but set them to return a value that is set in + // the unit + // test class above. That allows you to change the return value in each test. + when(mock.echo()).thenReturn(OperationHandler.sync((ctx, details, input) -> echoResult)); + when(mock.hello()).thenReturn(OperationHandler.sync((ctx, details, input) -> helloResult)); + + return mock; + } + + @Rule + public TestWorkflowRule testWorkflowRule = + TestWorkflowRule.newBuilder() + // If a Nexus service is registered as part of the test as in the following line of code, + // the TestWorkflowRule will, by default, automatically create a Nexus service endpoint + // and workflows registered as part of the TestWorkflowRule + // will automatically inherit the endpoint if none is set. + .setNexusServiceImplementation(mockNexusService) + // The Echo Nexus handler service just makes a call to a class, so no extra setup is + // needed. But the Hello Nexus service needs a worker for both the caller and handler + // in order to run. + // setWorkflowTypes will take the classes given and create workers for them, enabling + // workflows to run. This creates caller workflows, the handler workflows + // will be mocked in the test methods. + .setWorkflowTypes(HelloCallerWorkflowImpl.class, EchoCallerWorkflowImpl.class) + // Disable automatic worker startup as we are going to register some workflows manually + // per test + .setDoNotStart(true) + .build(); + + @Test + public void testHelloWorkflow() { + // Set the mock value to return + helloResult = new NexusService.HelloOutput("Hello Mock World πŸ‘‹"); + testWorkflowRule.getTestEnvironment().start(); + + HelloCallerWorkflow workflow = + testWorkflowRule + .getWorkflowClient() + .newWorkflowStub( + HelloCallerWorkflow.class, + WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build()); + String greeting = workflow.hello("World", NexusService.Language.EN); + assertEquals("Hello Mock World πŸ‘‹", greeting); + + testWorkflowRule.getTestEnvironment().shutdown(); + } + + @Test + public void testEchoWorkflow() { + // Set the mock value to return + echoResult = new NexusService.EchoOutput("mocked echo"); + testWorkflowRule.getTestEnvironment().start(); + + EchoCallerWorkflow workflow = + testWorkflowRule + .getWorkflowClient() + .newWorkflowStub( + EchoCallerWorkflow.class, + WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build()); + String greeting = workflow.echo("Hello"); + assertEquals("mocked echo", greeting); + + testWorkflowRule.getTestEnvironment().shutdown(); + } +} From 9dded5da5503a45285f34a56d6ae23d14ff8fd69 Mon Sep 17 00:00:00 2001 From: Evan Reynolds Date: Mon, 23 Mar 2026 14:21:02 -0700 Subject: [PATCH 4/7] Renamed EchoHandler to EchoClient --- .../handler/{EchoHandler.java => EchoClient.java} | 2 +- .../{EchoHandlerImpl.java => EchoClientImpl.java} | 2 +- .../samples/nexus/handler/NexusServiceImpl.java | 14 +++++++------- .../nexus/caller/CallerWorkflowJunit5MockTest.java | 10 +++++----- .../nexus/caller/CallerWorkflowMockTest.java | 12 ++++++------ 5 files changed, 20 insertions(+), 20 deletions(-) rename core/src/main/java/io/temporal/samples/nexus/handler/{EchoHandler.java => EchoClient.java} (84%) rename core/src/main/java/io/temporal/samples/nexus/handler/{EchoHandlerImpl.java => EchoClientImpl.java} (89%) diff --git a/core/src/main/java/io/temporal/samples/nexus/handler/EchoHandler.java b/core/src/main/java/io/temporal/samples/nexus/handler/EchoClient.java similarity index 84% rename from core/src/main/java/io/temporal/samples/nexus/handler/EchoHandler.java rename to core/src/main/java/io/temporal/samples/nexus/handler/EchoClient.java index eee147cc5..58ac9b117 100644 --- a/core/src/main/java/io/temporal/samples/nexus/handler/EchoHandler.java +++ b/core/src/main/java/io/temporal/samples/nexus/handler/EchoClient.java @@ -2,6 +2,6 @@ import io.temporal.samples.nexus.service.NexusService; -public interface EchoHandler { +public interface EchoClient { NexusService.EchoOutput echo(NexusService.EchoInput input); } diff --git a/core/src/main/java/io/temporal/samples/nexus/handler/EchoHandlerImpl.java b/core/src/main/java/io/temporal/samples/nexus/handler/EchoClientImpl.java similarity index 89% rename from core/src/main/java/io/temporal/samples/nexus/handler/EchoHandlerImpl.java rename to core/src/main/java/io/temporal/samples/nexus/handler/EchoClientImpl.java index 191309b09..9c35529cb 100644 --- a/core/src/main/java/io/temporal/samples/nexus/handler/EchoHandlerImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus/handler/EchoClientImpl.java @@ -4,7 +4,7 @@ // Note that this is a class, not a Temporal worker. This is to demonstrate that Nexus services can // simply call a class instead of a worker for fast operations that don't need retry handling. -public class EchoHandlerImpl implements EchoHandler { +public class EchoClientImpl implements EchoClient { @Override public NexusService.EchoOutput echo(NexusService.EchoInput input) { return new NexusService.EchoOutput(input.getMessage()); diff --git a/core/src/main/java/io/temporal/samples/nexus/handler/NexusServiceImpl.java b/core/src/main/java/io/temporal/samples/nexus/handler/NexusServiceImpl.java index 8bb17d215..2d6547720 100644 --- a/core/src/main/java/io/temporal/samples/nexus/handler/NexusServiceImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus/handler/NexusServiceImpl.java @@ -13,22 +13,22 @@ // return OperationHandler that correspond to the operations defined in the service interface. @ServiceImpl(service = NexusService.class) public class NexusServiceImpl { - private final EchoHandler echoHandler; + private final EchoClient echoClient; - // The injected EchoHandler makes this class unit-testable. + // The injected EchoClient makes this class unit-testable. // The no-arg constructor provides a default; the second allows tests to inject a mock. // If you are not using the sync call or do not need to mock a handler, then you will not // need this constructor pairing. public NexusServiceImpl() { - this(new EchoHandlerImpl()); + this(new EchoClientImpl()); } - public NexusServiceImpl(EchoHandler echoHandler) { - this.echoHandler = echoHandler; + public NexusServiceImpl(EchoClient echoClient) { + this.echoClient = echoClient; } // The Echo Nexus Service exemplifies making a synchronous call using OperationHandler.sync. - // In this case, it is calling the EchoHandler class - not a workflow - and simply returning the + // In this case, it is calling the EchoClient class - not a workflow - and simply returning the // result. @OperationImpl public OperationHandler echo() { @@ -38,7 +38,7 @@ public OperationHandler echo() // calling // Nexus.getOperationContext().getWorkflowClient(ctx) to make arbitrary calls such as // signaling, querying, or listing workflows. - (ctx, details, input) -> echoHandler.echo(input)); + (ctx, details, input) -> echoClient.echo(input)); } @OperationImpl diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java index 423e0ee16..802fc56ac 100644 --- a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java +++ b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java @@ -3,7 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.*; -import io.temporal.samples.nexus.handler.EchoHandler; +import io.temporal.samples.nexus.handler.EchoClient; import io.temporal.samples.nexus.handler.HelloHandlerWorkflow; import io.temporal.samples.nexus.handler.NexusServiceImpl; import io.temporal.samples.nexus.service.NexusService; @@ -20,7 +20,7 @@ public class CallerWorkflowJunit5MockTest { // Sync Nexus operations run inline in the handler thread β€” there is no backing workflow to // register a factory for. To mock one, inject a mock dependency into the service implementation. - private static final EchoHandler mockEchoHandler = mock(EchoHandler.class); + private static final EchoClient mockEchoClient = mock(EchoClient.class); @RegisterExtension public static final TestWorkflowExtension testWorkflowExtension = @@ -29,7 +29,7 @@ public class CallerWorkflowJunit5MockTest { // the TestWorkflowExtension will, by default, automatically create a Nexus service // endpoint and workflows registered as part of the TestWorkflowExtension will // automatically inherit the endpoint if none is set. - .setNexusServiceImplementation(new NexusServiceImpl(mockEchoHandler)) + .setNexusServiceImplementation(new NexusServiceImpl(mockEchoClient)) // The Echo Nexus handler service just makes a call to a class, so no extra setup is // needed. But the Hello Nexus service needs a worker for both the caller and handler // in order to run, and the Echo Nexus caller service needs a worker. @@ -66,8 +66,8 @@ public void testHelloWorkflow( public void testEchoWorkflow( TestWorkflowEnvironment testEnv, Worker worker, EchoCallerWorkflow workflow) { // Sync Nexus operations run inline in the handler thread β€” there is no backing workflow to - // register a factory for. Instead, stub the injected EchoHandler dependency directly. - when(mockEchoHandler.echo(any())).thenReturn(new NexusService.EchoOutput("mocked echo")); + // register a factory for. Instead, stub the injected EchoClient dependency directly. + when(mockEchoClient.echo(any())).thenReturn(new NexusService.EchoOutput("mocked echo")); testEnv.start(); // Execute a workflow waiting for it to complete. diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowMockTest.java b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowMockTest.java index 83520c13c..b15ac703e 100644 --- a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowMockTest.java +++ b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowMockTest.java @@ -6,7 +6,7 @@ import static org.mockito.Mockito.when; import io.temporal.client.WorkflowOptions; -import io.temporal.samples.nexus.handler.EchoHandler; +import io.temporal.samples.nexus.handler.EchoClient; import io.temporal.samples.nexus.handler.HelloHandlerWorkflow; import io.temporal.samples.nexus.handler.NexusServiceImpl; import io.temporal.samples.nexus.service.NexusService; @@ -19,9 +19,9 @@ public class CallerWorkflowMockTest { - // Inject a mock EchoHandler so sync Nexus operations can be stubbed per test. + // Inject a mock EchoClient so sync Nexus operations can be stubbed per test. // JUnit 4 creates a new test class instance per test method, so this mock is fresh each time. - private final EchoHandler mockEchoHandler = mock(EchoHandler.class); + private final EchoClient mockEchoClient = mock(EchoClient.class); @Rule public TestWorkflowRule testWorkflowRule = @@ -30,7 +30,7 @@ public class CallerWorkflowMockTest { // the TestWorkflowRule will, by default, automatically create a Nexus service endpoint // and workflows registered as part of the TestWorkflowRule // will automatically inherit the endpoint if none is set. - .setNexusServiceImplementation(new NexusServiceImpl(mockEchoHandler)) + .setNexusServiceImplementation(new NexusServiceImpl(mockEchoClient)) // The Echo Nexus handler service just makes a call to a class, so no extra setup is // needed. But the Hello Nexus service needs a worker for both the caller and handler // in order to run. @@ -73,8 +73,8 @@ public void testHelloWorkflow() { @Test public void testEchoWorkflow() { // Sync Nexus operations run inline in the handler thread β€” there is no backing workflow to - // register a factory for. Instead, stub the injected EchoHandler dependency directly. - when(mockEchoHandler.echo(any())).thenReturn(new NexusService.EchoOutput("mocked echo")); + // register a factory for. Instead, stub the injected EchoCient dependency directly. + when(mockEchoClient.echo(any())).thenReturn(new NexusService.EchoOutput("mocked echo")); testWorkflowRule.getTestEnvironment().start(); EchoCallerWorkflow workflow = From de0a9bb9b61da7e3fe5f18e623069268745fb8f1 Mon Sep 17 00:00:00 2001 From: Evan Reynolds Date: Mon, 23 Mar 2026 15:03:27 -0700 Subject: [PATCH 5/7] Updating gradle wrapper validator --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6eac7d64b..e3a085782 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,8 +8,8 @@ jobs: name: "Gradle wrapper validation" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: gradle/wrapper-validation-action@v3 + - uses: actions/checkout@v6 + - uses: gradle/actions/wrapper-validation@v5 unittest: name: Unit Tests From 5dc119b320a210255cd1cc83d17da7ef59bc2bd2 Mon Sep 17 00:00:00 2001 From: Evan Reynolds Date: Tue, 24 Mar 2026 10:29:41 -0700 Subject: [PATCH 6/7] Changed NexusService name for clarity, modified two tests --- .../samples/nexus/caller/CallerStarter.java | 7 +- .../samples/nexus/caller/CallerWorker.java | 2 +- .../nexus/caller/EchoCallerWorkflowImpl.java | 8 +- .../nexus/caller/HelloCallerWorkflow.java | 4 +- .../nexus/caller/HelloCallerWorkflowImpl.java | 12 +- .../samples/nexus/handler/EchoClient.java | 4 +- .../samples/nexus/handler/EchoClientImpl.java | 6 +- .../samples/nexus/handler/HandlerWorker.java | 2 +- .../nexus/handler/HelloHandlerWorkflow.java | 4 +- .../handler/HelloHandlerWorkflowImpl.java | 14 +- ...eImpl.java => SampleNexusServiceImpl.java} | 14 +- ...usService.java => SampleNexusService.java} | 2 +- .../samples/nexus/service/description.md | 4 +- .../caller/CallerWorker.java | 4 +- .../caller/HelloCallerWorkflowImpl.java | 23 +-- .../handler/HandlerWorker.java | 4 +- .../handler/HelloHandlerWorkflowImpl.java | 14 +- .../samples/nexuscontextpropagation/README.MD | 2 +- .../caller/CallerStarter.java | 7 +- .../caller/CallerWorker.java | 2 +- .../caller/EchoCallerWorkflowImpl.java | 8 +- .../caller/HelloCallerWorkflowImpl.java | 12 +- .../handler/HandlerWorker.java | 2 +- .../handler/HelloHandlerWorkflowImpl.java | 14 +- ...eImpl.java => SampleNexusServiceImpl.java} | 14 +- .../caller/CallerStarter.java | 7 +- .../caller/CallerWorker.java | 2 +- .../caller/EchoCallerWorkflowImpl.java | 8 +- .../caller/HelloCallerWorkflow.java | 4 +- .../caller/HelloCallerWorkflowImpl.java | 12 +- .../handler/HandlerWorker.java | 2 +- .../handler/HelloHandlerWorkflow.java | 4 +- .../handler/HelloHandlerWorkflowImpl.java | 14 +- ...eImpl.java => SampleNexusServiceImpl.java} | 12 +- .../caller/CallerWorkflowJunit5MockTest.java | 12 +- .../caller/CallerWorkflowJunit5Test.java | 8 +- .../nexus/caller/CallerWorkflowMockTest.java | 13 +- .../nexus/caller/CallerWorkflowTest.java | 10 +- .../nexus/caller/NexusServiceJunit5Test.java | 92 ++++++------ .../nexus/caller/NexusServiceMockTest.java | 133 ++++++++---------- 40 files changed, 259 insertions(+), 263 deletions(-) rename core/src/main/java/io/temporal/samples/nexus/handler/{NexusServiceImpl.java => SampleNexusServiceImpl.java} (87%) rename core/src/main/java/io/temporal/samples/nexus/service/{NexusService.java => SampleNexusService.java} (97%) rename core/src/main/java/io/temporal/samples/nexuscontextpropagation/handler/{NexusServiceImpl.java => SampleNexusServiceImpl.java} (84%) rename core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/{NexusServiceImpl.java => SampleNexusServiceImpl.java} (86%) diff --git a/core/src/main/java/io/temporal/samples/nexus/caller/CallerStarter.java b/core/src/main/java/io/temporal/samples/nexus/caller/CallerStarter.java index be1cc788e..f207d64cf 100644 --- a/core/src/main/java/io/temporal/samples/nexus/caller/CallerStarter.java +++ b/core/src/main/java/io/temporal/samples/nexus/caller/CallerStarter.java @@ -4,7 +4,7 @@ import io.temporal.client.WorkflowClient; import io.temporal.client.WorkflowOptions; import io.temporal.samples.nexus.options.ClientOptions; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,11 +26,12 @@ public static void main(String[] args) { logger.info("Workflow result: {}", echoWorkflow.echo("Nexus Echo πŸ‘‹")); HelloCallerWorkflow helloWorkflow = client.newWorkflowStub(HelloCallerWorkflow.class, workflowOptions); - execution = WorkflowClient.start(helloWorkflow::hello, "Nexus", NexusService.Language.EN); + execution = WorkflowClient.start(helloWorkflow::hello, "Nexus", SampleNexusService.Language.EN); logger.info( "Started HelloCallerWorkflow workflowId: {} runId: {}", execution.getWorkflowId(), execution.getRunId()); - logger.info("Workflow result: {}", helloWorkflow.hello("Nexus", NexusService.Language.ES)); + logger.info( + "Workflow result: {}", helloWorkflow.hello("Nexus", SampleNexusService.Language.ES)); } } diff --git a/core/src/main/java/io/temporal/samples/nexus/caller/CallerWorker.java b/core/src/main/java/io/temporal/samples/nexus/caller/CallerWorker.java index 55186489e..5480917ba 100644 --- a/core/src/main/java/io/temporal/samples/nexus/caller/CallerWorker.java +++ b/core/src/main/java/io/temporal/samples/nexus/caller/CallerWorker.java @@ -21,7 +21,7 @@ public static void main(String[] args) { WorkflowImplementationOptions.newBuilder() .setNexusServiceOptions( Collections.singletonMap( - "NexusService", + "SampleNexusService", NexusServiceOptions.newBuilder().setEndpoint("my-nexus-endpoint-name").build())) .build(), EchoCallerWorkflowImpl.class, diff --git a/core/src/main/java/io/temporal/samples/nexus/caller/EchoCallerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexus/caller/EchoCallerWorkflowImpl.java index 948008772..f76edbfe4 100644 --- a/core/src/main/java/io/temporal/samples/nexus/caller/EchoCallerWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus/caller/EchoCallerWorkflowImpl.java @@ -1,15 +1,15 @@ package io.temporal.samples.nexus.caller; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.workflow.NexusOperationOptions; import io.temporal.workflow.NexusServiceOptions; import io.temporal.workflow.Workflow; import java.time.Duration; public class EchoCallerWorkflowImpl implements EchoCallerWorkflow { - NexusService nexusService = + SampleNexusService sampleNexusService = Workflow.newNexusServiceStub( - NexusService.class, + SampleNexusService.class, NexusServiceOptions.newBuilder() .setOperationOptions( NexusOperationOptions.newBuilder() @@ -19,6 +19,6 @@ public class EchoCallerWorkflowImpl implements EchoCallerWorkflow { @Override public String echo(String message) { - return nexusService.echo(new NexusService.EchoInput(message)).getMessage(); + return sampleNexusService.echo(new SampleNexusService.EchoInput(message)).getMessage(); } } diff --git a/core/src/main/java/io/temporal/samples/nexus/caller/HelloCallerWorkflow.java b/core/src/main/java/io/temporal/samples/nexus/caller/HelloCallerWorkflow.java index 173c0be8b..1f78e9c02 100644 --- a/core/src/main/java/io/temporal/samples/nexus/caller/HelloCallerWorkflow.java +++ b/core/src/main/java/io/temporal/samples/nexus/caller/HelloCallerWorkflow.java @@ -1,11 +1,11 @@ package io.temporal.samples.nexus.caller; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.workflow.WorkflowInterface; import io.temporal.workflow.WorkflowMethod; @WorkflowInterface public interface HelloCallerWorkflow { @WorkflowMethod - String hello(String message, NexusService.Language language); + String hello(String message, SampleNexusService.Language language); } diff --git a/core/src/main/java/io/temporal/samples/nexus/caller/HelloCallerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexus/caller/HelloCallerWorkflowImpl.java index f1612e359..6a6fe8dce 100644 --- a/core/src/main/java/io/temporal/samples/nexus/caller/HelloCallerWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus/caller/HelloCallerWorkflowImpl.java @@ -1,6 +1,6 @@ package io.temporal.samples.nexus.caller; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.workflow.NexusOperationHandle; import io.temporal.workflow.NexusOperationOptions; import io.temporal.workflow.NexusServiceOptions; @@ -8,9 +8,9 @@ import java.time.Duration; public class HelloCallerWorkflowImpl implements HelloCallerWorkflow { - NexusService nexusService = + SampleNexusService sampleNexusService = Workflow.newNexusServiceStub( - NexusService.class, + SampleNexusService.class, NexusServiceOptions.newBuilder() .setOperationOptions( NexusOperationOptions.newBuilder() @@ -19,10 +19,10 @@ public class HelloCallerWorkflowImpl implements HelloCallerWorkflow { .build()); @Override - public String hello(String message, NexusService.Language language) { - NexusOperationHandle handle = + public String hello(String message, SampleNexusService.Language language) { + NexusOperationHandle handle = Workflow.startNexusOperation( - nexusService::hello, new NexusService.HelloInput(message, language)); + sampleNexusService::hello, new SampleNexusService.HelloInput(message, language)); // Optionally wait for the operation to be started. NexusOperationExecution will contain the // operation token in case this operation is asynchronous. handle.getExecution().get(); diff --git a/core/src/main/java/io/temporal/samples/nexus/handler/EchoClient.java b/core/src/main/java/io/temporal/samples/nexus/handler/EchoClient.java index 58ac9b117..74b6f6c69 100644 --- a/core/src/main/java/io/temporal/samples/nexus/handler/EchoClient.java +++ b/core/src/main/java/io/temporal/samples/nexus/handler/EchoClient.java @@ -1,7 +1,7 @@ package io.temporal.samples.nexus.handler; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; public interface EchoClient { - NexusService.EchoOutput echo(NexusService.EchoInput input); + SampleNexusService.EchoOutput echo(SampleNexusService.EchoInput input); } diff --git a/core/src/main/java/io/temporal/samples/nexus/handler/EchoClientImpl.java b/core/src/main/java/io/temporal/samples/nexus/handler/EchoClientImpl.java index 9c35529cb..1c9a2e524 100644 --- a/core/src/main/java/io/temporal/samples/nexus/handler/EchoClientImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus/handler/EchoClientImpl.java @@ -1,12 +1,12 @@ package io.temporal.samples.nexus.handler; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; // Note that this is a class, not a Temporal worker. This is to demonstrate that Nexus services can // simply call a class instead of a worker for fast operations that don't need retry handling. public class EchoClientImpl implements EchoClient { @Override - public NexusService.EchoOutput echo(NexusService.EchoInput input) { - return new NexusService.EchoOutput(input.getMessage()); + public SampleNexusService.EchoOutput echo(SampleNexusService.EchoInput input) { + return new SampleNexusService.EchoOutput(input.getMessage()); } } diff --git a/core/src/main/java/io/temporal/samples/nexus/handler/HandlerWorker.java b/core/src/main/java/io/temporal/samples/nexus/handler/HandlerWorker.java index 3d8afa3d7..656b18c65 100644 --- a/core/src/main/java/io/temporal/samples/nexus/handler/HandlerWorker.java +++ b/core/src/main/java/io/temporal/samples/nexus/handler/HandlerWorker.java @@ -15,7 +15,7 @@ public static void main(String[] args) { Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME); worker.registerWorkflowImplementationTypes(HelloHandlerWorkflowImpl.class); - worker.registerNexusServiceImplementation(new NexusServiceImpl()); + worker.registerNexusServiceImplementation(new SampleNexusServiceImpl()); factory.start(); } diff --git a/core/src/main/java/io/temporal/samples/nexus/handler/HelloHandlerWorkflow.java b/core/src/main/java/io/temporal/samples/nexus/handler/HelloHandlerWorkflow.java index 3cae13b31..2c85d0792 100644 --- a/core/src/main/java/io/temporal/samples/nexus/handler/HelloHandlerWorkflow.java +++ b/core/src/main/java/io/temporal/samples/nexus/handler/HelloHandlerWorkflow.java @@ -1,11 +1,11 @@ package io.temporal.samples.nexus.handler; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.workflow.WorkflowInterface; import io.temporal.workflow.WorkflowMethod; @WorkflowInterface public interface HelloHandlerWorkflow { @WorkflowMethod - NexusService.HelloOutput hello(NexusService.HelloInput input); + SampleNexusService.HelloOutput hello(SampleNexusService.HelloInput input); } diff --git a/core/src/main/java/io/temporal/samples/nexus/handler/HelloHandlerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexus/handler/HelloHandlerWorkflowImpl.java index c897a5181..b896ab523 100644 --- a/core/src/main/java/io/temporal/samples/nexus/handler/HelloHandlerWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus/handler/HelloHandlerWorkflowImpl.java @@ -1,22 +1,22 @@ package io.temporal.samples.nexus.handler; import io.temporal.failure.ApplicationFailure; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; public class HelloHandlerWorkflowImpl implements HelloHandlerWorkflow { @Override - public NexusService.HelloOutput hello(NexusService.HelloInput input) { + public SampleNexusService.HelloOutput hello(SampleNexusService.HelloInput input) { switch (input.getLanguage()) { case EN: - return new NexusService.HelloOutput("Hello " + input.getName() + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Hello " + input.getName() + " πŸ‘‹"); case FR: - return new NexusService.HelloOutput("Bonjour " + input.getName() + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Bonjour " + input.getName() + " πŸ‘‹"); case DE: - return new NexusService.HelloOutput("Hallo " + input.getName() + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Hallo " + input.getName() + " πŸ‘‹"); case ES: - return new NexusService.HelloOutput("Β‘Hola! " + input.getName() + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Β‘Hola! " + input.getName() + " πŸ‘‹"); case TR: - return new NexusService.HelloOutput("Merhaba " + input.getName() + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Merhaba " + input.getName() + " πŸ‘‹"); } throw ApplicationFailure.newFailure( "Unsupported language: " + input.getLanguage(), "UNSUPPORTED_LANGUAGE"); diff --git a/core/src/main/java/io/temporal/samples/nexus/handler/NexusServiceImpl.java b/core/src/main/java/io/temporal/samples/nexus/handler/SampleNexusServiceImpl.java similarity index 87% rename from core/src/main/java/io/temporal/samples/nexus/handler/NexusServiceImpl.java rename to core/src/main/java/io/temporal/samples/nexus/handler/SampleNexusServiceImpl.java index 2d6547720..42952b72b 100644 --- a/core/src/main/java/io/temporal/samples/nexus/handler/NexusServiceImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus/handler/SampleNexusServiceImpl.java @@ -6,24 +6,24 @@ import io.temporal.client.WorkflowOptions; import io.temporal.nexus.Nexus; import io.temporal.nexus.WorkflowRunOperation; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; // To create a service implementation, annotate the class with @ServiceImpl and provide the // interface that the service implements. The service implementation class should have methods that // return OperationHandler that correspond to the operations defined in the service interface. -@ServiceImpl(service = NexusService.class) -public class NexusServiceImpl { +@ServiceImpl(service = SampleNexusService.class) +public class SampleNexusServiceImpl { private final EchoClient echoClient; // The injected EchoClient makes this class unit-testable. // The no-arg constructor provides a default; the second allows tests to inject a mock. // If you are not using the sync call or do not need to mock a handler, then you will not // need this constructor pairing. - public NexusServiceImpl() { + public SampleNexusServiceImpl() { this(new EchoClientImpl()); } - public NexusServiceImpl(EchoClient echoClient) { + public SampleNexusServiceImpl(EchoClient echoClient) { this.echoClient = echoClient; } @@ -31,7 +31,7 @@ public NexusServiceImpl(EchoClient echoClient) { // In this case, it is calling the EchoClient class - not a workflow - and simply returning the // result. @OperationImpl - public OperationHandler echo() { + public OperationHandler echo() { return OperationHandler.sync( // The method is for making arbitrary short calls to other services or databases, or // perform simple computations such as this one. Users can also access a workflow client by @@ -42,7 +42,7 @@ public OperationHandler echo() } @OperationImpl - public OperationHandler hello() { + public OperationHandler hello() { // Use the WorkflowRunOperation.fromWorkflowMethod constructor, which is the easiest // way to expose a workflow as an operation. To expose a workflow with a different input // parameters then the operation or from an untyped stub, use the diff --git a/core/src/main/java/io/temporal/samples/nexus/service/NexusService.java b/core/src/main/java/io/temporal/samples/nexus/service/SampleNexusService.java similarity index 97% rename from core/src/main/java/io/temporal/samples/nexus/service/NexusService.java rename to core/src/main/java/io/temporal/samples/nexus/service/SampleNexusService.java index ad65b1b33..180f9ec28 100644 --- a/core/src/main/java/io/temporal/samples/nexus/service/NexusService.java +++ b/core/src/main/java/io/temporal/samples/nexus/service/SampleNexusService.java @@ -6,7 +6,7 @@ import io.nexusrpc.Service; @Service -public interface NexusService { +public interface SampleNexusService { enum Language { EN, FR, diff --git a/core/src/main/java/io/temporal/samples/nexus/service/description.md b/core/src/main/java/io/temporal/samples/nexus/service/description.md index d79e151cd..b1cafb3a2 100644 --- a/core/src/main/java/io/temporal/samples/nexus/service/description.md +++ b/core/src/main/java/io/temporal/samples/nexus/service/description.md @@ -1,6 +1,6 @@ -## Service: [NexusService](https://github.com/temporalio/samples-java/blob/main/core/src/main/java/io/temporal/samples/nexus/service/NexusService.java) +## Service: [SampleNexusService](https://github.com/temporalio/samples-java/blob/main/core/src/main/java/io/temporal/samples/nexus/service/SampleNexusService.java) - operation: `echo` - operation: `hello` -See https://github.com/temporalio/samples-java/blob/main/core/src/main/java/io/temporal/samples/nexus/service/NexusService.java for Input / Output types. +See https://github.com/temporalio/samples-java/blob/main/core/src/main/java/io/temporal/samples/nexus/service/SampleNexusService.java for Input / Output types. diff --git a/core/src/main/java/io/temporal/samples/nexuscancellation/caller/CallerWorker.java b/core/src/main/java/io/temporal/samples/nexuscancellation/caller/CallerWorker.java index cb05189cf..811cecde8 100644 --- a/core/src/main/java/io/temporal/samples/nexuscancellation/caller/CallerWorker.java +++ b/core/src/main/java/io/temporal/samples/nexuscancellation/caller/CallerWorker.java @@ -2,7 +2,7 @@ import io.temporal.client.WorkflowClient; import io.temporal.samples.nexus.options.ClientOptions; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.worker.Worker; import io.temporal.worker.WorkerFactory; import io.temporal.worker.WorkflowImplementationOptions; @@ -22,7 +22,7 @@ public static void main(String[] args) { WorkflowImplementationOptions.newBuilder() .setNexusServiceOptions( Collections.singletonMap( - NexusService.class.getSimpleName(), + SampleNexusService.class.getSimpleName(), NexusServiceOptions.newBuilder().setEndpoint("my-nexus-endpoint-name").build())) .build(), HelloCallerWorkflowImpl.class); diff --git a/core/src/main/java/io/temporal/samples/nexuscancellation/caller/HelloCallerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexuscancellation/caller/HelloCallerWorkflowImpl.java index 1bea801c8..6072906b4 100644 --- a/core/src/main/java/io/temporal/samples/nexuscancellation/caller/HelloCallerWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexuscancellation/caller/HelloCallerWorkflowImpl.java @@ -1,10 +1,10 @@ package io.temporal.samples.nexuscancellation.caller; -import static io.temporal.samples.nexus.service.NexusService.Language.*; +import static io.temporal.samples.nexus.service.SampleNexusService.Language.*; import io.temporal.failure.CanceledFailure; import io.temporal.failure.NexusOperationFailure; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.workflow.*; import java.time.Duration; import java.util.ArrayList; @@ -13,11 +13,11 @@ public class HelloCallerWorkflowImpl implements HelloCallerWorkflow { public static final Logger log = Workflow.getLogger(HelloCallerWorkflowImpl.class); - private static final NexusService.Language[] languages = - new NexusService.Language[] {EN, FR, DE, ES, TR}; - NexusService nexusService = + private static final SampleNexusService.Language[] languages = + new SampleNexusService.Language[] {EN, FR, DE, ES, TR}; + SampleNexusService sampleNexusService = Workflow.newNexusServiceStub( - NexusService.class, + SampleNexusService.class, NexusServiceOptions.newBuilder() .setOperationOptions( NexusOperationOptions.newBuilder() @@ -33,7 +33,7 @@ public class HelloCallerWorkflowImpl implements HelloCallerWorkflow { @Override public String hello(String message) { - List> results = new ArrayList<>(languages.length); + List> results = new ArrayList<>(languages.length); /* * Create our CancellationScope. Within this scope we call the nexus operation asynchronously @@ -42,10 +42,11 @@ public String hello(String message) { CancellationScope scope = Workflow.newCancellationScope( () -> { - for (NexusService.Language language : languages) { + for (SampleNexusService.Language language : languages) { results.add( Async.function( - nexusService::hello, new NexusService.HelloInput(message, language))); + sampleNexusService::hello, + new SampleNexusService.HelloInput(message, language))); } }); @@ -56,7 +57,7 @@ public String hello(String message) { scope.run(); // We use "anyOf" here to wait for one of the nexus operation invocations to return - NexusService.HelloOutput result = Promise.anyOf(results).get(); + SampleNexusService.HelloOutput result = Promise.anyOf(results).get(); // Trigger cancellation of all uncompleted nexus operations invocations within the cancellation // scope @@ -67,7 +68,7 @@ public String hello(String message) { // Note: Once the workflow completes any pending cancellation requests are dropped by the // server. In general, it is a good practice to wait for all cancellation requests to be // processed before completing the workflow. - for (Promise promise : results) { + for (Promise promise : results) { try { promise.get(); } catch (NexusOperationFailure e) { diff --git a/core/src/main/java/io/temporal/samples/nexuscancellation/handler/HandlerWorker.java b/core/src/main/java/io/temporal/samples/nexuscancellation/handler/HandlerWorker.java index 8b8949a70..f7d0f6940 100644 --- a/core/src/main/java/io/temporal/samples/nexuscancellation/handler/HandlerWorker.java +++ b/core/src/main/java/io/temporal/samples/nexuscancellation/handler/HandlerWorker.java @@ -1,7 +1,7 @@ package io.temporal.samples.nexuscancellation.handler; import io.temporal.client.WorkflowClient; -import io.temporal.samples.nexus.handler.NexusServiceImpl; +import io.temporal.samples.nexus.handler.SampleNexusServiceImpl; import io.temporal.samples.nexus.options.ClientOptions; import io.temporal.worker.Worker; import io.temporal.worker.WorkerFactory; @@ -16,7 +16,7 @@ public static void main(String[] args) { Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME); worker.registerWorkflowImplementationTypes(HelloHandlerWorkflowImpl.class); - worker.registerNexusServiceImplementation(new NexusServiceImpl()); + worker.registerNexusServiceImplementation(new SampleNexusServiceImpl()); factory.start(); } diff --git a/core/src/main/java/io/temporal/samples/nexuscancellation/handler/HelloHandlerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexuscancellation/handler/HelloHandlerWorkflowImpl.java index ca6510f60..de8b93557 100644 --- a/core/src/main/java/io/temporal/samples/nexuscancellation/handler/HelloHandlerWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexuscancellation/handler/HelloHandlerWorkflowImpl.java @@ -3,7 +3,7 @@ import io.temporal.failure.ApplicationFailure; import io.temporal.failure.CanceledFailure; import io.temporal.samples.nexus.handler.HelloHandlerWorkflow; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.workflow.Workflow; import java.time.Duration; import org.slf4j.Logger; @@ -12,21 +12,21 @@ public class HelloHandlerWorkflowImpl implements HelloHandlerWorkflow { public static final Logger log = Workflow.getLogger(HelloHandlerWorkflowImpl.class); @Override - public NexusService.HelloOutput hello(NexusService.HelloInput input) { + public SampleNexusService.HelloOutput hello(SampleNexusService.HelloInput input) { // Sleep for a random duration to simulate some work try { Workflow.sleep(Duration.ofSeconds(Workflow.newRandom().nextInt(5))); switch (input.getLanguage()) { case EN: - return new NexusService.HelloOutput("Hello " + input.getName() + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Hello " + input.getName() + " πŸ‘‹"); case FR: - return new NexusService.HelloOutput("Bonjour " + input.getName() + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Bonjour " + input.getName() + " πŸ‘‹"); case DE: - return new NexusService.HelloOutput("Hallo " + input.getName() + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Hallo " + input.getName() + " πŸ‘‹"); case ES: - return new NexusService.HelloOutput("Β‘Hola! " + input.getName() + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Β‘Hola! " + input.getName() + " πŸ‘‹"); case TR: - return new NexusService.HelloOutput("Merhaba " + input.getName() + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Merhaba " + input.getName() + " πŸ‘‹"); } throw ApplicationFailure.newFailure( "Unsupported language: " + input.getLanguage(), "UNSUPPORTED_LANGUAGE"); diff --git a/core/src/main/java/io/temporal/samples/nexuscontextpropagation/README.MD b/core/src/main/java/io/temporal/samples/nexuscontextpropagation/README.MD index 79453cc4d..54e2bc4f9 100644 --- a/core/src/main/java/io/temporal/samples/nexuscontextpropagation/README.MD +++ b/core/src/main/java/io/temporal/samples/nexuscontextpropagation/README.MD @@ -40,6 +40,6 @@ INFO i.t.s.n.caller.CallerStarter - Workflow result: Hello Nexus πŸ‘‹ And this on the handler side: ``` -INFO i.t.s.n.handler.NexusServiceImpl - Echo called from a workflow with ID : 7ac97cb9-b457-4052-af94-d82478c35c5e +INFO i.t.s.n.handler.SampleNexusServiceImpl - Echo called from a workflow with ID : 7ac97cb9-b457-4052-af94-d82478c35c5e INFO i.t.s.n.h.HelloHandlerWorkflowImpl - HelloHandlerWorkflow called from a workflow with ID : 9e0bc89c-5709-4742-b7c0-868464c2fccf ``` diff --git a/core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/CallerStarter.java b/core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/CallerStarter.java index f0896221a..cfcc739de 100644 --- a/core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/CallerStarter.java +++ b/core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/CallerStarter.java @@ -7,7 +7,7 @@ import io.temporal.samples.nexus.caller.EchoCallerWorkflow; import io.temporal.samples.nexus.caller.HelloCallerWorkflow; import io.temporal.samples.nexus.options.ClientOptions; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.samples.nexuscontextpropagation.propagation.MDCContextPropagator; import java.util.Collections; import org.slf4j.Logger; @@ -35,11 +35,12 @@ public static void main(String[] args) { logger.info("Workflow result: {}", echoWorkflow.echo("Nexus Echo πŸ‘‹")); HelloCallerWorkflow helloWorkflow = client.newWorkflowStub(HelloCallerWorkflow.class, workflowOptions); - execution = WorkflowClient.start(helloWorkflow::hello, "Nexus", NexusService.Language.EN); + execution = WorkflowClient.start(helloWorkflow::hello, "Nexus", SampleNexusService.Language.EN); logger.info( "Started HelloCallerWorkflow workflowId: {} runId: {}", execution.getWorkflowId(), execution.getRunId()); - logger.info("Workflow result: {}", helloWorkflow.hello("Nexus", NexusService.Language.ES)); + logger.info( + "Workflow result: {}", helloWorkflow.hello("Nexus", SampleNexusService.Language.ES)); } } diff --git a/core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/CallerWorker.java b/core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/CallerWorker.java index 13e568822..db3f1cfb9 100644 --- a/core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/CallerWorker.java +++ b/core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/CallerWorker.java @@ -28,7 +28,7 @@ public static void main(String[] args) { WorkflowImplementationOptions.newBuilder() .setNexusServiceOptions( Collections.singletonMap( - "NexusService", + "SampleNexusService", NexusServiceOptions.newBuilder().setEndpoint("my-nexus-endpoint-name").build())) .build(), EchoCallerWorkflowImpl.class, diff --git a/core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/EchoCallerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/EchoCallerWorkflowImpl.java index 14e998629..3de27350d 100644 --- a/core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/EchoCallerWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/EchoCallerWorkflowImpl.java @@ -1,7 +1,7 @@ package io.temporal.samples.nexuscontextpropagation.caller; import io.temporal.samples.nexus.caller.EchoCallerWorkflow; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.workflow.NexusOperationOptions; import io.temporal.workflow.NexusServiceOptions; import io.temporal.workflow.Workflow; @@ -9,9 +9,9 @@ import org.slf4j.MDC; public class EchoCallerWorkflowImpl implements EchoCallerWorkflow { - NexusService nexusService = + SampleNexusService sampleNexusService = Workflow.newNexusServiceStub( - NexusService.class, + SampleNexusService.class, NexusServiceOptions.newBuilder() .setOperationOptions( NexusOperationOptions.newBuilder() @@ -22,6 +22,6 @@ public class EchoCallerWorkflowImpl implements EchoCallerWorkflow { @Override public String echo(String message) { MDC.put("x-nexus-caller-workflow-id", Workflow.getInfo().getWorkflowId()); - return nexusService.echo(new NexusService.EchoInput(message)).getMessage(); + return sampleNexusService.echo(new SampleNexusService.EchoInput(message)).getMessage(); } } diff --git a/core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/HelloCallerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/HelloCallerWorkflowImpl.java index fedefbc34..b817179a4 100644 --- a/core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/HelloCallerWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/HelloCallerWorkflowImpl.java @@ -1,7 +1,7 @@ package io.temporal.samples.nexuscontextpropagation.caller; import io.temporal.samples.nexus.caller.HelloCallerWorkflow; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.workflow.NexusOperationHandle; import io.temporal.workflow.NexusOperationOptions; import io.temporal.workflow.NexusServiceOptions; @@ -10,9 +10,9 @@ import org.slf4j.MDC; public class HelloCallerWorkflowImpl implements HelloCallerWorkflow { - NexusService nexusService = + SampleNexusService sampleNexusService = Workflow.newNexusServiceStub( - NexusService.class, + SampleNexusService.class, NexusServiceOptions.newBuilder() .setOperationOptions( NexusOperationOptions.newBuilder() @@ -21,11 +21,11 @@ public class HelloCallerWorkflowImpl implements HelloCallerWorkflow { .build()); @Override - public String hello(String message, NexusService.Language language) { + public String hello(String message, SampleNexusService.Language language) { MDC.put("x-nexus-caller-workflow-id", Workflow.getInfo().getWorkflowId()); - NexusOperationHandle handle = + NexusOperationHandle handle = Workflow.startNexusOperation( - nexusService::hello, new NexusService.HelloInput(message, language)); + sampleNexusService::hello, new SampleNexusService.HelloInput(message, language)); // Optionally wait for the operation to be started. NexusOperationExecution will contain the // operation token in case this operation is asynchronous. handle.getExecution().get(); diff --git a/core/src/main/java/io/temporal/samples/nexuscontextpropagation/handler/HandlerWorker.java b/core/src/main/java/io/temporal/samples/nexuscontextpropagation/handler/HandlerWorker.java index c4f9ef54c..31b665a8a 100644 --- a/core/src/main/java/io/temporal/samples/nexuscontextpropagation/handler/HandlerWorker.java +++ b/core/src/main/java/io/temporal/samples/nexuscontextpropagation/handler/HandlerWorker.java @@ -29,7 +29,7 @@ public static void main(String[] args) { Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME); worker.registerWorkflowImplementationTypes(HelloHandlerWorkflowImpl.class); - worker.registerNexusServiceImplementation(new NexusServiceImpl()); + worker.registerNexusServiceImplementation(new SampleNexusServiceImpl()); factory.start(); } diff --git a/core/src/main/java/io/temporal/samples/nexuscontextpropagation/handler/HelloHandlerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexuscontextpropagation/handler/HelloHandlerWorkflowImpl.java index a2db3cb0e..324ad34c1 100644 --- a/core/src/main/java/io/temporal/samples/nexuscontextpropagation/handler/HelloHandlerWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexuscontextpropagation/handler/HelloHandlerWorkflowImpl.java @@ -2,7 +2,7 @@ import io.temporal.failure.ApplicationFailure; import io.temporal.samples.nexus.handler.HelloHandlerWorkflow; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.workflow.Workflow; import org.slf4j.Logger; import org.slf4j.MDC; @@ -11,7 +11,7 @@ public class HelloHandlerWorkflowImpl implements HelloHandlerWorkflow { public static final Logger log = Workflow.getLogger(HelloHandlerWorkflowImpl.class); @Override - public NexusService.HelloOutput hello(NexusService.HelloInput input) { + public SampleNexusService.HelloOutput hello(SampleNexusService.HelloInput input) { if (MDC.get("x-nexus-caller-workflow-id") != null) { log.info( "HelloHandlerWorkflow called from a workflow with ID : {}", @@ -19,15 +19,15 @@ public NexusService.HelloOutput hello(NexusService.HelloInput input) { } switch (input.getLanguage()) { case EN: - return new NexusService.HelloOutput("Hello " + input.getName() + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Hello " + input.getName() + " πŸ‘‹"); case FR: - return new NexusService.HelloOutput("Bonjour " + input.getName() + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Bonjour " + input.getName() + " πŸ‘‹"); case DE: - return new NexusService.HelloOutput("Hallo " + input.getName() + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Hallo " + input.getName() + " πŸ‘‹"); case ES: - return new NexusService.HelloOutput("Β‘Hola! " + input.getName() + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Β‘Hola! " + input.getName() + " πŸ‘‹"); case TR: - return new NexusService.HelloOutput("Merhaba " + input.getName() + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Merhaba " + input.getName() + " πŸ‘‹"); } throw ApplicationFailure.newFailure( "Unsupported language: " + input.getLanguage(), "UNSUPPORTED_LANGUAGE"); diff --git a/core/src/main/java/io/temporal/samples/nexuscontextpropagation/handler/NexusServiceImpl.java b/core/src/main/java/io/temporal/samples/nexuscontextpropagation/handler/SampleNexusServiceImpl.java similarity index 84% rename from core/src/main/java/io/temporal/samples/nexuscontextpropagation/handler/NexusServiceImpl.java rename to core/src/main/java/io/temporal/samples/nexuscontextpropagation/handler/SampleNexusServiceImpl.java index 4977ff0c0..fc69e756b 100644 --- a/core/src/main/java/io/temporal/samples/nexuscontextpropagation/handler/NexusServiceImpl.java +++ b/core/src/main/java/io/temporal/samples/nexuscontextpropagation/handler/SampleNexusServiceImpl.java @@ -7,7 +7,7 @@ import io.temporal.nexus.Nexus; import io.temporal.nexus.WorkflowRunOperation; import io.temporal.samples.nexus.handler.HelloHandlerWorkflow; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; @@ -15,12 +15,12 @@ // To create a service implementation, annotate the class with @ServiceImpl and provide the // interface that the service implements. The service implementation class should have methods that // return OperationHandler that correspond to the operations defined in the service interface. -@ServiceImpl(service = NexusService.class) -public class NexusServiceImpl { - private static final Logger logger = LoggerFactory.getLogger(NexusServiceImpl.class); +@ServiceImpl(service = SampleNexusService.class) +public class SampleNexusServiceImpl { + private static final Logger logger = LoggerFactory.getLogger(SampleNexusServiceImpl.class); @OperationImpl - public OperationHandler echo() { + public OperationHandler echo() { // OperationHandler.sync is a meant for exposing simple RPC handlers. return OperationHandler.sync( // The method is for making arbitrary short calls to other services or databases, or @@ -33,12 +33,12 @@ public OperationHandler echo() logger.info( "Echo called from a workflow with ID : {}", MDC.get("x-nexus-caller-workflow-id")); } - return new NexusService.EchoOutput(input.getMessage()); + return new SampleNexusService.EchoOutput(input.getMessage()); }); } @OperationImpl - public OperationHandler hello() { + public OperationHandler hello() { // Use the WorkflowRunOperation.fromWorkflowMethod constructor, which is the easiest // way to expose a workflow as an operation. To expose a workflow with a different input // parameters then the operation or from an untyped stub, use the diff --git a/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/CallerStarter.java b/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/CallerStarter.java index beebdf406..4d3e7cff5 100644 --- a/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/CallerStarter.java +++ b/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/CallerStarter.java @@ -7,7 +7,7 @@ import io.temporal.samples.nexus.caller.EchoCallerWorkflow; import io.temporal.samples.nexus.caller.HelloCallerWorkflow; import io.temporal.samples.nexus.options.ClientOptions; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,11 +29,12 @@ public static void main(String[] args) { logger.info("Workflow result: {}", echoWorkflow.echo("Nexus Echo πŸ‘‹")); HelloCallerWorkflow helloWorkflow = client.newWorkflowStub(HelloCallerWorkflow.class, workflowOptions); - execution = WorkflowClient.start(helloWorkflow::hello, "Nexus", NexusService.Language.EN); + execution = WorkflowClient.start(helloWorkflow::hello, "Nexus", SampleNexusService.Language.EN); logger.info( "Started HelloCallerWorkflow workflowId: {} runId: {}", execution.getWorkflowId(), execution.getRunId()); - logger.info("Workflow result: {}", helloWorkflow.hello("Nexus", NexusService.Language.ES)); + logger.info( + "Workflow result: {}", helloWorkflow.hello("Nexus", SampleNexusService.Language.ES)); } } diff --git a/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/CallerWorker.java b/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/CallerWorker.java index 1db03a677..64272e6a2 100644 --- a/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/CallerWorker.java +++ b/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/CallerWorker.java @@ -21,7 +21,7 @@ public static void main(String[] args) { WorkflowImplementationOptions.newBuilder() .setNexusServiceOptions( Collections.singletonMap( - "NexusService", + "SampleNexusService", NexusServiceOptions.newBuilder().setEndpoint("my-nexus-endpoint-name").build())) .build(), EchoCallerWorkflowImpl.class, diff --git a/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/EchoCallerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/EchoCallerWorkflowImpl.java index ea9aaabb0..20d875ddb 100644 --- a/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/EchoCallerWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/EchoCallerWorkflowImpl.java @@ -1,16 +1,16 @@ package io.temporal.samples.nexusmultipleargs.caller; import io.temporal.samples.nexus.caller.EchoCallerWorkflow; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.workflow.NexusOperationOptions; import io.temporal.workflow.NexusServiceOptions; import io.temporal.workflow.Workflow; import java.time.Duration; public class EchoCallerWorkflowImpl implements EchoCallerWorkflow { - NexusService nexusService = + SampleNexusService sampleNexusService = Workflow.newNexusServiceStub( - NexusService.class, + SampleNexusService.class, NexusServiceOptions.newBuilder() .setOperationOptions( NexusOperationOptions.newBuilder() @@ -20,6 +20,6 @@ public class EchoCallerWorkflowImpl implements EchoCallerWorkflow { @Override public String echo(String message) { - return nexusService.echo(new NexusService.EchoInput(message)).getMessage(); + return sampleNexusService.echo(new SampleNexusService.EchoInput(message)).getMessage(); } } diff --git a/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/HelloCallerWorkflow.java b/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/HelloCallerWorkflow.java index aeb7a13db..03a8635ed 100644 --- a/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/HelloCallerWorkflow.java +++ b/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/HelloCallerWorkflow.java @@ -1,11 +1,11 @@ package io.temporal.samples.nexusmultipleargs.caller; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.workflow.WorkflowInterface; import io.temporal.workflow.WorkflowMethod; @WorkflowInterface public interface HelloCallerWorkflow { @WorkflowMethod - String hello(String message, NexusService.Language language); + String hello(String message, SampleNexusService.Language language); } diff --git a/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/HelloCallerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/HelloCallerWorkflowImpl.java index 94f4c3fc5..5d3c0824b 100644 --- a/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/HelloCallerWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/HelloCallerWorkflowImpl.java @@ -1,7 +1,7 @@ package io.temporal.samples.nexusmultipleargs.caller; import io.temporal.samples.nexus.caller.HelloCallerWorkflow; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.workflow.NexusOperationHandle; import io.temporal.workflow.NexusOperationOptions; import io.temporal.workflow.NexusServiceOptions; @@ -9,9 +9,9 @@ import java.time.Duration; public class HelloCallerWorkflowImpl implements HelloCallerWorkflow { - NexusService nexusService = + SampleNexusService sampleNexusService = Workflow.newNexusServiceStub( - NexusService.class, + SampleNexusService.class, NexusServiceOptions.newBuilder() .setOperationOptions( NexusOperationOptions.newBuilder() @@ -20,10 +20,10 @@ public class HelloCallerWorkflowImpl implements HelloCallerWorkflow { .build()); @Override - public String hello(String message, NexusService.Language language) { - NexusOperationHandle handle = + public String hello(String message, SampleNexusService.Language language) { + NexusOperationHandle handle = Workflow.startNexusOperation( - nexusService::hello, new NexusService.HelloInput(message, language)); + sampleNexusService::hello, new SampleNexusService.HelloInput(message, language)); // Optionally wait for the operation to be started. NexusOperationExecution will contain the // operation token in case this operation is asynchronous. handle.getExecution().get(); diff --git a/core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/HandlerWorker.java b/core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/HandlerWorker.java index 0ec77889c..c3fd95e9f 100644 --- a/core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/HandlerWorker.java +++ b/core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/HandlerWorker.java @@ -15,7 +15,7 @@ public static void main(String[] args) { Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME); worker.registerWorkflowImplementationTypes(HelloHandlerWorkflowImpl.class); - worker.registerNexusServiceImplementation(new NexusServiceImpl()); + worker.registerNexusServiceImplementation(new SampleNexusServiceImpl()); factory.start(); } diff --git a/core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/HelloHandlerWorkflow.java b/core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/HelloHandlerWorkflow.java index 0a443d468..d13906496 100644 --- a/core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/HelloHandlerWorkflow.java +++ b/core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/HelloHandlerWorkflow.java @@ -1,11 +1,11 @@ package io.temporal.samples.nexusmultipleargs.handler; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.workflow.WorkflowInterface; import io.temporal.workflow.WorkflowMethod; @WorkflowInterface public interface HelloHandlerWorkflow { @WorkflowMethod - NexusService.HelloOutput hello(String name, NexusService.Language language); + SampleNexusService.HelloOutput hello(String name, SampleNexusService.Language language); } diff --git a/core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/HelloHandlerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/HelloHandlerWorkflowImpl.java index b802cee34..9d9cc3733 100644 --- a/core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/HelloHandlerWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/HelloHandlerWorkflowImpl.java @@ -1,22 +1,22 @@ package io.temporal.samples.nexusmultipleargs.handler; import io.temporal.failure.ApplicationFailure; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; public class HelloHandlerWorkflowImpl implements HelloHandlerWorkflow { @Override - public NexusService.HelloOutput hello(String name, NexusService.Language language) { + public SampleNexusService.HelloOutput hello(String name, SampleNexusService.Language language) { switch (language) { case EN: - return new NexusService.HelloOutput("Hello " + name + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Hello " + name + " πŸ‘‹"); case FR: - return new NexusService.HelloOutput("Bonjour " + name + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Bonjour " + name + " πŸ‘‹"); case DE: - return new NexusService.HelloOutput("Hallo " + name + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Hallo " + name + " πŸ‘‹"); case ES: - return new NexusService.HelloOutput("Β‘Hola! " + name + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Β‘Hola! " + name + " πŸ‘‹"); case TR: - return new NexusService.HelloOutput("Merhaba " + name + " πŸ‘‹"); + return new SampleNexusService.HelloOutput("Merhaba " + name + " πŸ‘‹"); } throw ApplicationFailure.newFailure( "Unsupported language: " + language, "UNSUPPORTED_LANGUAGE"); diff --git a/core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/NexusServiceImpl.java b/core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/SampleNexusServiceImpl.java similarity index 86% rename from core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/NexusServiceImpl.java rename to core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/SampleNexusServiceImpl.java index 20d55fed8..b5d819267 100644 --- a/core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/NexusServiceImpl.java +++ b/core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/SampleNexusServiceImpl.java @@ -7,15 +7,15 @@ import io.temporal.nexus.Nexus; import io.temporal.nexus.WorkflowHandle; import io.temporal.nexus.WorkflowRunOperation; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; // To create a service implementation, annotate the class with @ServiceImpl and provide the // interface that the service implements. The service implementation class should have methods that // return OperationHandler that correspond to the operations defined in the service interface. -@ServiceImpl(service = NexusService.class) -public class NexusServiceImpl { +@ServiceImpl(service = SampleNexusService.class) +public class SampleNexusServiceImpl { @OperationImpl - public OperationHandler echo() { + public OperationHandler echo() { // OperationHandler.sync is a meant for exposing simple RPC handlers. return OperationHandler.sync( // The method is for making arbitrary short calls to other services or databases, or @@ -23,11 +23,11 @@ public OperationHandler echo() // calling // Nexus.getOperationContext().getWorkflowClient(ctx) to make arbitrary calls such as // signaling, querying, or listing workflows. - (ctx, details, input) -> new NexusService.EchoOutput(input.getMessage())); + (ctx, details, input) -> new SampleNexusService.EchoOutput(input.getMessage())); } @OperationImpl - public OperationHandler hello() { + public OperationHandler hello() { // If the operation input parameters are different from the workflow input parameters, // use the WorkflowRunOperation.fromWorkflowHandler constructor and the appropriate constructor // method on WorkflowHandle to map the Nexus input to the workflow parameters. diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java index 802fc56ac..ca038ee0e 100644 --- a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java +++ b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java @@ -5,8 +5,8 @@ import io.temporal.samples.nexus.handler.EchoClient; import io.temporal.samples.nexus.handler.HelloHandlerWorkflow; -import io.temporal.samples.nexus.handler.NexusServiceImpl; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.handler.SampleNexusServiceImpl; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.testing.TestWorkflowEnvironment; import io.temporal.testing.TestWorkflowExtension; import io.temporal.worker.Worker; @@ -29,7 +29,7 @@ public class CallerWorkflowJunit5MockTest { // the TestWorkflowExtension will, by default, automatically create a Nexus service // endpoint and workflows registered as part of the TestWorkflowExtension will // automatically inherit the endpoint if none is set. - .setNexusServiceImplementation(new NexusServiceImpl(mockEchoClient)) + .setNexusServiceImplementation(new SampleNexusServiceImpl(mockEchoClient)) // The Echo Nexus handler service just makes a call to a class, so no extra setup is // needed. But the Hello Nexus service needs a worker for both the caller and handler // in order to run, and the Echo Nexus caller service needs a worker. @@ -50,13 +50,13 @@ public void testHelloWorkflow( () -> { HelloHandlerWorkflow mockHandler = mock(HelloHandlerWorkflow.class); when(mockHandler.hello(any())) - .thenReturn(new NexusService.HelloOutput("Hello Mock World πŸ‘‹")); + .thenReturn(new SampleNexusService.HelloOutput("Hello Mock World πŸ‘‹")); return mockHandler; }); testEnv.start(); // Execute a workflow waiting for it to complete. - String greeting = workflow.hello("World", NexusService.Language.EN); + String greeting = workflow.hello("World", SampleNexusService.Language.EN); assertEquals("Hello Mock World πŸ‘‹", greeting); testEnv.shutdown(); @@ -67,7 +67,7 @@ public void testEchoWorkflow( TestWorkflowEnvironment testEnv, Worker worker, EchoCallerWorkflow workflow) { // Sync Nexus operations run inline in the handler thread β€” there is no backing workflow to // register a factory for. Instead, stub the injected EchoClient dependency directly. - when(mockEchoClient.echo(any())).thenReturn(new NexusService.EchoOutput("mocked echo")); + when(mockEchoClient.echo(any())).thenReturn(new SampleNexusService.EchoOutput("mocked echo")); testEnv.start(); // Execute a workflow waiting for it to complete. diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5Test.java b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5Test.java index dc1ee0647..3da730c14 100644 --- a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5Test.java +++ b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5Test.java @@ -3,8 +3,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import io.temporal.samples.nexus.handler.HelloHandlerWorkflowImpl; -import io.temporal.samples.nexus.handler.NexusServiceImpl; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.handler.SampleNexusServiceImpl; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.testing.TestWorkflowEnvironment; import io.temporal.testing.TestWorkflowExtension; import io.temporal.worker.Worker; @@ -23,7 +23,7 @@ public class CallerWorkflowJunit5Test { // the TestWorkflowExtension will, by default, automatically create a Nexus service // endpoint and workflows registered as part of the TestWorkflowExtension will // automatically inherit the endpoint if none is set. - .setNexusServiceImplementation(new NexusServiceImpl()) + .setNexusServiceImplementation(new SampleNexusServiceImpl()) // The Echo Nexus handler service just makes a call to a class, so no extra setup is // needed. But the Hello Nexus service needs a worker for both the caller and handler // in order to run, and the Echo Nexus caller service needs a worker. @@ -44,7 +44,7 @@ public class CallerWorkflowJunit5Test { public void testHelloWorkflow( TestWorkflowEnvironment testEnv, Worker worker, HelloCallerWorkflow workflow) { // Execute a workflow waiting for it to complete. - String greeting = workflow.hello("World", NexusService.Language.EN); + String greeting = workflow.hello("World", SampleNexusService.Language.EN); assertEquals("Hello World πŸ‘‹", greeting); } diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowMockTest.java b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowMockTest.java index b15ac703e..c899713a2 100644 --- a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowMockTest.java +++ b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowMockTest.java @@ -8,8 +8,8 @@ import io.temporal.client.WorkflowOptions; import io.temporal.samples.nexus.handler.EchoClient; import io.temporal.samples.nexus.handler.HelloHandlerWorkflow; -import io.temporal.samples.nexus.handler.NexusServiceImpl; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.handler.SampleNexusServiceImpl; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.testing.TestWorkflowRule; import org.junit.Rule; import org.junit.Test; @@ -30,7 +30,7 @@ public class CallerWorkflowMockTest { // the TestWorkflowRule will, by default, automatically create a Nexus service endpoint // and workflows registered as part of the TestWorkflowRule // will automatically inherit the endpoint if none is set. - .setNexusServiceImplementation(new NexusServiceImpl(mockEchoClient)) + .setNexusServiceImplementation(new SampleNexusServiceImpl(mockEchoClient)) // The Echo Nexus handler service just makes a call to a class, so no extra setup is // needed. But the Hello Nexus service needs a worker for both the caller and handler // in order to run. @@ -52,7 +52,8 @@ public void testHelloWorkflow() { HelloHandlerWorkflow.class, () -> { HelloHandlerWorkflow wf = mock(HelloHandlerWorkflow.class); - when(wf.hello(any())).thenReturn(new NexusService.HelloOutput("Hello Mock World πŸ‘‹")); + when(wf.hello(any())) + .thenReturn(new SampleNexusService.HelloOutput("Hello Mock World πŸ‘‹")); return wf; }); testWorkflowRule.getTestEnvironment().start(); @@ -64,7 +65,7 @@ public void testHelloWorkflow() { .newWorkflowStub( HelloCallerWorkflow.class, WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build()); - String greeting = workflow.hello("World", NexusService.Language.EN); + String greeting = workflow.hello("World", SampleNexusService.Language.EN); assertEquals("Hello Mock World πŸ‘‹", greeting); testWorkflowRule.getTestEnvironment().shutdown(); @@ -74,7 +75,7 @@ public void testHelloWorkflow() { public void testEchoWorkflow() { // Sync Nexus operations run inline in the handler thread β€” there is no backing workflow to // register a factory for. Instead, stub the injected EchoCient dependency directly. - when(mockEchoClient.echo(any())).thenReturn(new NexusService.EchoOutput("mocked echo")); + when(mockEchoClient.echo(any())).thenReturn(new SampleNexusService.EchoOutput("mocked echo")); testWorkflowRule.getTestEnvironment().start(); EchoCallerWorkflow workflow = diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowTest.java b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowTest.java index 6023462df..682995e61 100644 --- a/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowTest.java +++ b/core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowTest.java @@ -4,8 +4,8 @@ import io.temporal.client.WorkflowOptions; import io.temporal.samples.nexus.handler.HelloHandlerWorkflowImpl; -import io.temporal.samples.nexus.handler.NexusServiceImpl; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.handler.SampleNexusServiceImpl; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.testing.TestWorkflowRule; import io.temporal.worker.WorkflowImplementationOptions; import io.temporal.workflow.NexusServiceOptions; @@ -25,7 +25,7 @@ public class CallerWorkflowTest { // the TestWorkflowRule will, by default, automatically create a Nexus service endpoint // and workflows registered as part of the TestWorkflowRule // will automatically inherit the endpoint if none is set. - .setNexusServiceImplementation(new NexusServiceImpl()) + .setNexusServiceImplementation(new SampleNexusServiceImpl()) // The Echo Nexus handler service just makes a call to a class, so no extra setup is // needed. But the Hello Nexus service needs a worker for both the caller and handler // in order to run. @@ -49,7 +49,7 @@ public void testHelloWorkflow() { .newWorkflowStub( HelloCallerWorkflow.class, WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build()); - String greeting = workflow.hello("World", NexusService.Language.EN); + String greeting = workflow.hello("World", SampleNexusService.Language.EN); assertEquals("Hello World πŸ‘‹", greeting); testWorkflowRule.getTestEnvironment().shutdown(); @@ -72,7 +72,7 @@ public void testEchoWorkflow() { WorkflowImplementationOptions.newBuilder() .setNexusServiceOptions( Collections.singletonMap( - "NexusService", + "SampleNexusService", NexusServiceOptions.newBuilder() .setEndpoint(testWorkflowRule.getNexusEndpoint().getSpec().getName()) .build())) diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceJunit5Test.java b/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceJunit5Test.java index 8a1861031..b630c2270 100644 --- a/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceJunit5Test.java +++ b/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceJunit5Test.java @@ -1,12 +1,13 @@ package io.temporal.samples.nexus.caller; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; import io.nexusrpc.handler.OperationHandler; -import io.temporal.samples.nexus.handler.NexusServiceImpl; -import io.temporal.samples.nexus.service.NexusService; +import io.nexusrpc.handler.OperationImpl; +import io.nexusrpc.handler.ServiceImpl; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.testing.TestWorkflowEnvironment; import io.temporal.testing.TestWorkflowExtension; import io.temporal.worker.Worker; @@ -14,45 +15,37 @@ import org.junit.jupiter.api.extension.RegisterExtension; // This unit test example shows how to mock the Nexus service itself in JUnit4. -// In this example since the NexusService itself is mocked, no handlers need to be set up or mocked. - +// This is the path to take when you don't have access to the service implementation so +// cannot mock it. Since the SampleNexusService itself is mocked, +// no handlers need to be set up or mocked. public class NexusServiceJunit5Test { - private final NexusServiceImpl mockNexusService = createMockNexusService(); - - // Mutable fields β€” set these in each test method before starting the environment - // to mock the return values for the Nexus service. - // Instance fields (not static) so each test gets its own copy; safe for parallel execution. - private NexusService.EchoOutput echoResult = new NexusService.EchoOutput("default"); - private NexusService.HelloOutput helloResult = new NexusService.HelloOutput("default"); + private final SampleNexusService mockNexusService = mock(SampleNexusService.class); - private NexusServiceImpl createMockNexusService() { - NexusServiceImpl mock = mock(NexusServiceImpl.class); - // Using OperationHandler.sync for both operations bypasses the need for a backing workflow, - // returning results inline just like a synchronous call. Mocks need to be done before the rule - // is defined, as creating the rule will fail if either call is still null. - // - // The following is the simplest - just mock the services to return a value. But then these - // values cannot change per test case, so this will not always suffice. - // when(mock.echo()) - // .thenReturn( - // OperationHandler.sync( - // (ctx, details, input) -> new NexusService.EchoOutput("mocked echo"))); - // when(mock.hello()) - // .thenReturn( - // OperationHandler.sync( - // (ctx, details, input) -> new NexusService.HelloOutput("Hello Mock World - // πŸ‘‹"))); - // - // An alternative approach is to create the mocks but set them to return a value that is set in - // the unit - // test class above. That allows you to change the return value in each test. - when(mock.echo()).thenReturn(OperationHandler.sync((ctx, details, input) -> echoResult)); - when(mock.hello()).thenReturn(OperationHandler.sync((ctx, details, input) -> helloResult)); + /** + * A test-only Nexus service implementation that delegates to the Mockito mock defined above. Both + * operations are implemented as synchronous handlers that forward calls to the mock, allowing + * full control over return values and verification of inputs. + */ + @ServiceImpl(service = SampleNexusService.class) + public class TestNexusServiceImpl { + @OperationImpl + public OperationHandler echo() { + return OperationHandler.sync((ctx, details, input) -> mockNexusService.echo(input)); + } - return mock; + @OperationImpl + public OperationHandler hello() { + return OperationHandler.sync((ctx, details, input) -> mockNexusService.hello(input)); + } } + // Using OperationHandler.sync for both operations bypasses the need for a backing workflow, + // returning results inline just like a synchronous call. + // + // Note that the Mocks need to be done before the extension + // is defined, as creating the rule will fail if either call is still null. + @RegisterExtension public final TestWorkflowExtension testWorkflowExtension = TestWorkflowExtension.newBuilder() @@ -60,7 +53,7 @@ private NexusServiceImpl createMockNexusService() { // the TestWorkflowExtension will, by default, automatically create a Nexus service // endpoint and workflows registered as part of the TestWorkflowExtension will // automatically inherit the endpoint if none is set. - .setNexusServiceImplementation(mockNexusService) + .setNexusServiceImplementation(new TestNexusServiceImpl()) // The Echo Nexus handler service just makes a call to a class, so no extra setup is // needed. But the Hello Nexus service needs a worker for both the caller and handler // in order to run, and the Echo Nexus caller service needs a worker. @@ -80,20 +73,31 @@ private NexusServiceImpl createMockNexusService() { @Test public void testHelloWorkflow( TestWorkflowEnvironment testEnv, Worker worker, HelloCallerWorkflow workflow) { + // Set the mock value to return - helloResult = new NexusService.HelloOutput("Hello Mock World πŸ‘‹"); + when(mockNexusService.hello(any())) + .thenReturn(new SampleNexusService.HelloOutput("Hello Mock World πŸ‘‹")); + // Execute a workflow waiting for it to complete. - String greeting = workflow.hello("World", NexusService.Language.EN); + String greeting = workflow.hello("World", SampleNexusService.Language.EN); assertEquals("Hello Mock World πŸ‘‹", greeting); } @Test public void testEchoWorkflow( TestWorkflowEnvironment testEnv, Worker worker, EchoCallerWorkflow workflow) { - // Set the mock value to return - echoResult = new NexusService.EchoOutput("mocked echo"); + when(mockNexusService.echo(any())) + .thenReturn(new SampleNexusService.EchoOutput("echo response")); + // Execute a workflow waiting for it to complete. - String greeting = workflow.echo("Hello"); - assertEquals("mocked echo", greeting); + String greeting = workflow.echo("echo input"); + assertEquals("echo response", greeting); + + // Verify the echo operation was called exactly once and no other operations were invoked + verify(mockNexusService, times(1)).echo(any()); + // Verify the Nexus service was called with the correct input + verify(mockNexusService).echo(argThat(input -> "echo input".equals(input.getMessage()))); + + verifyNoMoreInteractions(mockNexusService); } } diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceMockTest.java b/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceMockTest.java index 6090a3fc9..75922673b 100644 --- a/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceMockTest.java +++ b/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceMockTest.java @@ -1,87 +1,61 @@ package io.temporal.samples.nexus.caller; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; import io.nexusrpc.handler.OperationHandler; +import io.nexusrpc.handler.OperationImpl; +import io.nexusrpc.handler.ServiceImpl; import io.temporal.client.WorkflowOptions; -import io.temporal.samples.nexus.handler.NexusServiceImpl; -import io.temporal.samples.nexus.service.NexusService; +import io.temporal.samples.nexus.service.SampleNexusService; import io.temporal.testing.TestWorkflowRule; import org.junit.Rule; import org.junit.Test; // This unit test example shows how to mock the Nexus service itself in JUnit4. -// In this example since the NexusService itself is mocked, no handlers need to be set up or mocked. - +// This is the path to take when you don't have access to the service implementation so +// cannot mock it. Since the SampleNexusService itself is mocked, +// no handlers need to be set up or mocked. public class NexusServiceMockTest { - // Stubs must be set up before TestWorkflowRule initializes, because ServiceImplInstance calls - // each @OperationImpl method on the mock during TestWorkflowRule.init() to capture the - // OperationHandler objects. Any stub set up after that point has no effect. - // JUnit 4 creates a new test class instance per test method, so this mock is fresh each time. - private final NexusServiceImpl mockNexusService = createMockNexusService(); - - // Mutable fields β€” set these in each test method before starting the environment - // to mock the return values for the Nexus service. - // Instance fields (not static) so each test gets its own copy; safe for parallel execution. - private NexusService.EchoOutput echoResult = new NexusService.EchoOutput("default"); - private NexusService.HelloOutput helloResult = new NexusService.HelloOutput("default"); - - private NexusServiceImpl createMockNexusService() { - NexusServiceImpl mock = mock(NexusServiceImpl.class); - - // Using OperationHandler.sync for both operations bypasses the need for a backing workflow, - // returning results inline just like a synchronous call. Mocks need to be done before the rule - // is defined, as creating the rule will fail if either call is still null. - // - // The following is the simplest - just mock the services to return a value. But then these - // values cannot change per test case, so this will not always suffice. - // when(mock.echo()) - // .thenReturn( - // OperationHandler.sync( - // (ctx, details, input) -> new NexusService.EchoOutput("mocked echo"))); - // when(mock.hello()) - // .thenReturn( - // OperationHandler.sync( - // (ctx, details, input) -> new NexusService.HelloOutput("Hello Mock World - // πŸ‘‹"))); - // - // An alternative approach is to create the mocks but set them to return a value that is set in - // the unit - // test class above. That allows you to change the return value in each test. - when(mock.echo()).thenReturn(OperationHandler.sync((ctx, details, input) -> echoResult)); - when(mock.hello()).thenReturn(OperationHandler.sync((ctx, details, input) -> helloResult)); - - return mock; + private final SampleNexusService mockNexusService = mock(SampleNexusService.class); + + /** + * A test-only Nexus service implementation that delegates to the Mockito mock defined above. Both + * operations are implemented as synchronous handlers that forward calls to the mock, allowing + * full control over return values and verification of inputs. + */ + @ServiceImpl(service = SampleNexusService.class) + public class TestNexusServiceImpl { + @OperationImpl + public OperationHandler echo() { + return OperationHandler.sync((ctx, details, input) -> mockNexusService.echo(input)); + } + + @OperationImpl + public OperationHandler hello() { + return OperationHandler.sync((ctx, details, input) -> mockNexusService.hello(input)); + } } + // Using OperationHandler.sync for both operations bypasses the need for a backing workflow, + // returning results inline just like a synchronous call. + // + // Note that the Mocks need to be done before the rule + // is defined, as creating the rule will fail if either call is still null. + @Rule public TestWorkflowRule testWorkflowRule = TestWorkflowRule.newBuilder() - // If a Nexus service is registered as part of the test as in the following line of code, - // the TestWorkflowRule will, by default, automatically create a Nexus service endpoint - // and workflows registered as part of the TestWorkflowRule - // will automatically inherit the endpoint if none is set. - .setNexusServiceImplementation(mockNexusService) - // The Echo Nexus handler service just makes a call to a class, so no extra setup is - // needed. But the Hello Nexus service needs a worker for both the caller and handler - // in order to run. - // setWorkflowTypes will take the classes given and create workers for them, enabling - // workflows to run. This creates caller workflows, the handler workflows - // will be mocked in the test methods. - .setWorkflowTypes(HelloCallerWorkflowImpl.class, EchoCallerWorkflowImpl.class) - // Disable automatic worker startup as we are going to register some workflows manually - // per test - .setDoNotStart(true) + .setNexusServiceImplementation(new TestNexusServiceImpl()) + .setWorkflowTypes(EchoCallerWorkflowImpl.class, HelloCallerWorkflowImpl.class) .build(); @Test - public void testHelloWorkflow() { - // Set the mock value to return - helloResult = new NexusService.HelloOutput("Hello Mock World πŸ‘‹"); - testWorkflowRule.getTestEnvironment().start(); + public void testHelloCallerWithMockedService() { + when(mockNexusService.hello(any())) + .thenReturn(new SampleNexusService.HelloOutput("Bonjour World")); HelloCallerWorkflow workflow = testWorkflowRule @@ -89,17 +63,23 @@ public void testHelloWorkflow() { .newWorkflowStub( HelloCallerWorkflow.class, WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build()); - String greeting = workflow.hello("World", NexusService.Language.EN); - assertEquals("Hello Mock World πŸ‘‹", greeting); - testWorkflowRule.getTestEnvironment().shutdown(); + String result = workflow.hello("World", SampleNexusService.Language.FR); + assertEquals("Bonjour World", result); + + // Verify the Nexus service was called with the correct name and language + verify(mockNexusService) + .hello( + argThat( + input -> + "World".equals(input.getName()) + && SampleNexusService.Language.FR == input.getLanguage())); } @Test - public void testEchoWorkflow() { - // Set the mock value to return - echoResult = new NexusService.EchoOutput("mocked echo"); - testWorkflowRule.getTestEnvironment().start(); + public void testEchoCallerWithMockedService() { + when(mockNexusService.echo(any())) + .thenReturn(new SampleNexusService.EchoOutput("echo response")); EchoCallerWorkflow workflow = testWorkflowRule @@ -107,9 +87,16 @@ public void testEchoWorkflow() { .newWorkflowStub( EchoCallerWorkflow.class, WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build()); - String greeting = workflow.echo("Hello"); - assertEquals("mocked echo", greeting); - testWorkflowRule.getTestEnvironment().shutdown(); + String echoOutput = workflow.echo("echo input"); + + assertEquals("echo response", echoOutput); + + // Verify the echo operation was called exactly once and no other operations were invoked + verify(mockNexusService, times(1)).echo(any()); + // Verify the Nexus service was called with the correct input + verify(mockNexusService).echo(argThat(input -> "echo input".equals(input.getMessage()))); + + verifyNoMoreInteractions(mockNexusService); } } From 75b5fed7f1ad18bc447603dd2592a7ab9c9e7091 Mon Sep 17 00:00:00 2001 From: Evan Reynolds Date: Tue, 24 Mar 2026 14:50:43 -0700 Subject: [PATCH 7/7] Suppressing two false warnings that turned into build errors --- .../temporal/samples/nexus/caller/NexusServiceJunit5Test.java | 2 ++ .../io/temporal/samples/nexus/caller/NexusServiceMockTest.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceJunit5Test.java b/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceJunit5Test.java index b630c2270..c5fd578c0 100644 --- a/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceJunit5Test.java +++ b/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceJunit5Test.java @@ -30,11 +30,13 @@ public class NexusServiceJunit5Test { @ServiceImpl(service = SampleNexusService.class) public class TestNexusServiceImpl { @OperationImpl + @SuppressWarnings("DirectInvocationOnMock") public OperationHandler echo() { return OperationHandler.sync((ctx, details, input) -> mockNexusService.echo(input)); } @OperationImpl + @SuppressWarnings("DirectInvocationOnMock") public OperationHandler hello() { return OperationHandler.sync((ctx, details, input) -> mockNexusService.hello(input)); } diff --git a/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceMockTest.java b/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceMockTest.java index 75922673b..c2264ec65 100644 --- a/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceMockTest.java +++ b/core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceMockTest.java @@ -29,11 +29,13 @@ public class NexusServiceMockTest { @ServiceImpl(service = SampleNexusService.class) public class TestNexusServiceImpl { @OperationImpl + @SuppressWarnings("DirectInvocationOnMock") public OperationHandler echo() { return OperationHandler.sync((ctx, details, input) -> mockNexusService.echo(input)); } @OperationImpl + @SuppressWarnings("DirectInvocationOnMock") public OperationHandler hello() { return OperationHandler.sync((ctx, details, input) -> mockNexusService.hello(input)); }