Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ jobs:

- name: Run cloud test
if: ${{ steps.cloud-test-eligibility.outputs.enabled == 'true' }}
timeout-minutes: 15
timeout-minutes: 30
env:
USER: unittest
TEMPORAL_TEST_ENV_CONFIG_SERVER: "true"
Expand All @@ -184,10 +184,7 @@ jobs:
TEMPORAL_CLIENT_CLOUD_NAMESPACE: ${{ steps.create-cloud-namespace.outputs.namespace }}
TEMPORAL_CLIENT_CLOUD_API_KEY: ${{ secrets.TEMPORAL_CLIENT_CLOUD_API_KEY }}
TEMPORAL_CLIENT_CLOUD_API_VERSION: v0.19.1
run: |
./gradlew --no-daemon :temporal-sdk:test \
--tests '*CloudOperationsClientTest' \
--tests 'io.temporal.client.functional.SignalTest.signalCompletedWorkflow'
run: ./gradlew --no-daemon :temporal-sdk:testCloud

- name: Delete Cloud namespace
id: delete-cloud-namespace
Expand All @@ -207,7 +204,7 @@ jobs:
uses: mikepenz/action-junit-report@bccf2e31636835cf0874589931c4116687171386 # v6
if: success() || failure() # always run even if the previous step fails
with:
report_paths: "**/build/test-results/test/TEST-*.xml"
report_paths: "**/build/test-results/testCloud/TEST-*.xml"

