|
| 1 | +package io.temporal.workflow.shared; |
| 2 | + |
| 3 | +import io.grpc.Status; |
| 4 | +import io.grpc.StatusRuntimeException; |
| 5 | +import io.temporal.api.workflowservice.v1.CountNexusOperationExecutionsRequest; |
| 6 | +import io.temporal.serviceclient.WorkflowServiceStubs; |
| 7 | +import io.temporal.serviceclient.WorkflowServiceStubsOptions; |
| 8 | +import io.temporal.testing.internal.SDKTestWorkflowRule; |
| 9 | +import org.junit.Assume; |
| 10 | + |
| 11 | +/** |
| 12 | + * Skip-guard for tests that exercise standalone Nexus operation RPCs ({@code |
| 13 | + * StartNexusOperationExecution}, {@code DescribeNexusOperationExecution}, etc.). Call from |
| 14 | + * {@code @BeforeClass}. |
| 15 | + * |
| 16 | + * <p>{@link SDKTestWorkflowRule#useExternalService} only tells us that an external server is |
| 17 | + * reachable — not that it implements every RPC we want to call. The Temporal CLI's {@code |
| 18 | + * start-dev} server (which CI uses for the "Unit test with CLI" job) accepts connections but |
| 19 | + * returns {@code UNIMPLEMENTED} for the standalone Nexus RPCs. Hitting that mid-test produces a |
| 20 | + * confusing failure; this guard probes the server once and skips the suite cleanly if the RPCs |
| 21 | + * aren't wired through. |
| 22 | + * |
| 23 | + * <p>Suites are skipped via {@link Assume#assumeTrue} when: |
| 24 | + * |
| 25 | + * <ul> |
| 26 | + * <li>the rule is using the in-memory test server ({@code USE_EXTERNAL_SERVICE} unset/false), or |
| 27 | + * <li>the configured external server returns {@code UNIMPLEMENTED} for a standalone Nexus RPC. |
| 28 | + * </ul> |
| 29 | + */ |
| 30 | +public final class StandaloneNexusTestPrerequisites { |
| 31 | + |
| 32 | + private static volatile Boolean cachedServerSupportsStandaloneNexus; |
| 33 | + private static final Object PROBE_LOCK = new Object(); |
| 34 | + |
| 35 | + private StandaloneNexusTestPrerequisites() {} |
| 36 | + |
| 37 | + /** |
| 38 | + * Skips the calling suite if the configured server doesn't support standalone Nexus RPCs. Probes |
| 39 | + * the server at most once per JVM and caches the outcome. |
| 40 | + */ |
| 41 | + public static void requireServerSupport() { |
| 42 | + Assume.assumeTrue( |
| 43 | + "standalone Nexus operations require an external server (USE_EXTERNAL_SERVICE=true)", |
| 44 | + SDKTestWorkflowRule.useExternalService); |
| 45 | + Assume.assumeTrue( |
| 46 | + "configured external server does not implement standalone Nexus RPCs", |
| 47 | + probeServerSupport()); |
| 48 | + } |
| 49 | + |
| 50 | + private static boolean probeServerSupport() { |
| 51 | + Boolean cached = cachedServerSupportsStandaloneNexus; |
| 52 | + if (cached != null) { |
| 53 | + return cached; |
| 54 | + } |
| 55 | + synchronized (PROBE_LOCK) { |
| 56 | + if (cachedServerSupportsStandaloneNexus != null) { |
| 57 | + return cachedServerSupportsStandaloneNexus; |
| 58 | + } |
| 59 | + cachedServerSupportsStandaloneNexus = probeOnce(); |
| 60 | + return cachedServerSupportsStandaloneNexus; |
| 61 | + } |
| 62 | + } |
| 63 | + |
| 64 | + private static boolean probeOnce() { |
| 65 | + String address = System.getenv("TEMPORAL_SERVICE_ADDRESS"); |
| 66 | + if (address == null || address.isEmpty()) { |
| 67 | + address = "127.0.0.1:7233"; |
| 68 | + } |
| 69 | + WorkflowServiceStubs stubs = |
| 70 | + WorkflowServiceStubs.newServiceStubs( |
| 71 | + WorkflowServiceStubsOptions.newBuilder().setTarget(address).build()); |
| 72 | + try { |
| 73 | + stubs |
| 74 | + .blockingStub() |
| 75 | + .countNexusOperationExecutions( |
| 76 | + CountNexusOperationExecutionsRequest.newBuilder().setNamespace("default").build()); |
| 77 | + return true; |
| 78 | + } catch (StatusRuntimeException e) { |
| 79 | + // UNIMPLEMENTED is the only status that tells us the RPC method isn't wired through. Every |
| 80 | + // other status (NOT_FOUND for an absent namespace, INVALID_ARGUMENT, PERMISSION_DENIED, etc.) |
| 81 | + // proves the method exists on the server even if this particular call was rejected. |
| 82 | + return e.getStatus().getCode() != Status.Code.UNIMPLEMENTED; |
| 83 | + } finally { |
| 84 | + stubs.shutdownNow(); |
| 85 | + } |
| 86 | + } |
| 87 | +} |
0 commit comments