code_format:
name: Code format
Expand Down
7 changes: 7 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ Values from `TEMPORAL_ADDRESS`, `TEMPORAL_NAMESPACE`, `TEMPORAL_API_KEY`, `TEMPO
`TEMPORAL_GRPC_META_*` override the selected profile. Envconfig mode connects to an existing server
and namespace; it does not create or register either one.

The `:temporal-sdk:testCloud` task runs tests that are eligible for Temporal Cloud. It uses the same
envconfig variables and excludes tests annotated with a `CloudTestExclusion` JUnit category. Tests
are Cloud-eligible by default; use the narrowest applicable exclusion reason when a test requires a
local server, requires Cloud resources that CI does not provision, or still needs Cloud-specific
adaptation. Run `./gradlew :temporal-sdk:testCloud --test-dry-run` to inventory the selected tests
without executing them. The normal `test` task continues to run Cloud-excluded tests locally.

## Things to Avoid

Avoid changes that make review harder without improving the contribution:
Expand Down
28 changes: 28 additions & 0 deletions temporal-sdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,34 @@ test {
}
}

task testCloud(type: Test) {
group = 'verification'
description = 'Runs temporal-sdk tests that are eligible for Temporal Cloud.'
testClassesDirs = sourceSets.test.output.classesDirs
classpath = sourceSets.test.runtimeClasspath
if (project.hasProperty('testJavaVersion')) {
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(project.property('testJavaVersion') as int)
}
}
useJUnit {
excludeCategories 'io.temporal.testing.CloudTestExclusion'
excludeCategories 'io.temporal.worker.IndependentResourceBasedTests'
}
testLogging {
events 'passed', 'skipped', 'failed'
exceptionFormat 'full'
showStandardStreams true
}
forkEvery = 1
maxParallelForks = Math.max(Runtime.runtime.availableProcessors().intdiv(2), 1) ?: 1
afterTest { TestDescriptor descriptor, TestResult result ->
if (result.resultType == org.gradle.api.tasks.testing.TestResult.ResultType.FAILURE) {
failedTests << ["${descriptor.className}::${descriptor.name}"]
}
}
}

// On Java 17+, prepend java17 classes to all test classpaths so that Class.forName finds
// the real Jackson3JsonPayloadConverter instead of the Java 8 stub. This lets us test
// the present-java17-but-absent-jackson3 behavior (NoClassDefFoundError) in the same
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ public void getActivityInfo() {
Assert.assertEquals(ACTIVITY_OPTIONS.getStartToCloseTimeout(), info.startToCloseTimeout);
Assert.assertEquals(ACTIVITY_OPTIONS.getHeartbeatTimeout(), info.heartbeatTimeout);
Assert.assertEquals(ActivityInfoWorkflow.class.getSimpleName(), info.workflowType);
Assert.assertEquals(SDKTestWorkflowRule.NAMESPACE, info.namespace);
Assert.assertEquals(
testWorkflowRule.getWorkflowClient().getOptions().getNamespace(), info.namespace);
Assert.assertEquals(testWorkflowRule.getTaskQueue(), info.activityTaskQueue);
Assert.assertFalse(info.isLocal);
Assert.assertEquals(0, info.priorityKey);
Expand All @@ -98,7 +99,8 @@ public void getLocalActivityInfo() {
Assert.assertTrue(info.startToCloseTimeout.isZero());
Assert.assertTrue(info.heartbeatTimeout.isZero());
Assert.assertEquals(ActivityInfoWorkflow.class.getSimpleName(), info.workflowType);
Assert.assertEquals(SDKTestWorkflowRule.NAMESPACE, info.namespace);
Assert.assertEquals(
testWorkflowRule.getWorkflowClient().getOptions().getNamespace(), info.namespace);
Assert.assertEquals(testWorkflowRule.getTaskQueue(), info.activityTaskQueue);
Assert.assertTrue(info.isLocal);
Assert.assertEquals(0, info.priorityKey);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.temporal.client.WorkflowClient;
import io.temporal.client.WorkflowOptions;
import io.temporal.serviceclient.WorkflowServiceStubsOptions;
import io.temporal.testing.CloudTestExclusion.RequiresLocalServer;
import io.temporal.testing.TestEnvironmentOptions;
import io.temporal.testing.TestWorkflowEnvironment;
import io.temporal.worker.Worker;
Expand All @@ -20,9 +21,12 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;

// This test directly creates and controls a local test service.
@Category(RequiresLocalServer.class)
public class AuthorizationTokenTest {
private static Metadata.Key<String> TEMPORAL_NAMESPACE_HEADER_KEY =
Metadata.Key.of("temporal-namespace", Metadata.ASCII_STRING_MARSHALLER);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ public void run() {
private ActivityClient newActivityClient() {
return ActivityClient.newInstance(
testWorkflowRule.getWorkflowClient().getWorkflowServiceStubs(),
ActivityClientOptions.newBuilder().setNamespace(SDKTestWorkflowRule.NAMESPACE).build());
ActivityClientOptions.newBuilder()
.setNamespace(testWorkflowRule.getWorkflowClient().getOptions().getNamespace())
.build());
}

private StartActivityOptions slowOpts() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ public void setUp() {
activityClient =
ActivityClient.newInstance(
clientStubs,
ActivityClientOptions.newBuilder().setNamespace(SDKTestWorkflowRule.NAMESPACE).build());
ActivityClientOptions.newBuilder()
.setNamespace(testWorkflowRule.getWorkflowClient().getOptions().getNamespace())
.build());
}

@After
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ public void run() {
private ActivityClient newActivityClient() {
return ActivityClient.newInstance(
testWorkflowRule.getWorkflowClient().getWorkflowServiceStubs(),
ActivityClientOptions.newBuilder().setNamespace(SDKTestWorkflowRule.NAMESPACE).build());
ActivityClientOptions.newBuilder()
.setNamespace(testWorkflowRule.getWorkflowClient().getOptions().getNamespace())
.build());
}

private StartActivityOptions slowOpts() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.temporal.client.functional;

import static io.temporal.testUtils.Eventually.assertEventually;
import static io.temporal.testing.internal.SDKTestWorkflowRule.NAMESPACE;
import static junit.framework.TestCase.*;
import static org.junit.Assume.assumeTrue;

Expand Down Expand Up @@ -54,22 +53,26 @@ public class MetricsTest {
private final ActivityClient activityClient =
ActivityClient.newInstance(
testWorkflowRule.getWorkflowServiceStubs(),
ActivityClientOptions.newBuilder().setNamespace(SDKTestWorkflowRule.NAMESPACE).build());

private static final List<Tag> TAGS_NAMESPACE =
MetricsTag.defaultTags(NAMESPACE).entrySet().stream()
.map(
nameValueEntry ->
new ImmutableTag(nameValueEntry.getKey(), nameValueEntry.getValue()))
.collect(Collectors.toList());
ActivityClientOptions.newBuilder()
.setNamespace(testWorkflowRule.getWorkflowClient().getOptions().getNamespace())
.build());

private List<Tag> tagsNamespace;
private List<Tag> tagsNamespaceQueue;

@Before
public void setUp() {
registry.clear();
tagsNamespace =
MetricsTag.defaultTags(testWorkflowRule.getWorkflowClient().getOptions().getNamespace())
.entrySet()
.stream()
.map(
nameValueEntry ->
new ImmutableTag(nameValueEntry.getKey(), nameValueEntry.getValue()))
.collect(Collectors.toList());
tagsNamespaceQueue =
replaceTags(TAGS_NAMESPACE, MetricsTag.TASK_QUEUE, testWorkflowRule.getTaskQueue());
replaceTags(tagsNamespace, MetricsTag.TASK_QUEUE, testWorkflowRule.getTaskQueue());
}

@After
Expand Down Expand Up @@ -97,7 +100,7 @@ public void testSynchronousStartAndGetResult() throws InterruptedException {
MetricsTag.WORKFLOW_TYPE,
"QuicklyCompletingWorkflow");
List<Tag> longPollRequestTags =
replaceTag(TAGS_NAMESPACE, MetricsTag.OPERATION_NAME, "GetWorkflowExecutionHistory");
replaceTag(tagsNamespace, MetricsTag.OPERATION_NAME, "GetWorkflowExecutionHistory");

assertEventually(
Duration.ofSeconds(2),
Expand Down Expand Up @@ -130,7 +133,7 @@ public void testAsynchronousStartAndGetResult() throws InterruptedException, Exe
MetricsTag.WORKFLOW_TYPE,
"QuicklyCompletingWorkflow");
List<Tag> longPollRequestTags =
replaceTag(TAGS_NAMESPACE, MetricsTag.OPERATION_NAME, "GetWorkflowExecutionHistory");
replaceTag(tagsNamespace, MetricsTag.OPERATION_NAME, "GetWorkflowExecutionHistory");

assertEventually(
Duration.ofSeconds(2),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,9 @@ private StartActivityOptions simpleOpts(String id) {
private ActivityClient newActivityClient() {
return ActivityClient.newInstance(
testWorkflowRule.getWorkflowServiceStubs(),
ActivityClientOptions.newBuilder().setNamespace(SDKTestWorkflowRule.NAMESPACE).build());
ActivityClientOptions.newBuilder()
.setNamespace(testWorkflowRule.getWorkflowClient().getOptions().getNamespace())
.build());
}

@Test
Expand Down Expand Up @@ -516,7 +518,7 @@ public void testStartActivityInterceptorsAreCalledProperly() throws InterruptedE
ActivityClient.newInstance(
testWorkflowRule.getWorkflowServiceStubs(),
ActivityClientOptions.newBuilder()
.setNamespace(SDKTestWorkflowRule.NAMESPACE)
.setNamespace(testWorkflowRule.getWorkflowClient().getOptions().getNamespace())
.setInterceptors(Collections.singletonList(interceptor))
.build());

Expand Down Expand Up @@ -570,7 +572,7 @@ public void testExecuteActivityWorkerActivityInfoIsAccurate() {

assertEquals(activityId, info.activityId);
assertEquals("InspectInfo", info.activityType);
assertEquals(SDKTestWorkflowRule.NAMESPACE, info.namespace);
assertEquals(testWorkflowRule.getWorkflowClient().getOptions().getNamespace(), info.namespace);
assertEquals(testWorkflowRule.getTaskQueue(), info.taskQueue);
assertFalse(info.isLocal);
assertFalse(info.isInWorkflow);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import io.temporal.client.WorkflowTargetOptions;
import io.temporal.common.WorkflowExecutionHistory;
import io.temporal.internal.common.ProtobufTimeUtils;
import io.temporal.testing.CloudTestExclusion.RequiresLocalServer;
import io.temporal.testing.internal.SDKTestOptions;
import io.temporal.testing.internal.SDKTestWorkflowRule;
import io.temporal.workflow.shared.TestMultiArgWorkflowFunctions.*;
Expand All @@ -21,6 +22,7 @@
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;

public class StartTest {

Expand Down Expand Up @@ -70,6 +72,8 @@ public void startNoArgFuncWithRejectDuplicate() {
"func", stubF.func()); // Check that duplicated start just returns the result.
}

// This test exercises behavior that is only supported by the local test server.
@Category(RequiresLocalServer.class)
@Test
public void startOneArgsFuncWithDefault() {
// TODO why it doesn't work with external service?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import io.temporal.client.UntypedNexusOperationHandle;
import io.temporal.client.UntypedNexusServiceClient;
import io.temporal.failure.ApplicationFailure;
import io.temporal.testing.CloudTestExclusion.RequiresCloudProvisioning;
import io.temporal.testing.internal.SDKTestWorkflowRule;
import io.temporal.workflow.shared.EchoNexusServiceImpl;
import io.temporal.workflow.shared.TestNexusServices;
Expand All @@ -25,6 +26,7 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;

/**
* Coverage tests for the {@link CompletableFuture}-returning surface on the standalone Nexus
Expand All @@ -33,6 +35,8 @@
* UntypedNexusOperationHandle}. Each overload is asserted against the existing sync echo handler so
* the Java async API is exercised without depending on server-side async completion.
*/
// Cloud CI does not provision the standalone Nexus endpoint required by this test.
@Category(RequiresCloudProvisioning.class)
public class NexusAsyncApiTest {

@Rule
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import io.temporal.client.UntypedNexusOperationHandle;
import io.temporal.client.UntypedNexusServiceClient;
import io.temporal.nexus.TemporalOperationHandler;
import io.temporal.testing.CloudTestExclusion.RequiresCloudProvisioning;
import io.temporal.testing.internal.SDKTestWorkflowRule;
import io.temporal.workflow.shared.EchoNexusServiceImpl;
import io.temporal.workflow.shared.TestNexusServices;
Expand All @@ -41,7 +42,10 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;

// Cloud CI does not provision the standalone Nexus endpoint required by this test.
@Category(RequiresCloudProvisioning.class)
public class NexusClientTest {

private final AtomicInteger activityInvocationCount = new AtomicInteger();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import io.temporal.client.UntypedNexusOperationHandle;
import io.temporal.client.UntypedNexusServiceClient;
import io.temporal.failure.ApplicationFailure;
import io.temporal.testing.CloudTestExclusion.RequiresCloudProvisioning;
import io.temporal.testing.internal.SDKTestWorkflowRule;
import io.temporal.workflow.shared.EchoNexusServiceImpl;
import io.temporal.workflow.shared.TestNexusServices;
Expand All @@ -24,12 +25,15 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;

/**
* Tests for {@link UntypedNexusOperationHandle} per-execution lifecycle methods returned by {@link
* NexusClient#getHandle(String, String)}: {@code describe()}, {@code cancel()}/{@code
* cancel(reason)}, and {@code terminate()}/{@code terminate(reason)}.
*/
// Cloud CI does not provision the standalone Nexus endpoint required by this test.
@Category(RequiresCloudProvisioning.class)
public class NexusOperationHandleTest {

@Rule
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import io.temporal.client.NexusServiceClient;
import io.temporal.client.StartNexusOperationOptions;
import io.temporal.client.UntypedNexusOperationHandle;
import io.temporal.testing.CloudTestExclusion.RequiresCloudProvisioning;
import io.temporal.testing.internal.SDKTestWorkflowRule;
import io.temporal.workflow.shared.EchoNexusServiceImpl;
import io.temporal.workflow.shared.TestNexusServices;
Expand All @@ -23,11 +24,14 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;

/**
* End-to-end tests for {@link NexusServiceClient}: typed start/execute via {@link
* io.temporal.workflow.Functions.Func2} method references.
*/
// Cloud CI does not provision the standalone Nexus endpoint required by this test.
@Category(RequiresCloudProvisioning.class)
public class NexusServiceClientTest {

@Rule
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import io.temporal.failure.CanceledFailure;
import io.temporal.nexus.Nexus;
import io.temporal.nexus.WorkflowRunOperation;
import io.temporal.testing.CloudTestExclusion.RequiresCloudProvisioning;
import io.temporal.testing.internal.SDKTestWorkflowRule;
import io.temporal.workflow.Workflow;
import io.temporal.workflow.WorkflowInterface;
Expand All @@ -36,13 +37,16 @@
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;

/**
* Behavior tests for standalone Nexus operations whose handler is {@link WorkflowRunOperation},
* i.e. each SANO is backed by a workflow. Shares one fixture (a workflow that awaits forever) so
* individual tests can exercise cancel propagation, bidirectional link plumbing, and any other
* behavior that depends on the SANO ↔ backing-workflow relationship.
*/
// Cloud CI does not provision the standalone Nexus endpoint required by this test.
@Category(RequiresCloudProvisioning.class)
public class StandaloneNexusBackingWorkflowTest {

static final AtomicReference<String> capturedWorkflowId = new AtomicReference<>();
Expand Down
Loading
Loading