From 53769e5cd19a31ea8196209a6b6ee6cdd9ed303b Mon Sep 17 00:00:00 2001 From: Thomas Hardy Date: Tue, 11 Aug 2026 12:42:05 -0400 Subject: [PATCH 1/5] Add envconfig support to test harness --- .github/workflows/ci.yml | 9 +- CONTRIBUTING.md | 18 +++ temporal-sdk/build.gradle | 3 +- temporal-testing/build.gradle | 2 + .../docker/RegisterTestNamespace.java | 5 +- .../ExternalServiceTestConfigurator.java | 141 +++++++++++++++--- .../ExternalServiceTestConfiguratorTest.java | 122 +++++++++++++++ 7 files changed, 275 insertions(+), 25 deletions(-) create mode 100644 temporal-testing/src/test/java/io/temporal/testing/internal/ExternalServiceTestConfiguratorTest.java diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c147f7e9a..cacd39c0e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -139,10 +139,17 @@ jobs: if: ${{ github.event.pull_request.head.repo.full_name == '' || github.event.pull_request.head.repo.full_name == 'temporalio/sdk-java' }} env: USER: unittest + TEMPORAL_TEST_ENV_CONFIG_SERVER: "true" + TEMPORAL_ADDRESS: sdk-ci.a2dd6.tmprl.cloud:7233 + TEMPORAL_NAMESPACE: sdk-ci.a2dd6 + TEMPORAL_API_KEY: ${{ secrets.TEMPORAL_CLIENT_CLOUD_API_KEY }} TEMPORAL_CLIENT_CLOUD_NAMESPACE: sdk-ci.a2dd6 TEMPORAL_CLIENT_CLOUD_API_KEY: ${{ secrets.TEMPORAL_CLIENT_CLOUD_API_KEY }} TEMPORAL_CLIENT_CLOUD_API_VERSION: 2024-05-13-00 - run: ./gradlew --no-daemon :temporal-sdk:test --tests '*CloudOperationsClientTest' + run: | + ./gradlew --no-daemon :temporal-sdk:test \ + --tests '*CloudOperationsClientTest' \ + --tests 'io.temporal.client.functional.SignalTest.signalCompletedWorkflow' - name: Publish Test Report uses: mikepenz/action-junit-report@bccf2e31636835cf0874589931c4116687171386 # v6 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d5b8881133..1dc11851ae 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -94,6 +94,24 @@ Normal Gradle test filtering works, so a single dev-server-backed test can be ru Java 11 must be available to Gradle for these commands. +To run an SDK test against an externally managed server using the standard Temporal client +environment configuration, set `TEMPORAL_TEST_ENV_CONFIG_SERVER`. For example, the following runs +one Cloud-safe workflow test: + +```bash +TEMPORAL_TEST_ENV_CONFIG_SERVER=true \ +TEMPORAL_ADDRESS=your-namespace.tmprl.cloud:7233 \ +TEMPORAL_NAMESPACE=your-namespace \ +TEMPORAL_API_KEY=your-api-key \ +./gradlew :temporal-sdk:test \ + --tests 'io.temporal.client.functional.SignalTest.signalCompletedWorkflow' +``` + +The harness also supports the standard `TEMPORAL_CONFIG_FILE` and `TEMPORAL_PROFILE` variables. +Values from `TEMPORAL_ADDRESS`, `TEMPORAL_NAMESPACE`, `TEMPORAL_API_KEY`, `TEMPORAL_TLS_*`, and +`TEMPORAL_GRPC_META_*` override the selected profile. Envconfig mode connects to an existing server +and namespace; it does not create or register either one. + ## Things to Avoid Avoid changes that make review harder without improving the contribution: diff --git a/temporal-sdk/build.gradle b/temporal-sdk/build.gradle index d7b090e5b5..594a7660e0 100644 --- a/temporal-sdk/build.gradle +++ b/temporal-sdk/build.gradle @@ -25,6 +25,7 @@ dependencies { } testImplementation project(':temporal-testing') + testRuntimeOnly project(':temporal-envconfig') testImplementation "junit:junit:${junitVersion}" testImplementation "org.mockito:mockito-core:${mockitoVersion}" testImplementation 'pl.pragmatists:JUnitParams:1.1.1' @@ -287,4 +288,4 @@ testing { tasks.named('check') { dependsOn(testing.suites.jackson3Tests) dependsOn(testing.suites.virtualThreadTests) -} \ No newline at end of file +} diff --git a/temporal-testing/build.gradle b/temporal-testing/build.gradle index f9ca013456..10a7ddc6a1 100644 --- a/temporal-testing/build.gradle +++ b/temporal-testing/build.gradle @@ -16,6 +16,7 @@ java { dependencies { api project(':temporal-sdk') api project(':temporal-test-server') + compileOnly project(':temporal-envconfig') implementation 'org.apache.commons:commons-compress:1.28.0' @@ -32,6 +33,7 @@ dependencies { junit5Api 'org.junit.jupiter:junit-jupiter-api' testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter' + testRuntimeOnly project(':temporal-envconfig') testRuntimeOnly group: 'ch.qos.logback', name: 'logback-classic', version: "${logbackVersion}" } diff --git a/temporal-testing/src/main/java/io/temporal/internal/docker/RegisterTestNamespace.java b/temporal-testing/src/main/java/io/temporal/internal/docker/RegisterTestNamespace.java index c840adf55c..b43a9ca3cf 100644 --- a/temporal-testing/src/main/java/io/temporal/internal/docker/RegisterTestNamespace.java +++ b/temporal-testing/src/main/java/io/temporal/internal/docker/RegisterTestNamespace.java @@ -7,6 +7,7 @@ import io.temporal.api.workflowservice.v1.ListNamespacesRequest; import io.temporal.api.workflowservice.v1.ListNamespacesResponse; import io.temporal.api.workflowservice.v1.RegisterNamespaceRequest; +import io.temporal.internal.common.env.EnvironmentVariableUtils; import io.temporal.serviceclient.WorkflowServiceStubs; import io.temporal.serviceclient.WorkflowServiceStubsOptions; @@ -15,10 +16,12 @@ public class RegisterTestNamespace { public static final String NAMESPACE = "UnitTest"; private static final boolean useExternalService = Boolean.parseBoolean(System.getenv("USE_EXTERNAL_SERVICE")); + private static final boolean useEnvConfig = + EnvironmentVariableUtils.readBooleanFlag("TEMPORAL_TEST_ENV_CONFIG_SERVER"); private static final String serviceAddress = System.getenv("TEMPORAL_SERVICE_ADDRESS"); public static void main(String[] args) throws InterruptedException { - if (!useExternalService) { + if (!useExternalService || useEnvConfig) { return; } diff --git a/temporal-testing/src/main/java/io/temporal/testing/internal/ExternalServiceTestConfigurator.java b/temporal-testing/src/main/java/io/temporal/testing/internal/ExternalServiceTestConfigurator.java index 6c68d5f2b5..a722f5041e 100644 --- a/temporal-testing/src/main/java/io/temporal/testing/internal/ExternalServiceTestConfigurator.java +++ b/temporal-testing/src/main/java/io/temporal/testing/internal/ExternalServiceTestConfigurator.java @@ -1,43 +1,69 @@ package io.temporal.testing.internal; -import io.temporal.internal.common.env.EnvironmentVariableUtils; +import io.temporal.envconfig.ClientConfigProfile; +import io.temporal.envconfig.LoadClientConfigProfileOptions; import io.temporal.testing.TestEnvironmentOptions; import io.temporal.testing.TestWorkflowRule; import io.temporal.testing.internal.devserver.SdkJavaTestServerProfile; +import java.io.IOException; +import java.util.Map; import javax.annotation.Nonnull; public class ExternalServiceTestConfigurator { - private static boolean USE_EXTERNAL_SERVICE = - EnvironmentVariableUtils.readBooleanFlag("USE_EXTERNAL_SERVICE"); - private static String TEMPORAL_SERVICE_ADDRESS = - EnvironmentVariableUtils.readString("TEMPORAL_SERVICE_ADDRESS"); - private static boolean USE_VIRTUAL_THREADS = - EnvironmentVariableUtils.readBooleanFlag("USE_VIRTUAL_THREADS"); + static final String TEMPORAL_TEST_ENV_CONFIG_SERVER = "TEMPORAL_TEST_ENV_CONFIG_SERVER"; + private static final String USE_EXTERNAL_SERVICE = "USE_EXTERNAL_SERVICE"; + private static final String TEMPORAL_SERVICE_ADDRESS = "TEMPORAL_SERVICE_ADDRESS"; + private static final String USE_VIRTUAL_THREADS = "USE_VIRTUAL_THREADS"; public static boolean isUseExternalService() { - return USE_EXTERNAL_SERVICE || SdkJavaTestServerProfile.isActive(); + return isUseExternalService(System.getenv()) || SdkJavaTestServerProfile.isActive(); } public static boolean isUseVirtualThreads() { - return USE_VIRTUAL_THREADS; + return readBooleanFlag(System.getenv(), USE_VIRTUAL_THREADS); } public static String getTemporalServiceAddress() { - if (SdkJavaTestServerProfile.isActive()) { - return SdkJavaTestServerProfile.getTarget(); + Map environment = System.getenv(); + if (readBooleanFlag(environment, TEMPORAL_TEST_ENV_CONFIG_SERVER)) { + return getTemporalServiceAddress(environment); } - return USE_EXTERNAL_SERVICE - ? (TEMPORAL_SERVICE_ADDRESS != null ? TEMPORAL_SERVICE_ADDRESS : "127.0.0.1:7233") - : null; + String devServerTarget = SdkJavaTestServerProfile.getTarget(); + if (devServerTarget != null) { + return devServerTarget; + } + return getTemporalServiceAddress(environment); } public static TestWorkflowRule.Builder configure( @Nonnull TestWorkflowRule.Builder testWorkflowRule) { - if (isUseExternalService()) { + return configure(testWorkflowRule, System.getenv(), SdkJavaTestServerProfile.getTarget()); + } + + static TestWorkflowRule.Builder configure( + TestWorkflowRule.Builder testWorkflowRule, Map environment) { + return configure(testWorkflowRule, environment, null); + } + + static TestWorkflowRule.Builder configure( + TestWorkflowRule.Builder testWorkflowRule, + Map environment, + String devServerTarget) { + ClientConfigProfile profile = loadEnvConfigProfile(environment); + if (profile != null) { + testWorkflowRule.setUseExternalService(true); + testWorkflowRule.setTarget(profile.getAddress()); + testWorkflowRule.setNamespace(profile.getNamespace()); + testWorkflowRule.setWorkflowServiceStubsOptions(profile.toWorkflowServiceStubsOptions()); + testWorkflowRule.setWorkflowClientOptions(profile.toWorkflowClientOptions()); + } else if (devServerTarget != null) { + testWorkflowRule.setUseExternalService(true); + testWorkflowRule.setTarget(devServerTarget); + } else if (readBooleanFlag(environment, USE_EXTERNAL_SERVICE)) { testWorkflowRule.setUseExternalService(true); - String target = getTemporalServiceAddress(); - if (target != null) { - testWorkflowRule.setTarget(target); + String serviceAddress = environment.get(TEMPORAL_SERVICE_ADDRESS); + if (serviceAddress != null) { + testWorkflowRule.setTarget(serviceAddress); } } return testWorkflowRule; @@ -45,11 +71,33 @@ public static TestWorkflowRule.Builder configure( public static TestEnvironmentOptions.Builder configure( @Nonnull TestEnvironmentOptions.Builder testEnvironmentOptions) { - if (isUseExternalService()) { + return configure(testEnvironmentOptions, System.getenv(), SdkJavaTestServerProfile.getTarget()); + } + + static TestEnvironmentOptions.Builder configure( + TestEnvironmentOptions.Builder testEnvironmentOptions, Map environment) { + return configure(testEnvironmentOptions, environment, null); + } + + static TestEnvironmentOptions.Builder configure( + TestEnvironmentOptions.Builder testEnvironmentOptions, + Map environment, + String devServerTarget) { + ClientConfigProfile profile = loadEnvConfigProfile(environment); + if (profile != null) { testEnvironmentOptions.setUseExternalService(true); - String target = getTemporalServiceAddress(); - if (target != null) { - testEnvironmentOptions.setTarget(target); + testEnvironmentOptions.setTarget(profile.getAddress()); + testEnvironmentOptions.setWorkflowServiceStubsOptions( + profile.toWorkflowServiceStubsOptions()); + testEnvironmentOptions.setWorkflowClientOptions(profile.toWorkflowClientOptions()); + } else if (devServerTarget != null) { + testEnvironmentOptions.setUseExternalService(true); + testEnvironmentOptions.setTarget(devServerTarget); + } else if (readBooleanFlag(environment, USE_EXTERNAL_SERVICE)) { + testEnvironmentOptions.setUseExternalService(true); + String serviceAddress = environment.get(TEMPORAL_SERVICE_ADDRESS); + if (serviceAddress != null) { + testEnvironmentOptions.setTarget(serviceAddress); } } return testEnvironmentOptions; @@ -58,4 +106,53 @@ public static TestEnvironmentOptions.Builder configure( public static TestEnvironmentOptions.Builder configuredTestEnvironmentOptions() { return configure(TestEnvironmentOptions.newBuilder()); } + + static boolean isUseExternalService(Map environment) { + return readBooleanFlag(environment, TEMPORAL_TEST_ENV_CONFIG_SERVER) + || readBooleanFlag(environment, USE_EXTERNAL_SERVICE); + } + + static String getTemporalServiceAddress(Map environment) { + ClientConfigProfile profile = loadEnvConfigProfile(environment); + if (profile != null) { + return profile.getAddress(); + } + return readBooleanFlag(environment, USE_EXTERNAL_SERVICE) + ? (environment.get(TEMPORAL_SERVICE_ADDRESS) != null + ? environment.get(TEMPORAL_SERVICE_ADDRESS) + : "127.0.0.1:7233") + : null; + } + + private static ClientConfigProfile loadEnvConfigProfile(Map environment) { + if (!readBooleanFlag(environment, TEMPORAL_TEST_ENV_CONFIG_SERVER)) { + return null; + } + + ClientConfigProfile profile; + try { + profile = + ClientConfigProfile.load( + LoadClientConfigProfileOptions.newBuilder().setEnvOverrides(environment).build()); + } catch (IOException e) { + throw new IllegalStateException( + "Unable to load client configuration for the Temporal test harness.", e); + } + if (profile.getAddress() == null || profile.getAddress().isEmpty()) { + throw new IllegalStateException("Envconfig test harness requires a Temporal server address."); + } + if (profile.getNamespace() == null || profile.getNamespace().isEmpty()) { + throw new IllegalStateException("Envconfig test harness requires a Temporal namespace."); + } + return profile; + } + + private static boolean readBooleanFlag(Map environment, String variableName) { + String value = environment.get(variableName); + if (value == null) { + return false; + } + value = value.trim(); + return !Boolean.FALSE.toString().equalsIgnoreCase(value) && !"0".equals(value); + } } diff --git a/temporal-testing/src/test/java/io/temporal/testing/internal/ExternalServiceTestConfiguratorTest.java b/temporal-testing/src/test/java/io/temporal/testing/internal/ExternalServiceTestConfiguratorTest.java new file mode 100644 index 0000000000..d02c28479f --- /dev/null +++ b/temporal-testing/src/test/java/io/temporal/testing/internal/ExternalServiceTestConfiguratorTest.java @@ -0,0 +1,122 @@ +package io.temporal.testing.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.grpc.Metadata; +import io.temporal.serviceclient.WorkflowServiceStubsOptions; +import io.temporal.testing.TestEnvironmentOptions; +import io.temporal.testing.TestWorkflowRule; +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import org.junit.jupiter.api.Test; + +public class ExternalServiceTestConfiguratorTest { + + @Test + public void configureTestWorkflowRuleFromEnvConfig() { + Map environment = newEnvConfigEnvironment(); + environment.put("USE_EXTERNAL_SERVICE", "true"); + environment.put("TEMPORAL_SERVICE_ADDRESS", "legacy-address:7233"); + + TestWorkflowRule rule = + ExternalServiceTestConfigurator.configure(TestWorkflowRule.newBuilder(), environment) + .build(); + try { + assertTrue(rule.isUseExternalService()); + assertEquals( + "envconfig-address:7233", rule.getWorkflowServiceStubs().getOptions().getTarget()); + assertEquals("envconfig-namespace", rule.getWorkflowClient().getOptions().getNamespace()); + + WorkflowServiceStubsOptions stubsOptions = rule.getWorkflowServiceStubs().getOptions(); + assertTrue(stubsOptions.getEnableHttps()); + Metadata metadata = new Metadata(); + stubsOptions + .getGrpcMetadataProviders() + .forEach(provider -> metadata.merge(provider.getMetadata())); + assertEquals( + "metadata-value", + metadata.get(Metadata.Key.of("test-header", Metadata.ASCII_STRING_MARSHALLER))); + assertEquals( + "Bearer api-key", + metadata.get(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER))); + } finally { + rule.getTestEnvironment().close(); + } + } + + @Test + public void preserveLegacyAndLocalModes() { + Map environment = new HashMap<>(); + TestEnvironmentOptions localOptions = + ExternalServiceTestConfigurator.configure(TestEnvironmentOptions.newBuilder(), environment) + .build(); + assertFalse(localOptions.isUseExternalService()); + + environment.put("USE_EXTERNAL_SERVICE", "true"); + environment.put("TEMPORAL_SERVICE_ADDRESS", "legacy-address:7233"); + TestEnvironmentOptions externalOptions = + ExternalServiceTestConfigurator.configure(TestEnvironmentOptions.newBuilder(), environment) + .build(); + assertTrue(externalOptions.isUseExternalService()); + assertEquals("legacy-address:7233", externalOptions.getTarget()); + } + + @Test + public void preserveDevServerProfilePrecedence() { + Map environment = new HashMap<>(); + environment.put("USE_EXTERNAL_SERVICE", "true"); + environment.put("TEMPORAL_SERVICE_ADDRESS", "legacy-address:7233"); + + TestEnvironmentOptions devServerOptions = + ExternalServiceTestConfigurator.configure( + TestEnvironmentOptions.newBuilder(), environment, "dev-server-address:7233") + .build(); + assertTrue(devServerOptions.isUseExternalService()); + assertEquals("dev-server-address:7233", devServerOptions.getTarget()); + + TestEnvironmentOptions envConfigOptions = + ExternalServiceTestConfigurator.configure( + TestEnvironmentOptions.newBuilder(), + newEnvConfigEnvironment(), + "dev-server-address:7233") + .build(); + assertEquals("envconfig-address:7233", envConfigOptions.getTarget()); + assertEquals("envconfig-namespace", envConfigOptions.getWorkflowClientOptions().getNamespace()); + } + + @Test + public void requireAddressAndNamespaceInEnvConfigMode() { + Map environment = new HashMap<>(); + environment.put(ExternalServiceTestConfigurator.TEMPORAL_TEST_ENV_CONFIG_SERVER, "true"); + environment.put("TEMPORAL_CONFIG_FILE", nonExistentConfigFile()); + environment.put("TEMPORAL_ADDRESS", "envconfig-address:7233"); + + IllegalStateException exception = + assertThrows( + IllegalStateException.class, + () -> + ExternalServiceTestConfigurator.configure( + TestEnvironmentOptions.newBuilder(), environment)); + assertEquals("Envconfig test harness requires a Temporal namespace.", exception.getMessage()); + } + + private static Map newEnvConfigEnvironment() { + Map environment = new HashMap<>(); + environment.put(ExternalServiceTestConfigurator.TEMPORAL_TEST_ENV_CONFIG_SERVER, "true"); + environment.put("TEMPORAL_CONFIG_FILE", nonExistentConfigFile()); + environment.put("TEMPORAL_ADDRESS", "envconfig-address:7233"); + environment.put("TEMPORAL_NAMESPACE", "envconfig-namespace"); + environment.put("TEMPORAL_API_KEY", "api-key"); + environment.put("TEMPORAL_GRPC_META_TEST_HEADER", "metadata-value"); + return environment; + } + + private static String nonExistentConfigFile() { + return new File("build/non-existent-envconfig-" + UUID.randomUUID()).getAbsolutePath(); + } +} From 4fd3de42d20f1de47f0a5989578c7f02cb4dfaa8 Mon Sep 17 00:00:00 2001 From: Thomas Hardy Date: Mon, 24 Aug 2026 14:39:07 -0400 Subject: [PATCH 2/5] Keep envconfig harness PR focused --- .github/workflows/ci.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cacd39c0e9..1c147f7e9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -139,17 +139,10 @@ jobs: if: ${{ github.event.pull_request.head.repo.full_name == '' || github.event.pull_request.head.repo.full_name == 'temporalio/sdk-java' }} env: USER: unittest - TEMPORAL_TEST_ENV_CONFIG_SERVER: "true" - TEMPORAL_ADDRESS: sdk-ci.a2dd6.tmprl.cloud:7233 - TEMPORAL_NAMESPACE: sdk-ci.a2dd6 - TEMPORAL_API_KEY: ${{ secrets.TEMPORAL_CLIENT_CLOUD_API_KEY }} TEMPORAL_CLIENT_CLOUD_NAMESPACE: sdk-ci.a2dd6 TEMPORAL_CLIENT_CLOUD_API_KEY: ${{ secrets.TEMPORAL_CLIENT_CLOUD_API_KEY }} TEMPORAL_CLIENT_CLOUD_API_VERSION: 2024-05-13-00 - run: | - ./gradlew --no-daemon :temporal-sdk:test \ - --tests '*CloudOperationsClientTest' \ - --tests 'io.temporal.client.functional.SignalTest.signalCompletedWorkflow' + run: ./gradlew --no-daemon :temporal-sdk:test --tests '*CloudOperationsClientTest' - name: Publish Test Report uses: mikepenz/action-junit-report@bccf2e31636835cf0874589931c4116687171386 # v6 From 2743f3cbd1e4ead5c3e5dbe5bc4b5433f02e7420 Mon Sep 17 00:00:00 2001 From: Thomas Hardy Date: Mon, 24 Aug 2026 15:19:58 -0400 Subject: [PATCH 3/5] Preserve test options with envconfig --- .../ExternalServiceTestConfigurator.java | 104 +++++++++++++++++- .../testing/internal/SDKTestWorkflowRule.java | 33 +++++- .../ExternalServiceTestConfiguratorTest.java | 92 +++++++++++++++- 3 files changed, 221 insertions(+), 8 deletions(-) diff --git a/temporal-testing/src/main/java/io/temporal/testing/internal/ExternalServiceTestConfigurator.java b/temporal-testing/src/main/java/io/temporal/testing/internal/ExternalServiceTestConfigurator.java index a722f5041e..15982b8bec 100644 --- a/temporal-testing/src/main/java/io/temporal/testing/internal/ExternalServiceTestConfigurator.java +++ b/temporal-testing/src/main/java/io/temporal/testing/internal/ExternalServiceTestConfigurator.java @@ -1,11 +1,19 @@ package io.temporal.testing.internal; +import io.grpc.Metadata; +import io.temporal.client.ActivityClientOptions; +import io.temporal.client.WorkflowClientOptions; import io.temporal.envconfig.ClientConfigProfile; import io.temporal.envconfig.LoadClientConfigProfileOptions; +import io.temporal.serviceclient.GrpcMetadataProvider; +import io.temporal.serviceclient.WorkflowServiceStubsOptions; import io.temporal.testing.TestEnvironmentOptions; import io.temporal.testing.TestWorkflowRule; import io.temporal.testing.internal.devserver.SdkJavaTestServerProfile; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Map; import javax.annotation.Nonnull; @@ -49,13 +57,30 @@ static TestWorkflowRule.Builder configure( TestWorkflowRule.Builder testWorkflowRule, Map environment, String devServerTarget) { + return configure(testWorkflowRule, environment, devServerTarget, true); + } + + static TestWorkflowRule.Builder configureConnection( + TestWorkflowRule.Builder testWorkflowRule, + Map environment, + String devServerTarget) { + return configure(testWorkflowRule, environment, devServerTarget, false); + } + + private static TestWorkflowRule.Builder configure( + TestWorkflowRule.Builder testWorkflowRule, + Map environment, + String devServerTarget, + boolean configureOptions) { ClientConfigProfile profile = loadEnvConfigProfile(environment); if (profile != null) { testWorkflowRule.setUseExternalService(true); testWorkflowRule.setTarget(profile.getAddress()); testWorkflowRule.setNamespace(profile.getNamespace()); - testWorkflowRule.setWorkflowServiceStubsOptions(profile.toWorkflowServiceStubsOptions()); - testWorkflowRule.setWorkflowClientOptions(profile.toWorkflowClientOptions()); + if (configureOptions) { + testWorkflowRule.setWorkflowServiceStubsOptions(profile.toWorkflowServiceStubsOptions()); + testWorkflowRule.setWorkflowClientOptions(profile.toWorkflowClientOptions()); + } } else if (devServerTarget != null) { testWorkflowRule.setUseExternalService(true); testWorkflowRule.setTarget(devServerTarget); @@ -107,6 +132,81 @@ public static TestEnvironmentOptions.Builder configuredTestEnvironmentOptions() return configure(TestEnvironmentOptions.newBuilder()); } + static WorkflowServiceStubsOptions configure( + WorkflowServiceStubsOptions workflowServiceStubsOptions, Map environment) { + ClientConfigProfile profile = loadEnvConfigProfile(environment); + if (profile == null) { + return workflowServiceStubsOptions; + } + + WorkflowServiceStubsOptions profileOptions = profile.toWorkflowServiceStubsOptions(); + GrpcMetadataProvider metadataProvider = + mergeMetadata( + profileOptions.getHeaders(), + profileOptions.getGrpcMetadataProviders(), + workflowServiceStubsOptions.getHeaders(), + workflowServiceStubsOptions.getGrpcMetadataProviders()); + return WorkflowServiceStubsOptions.newBuilder(workflowServiceStubsOptions) + .setChannel(null) + .setTarget(profileOptions.getTarget()) + .setEnableHttps(profileOptions.getEnableHttps()) + .setSslContext(profileOptions.getSslContext()) + .setChannelInitializer(profileOptions.getChannelInitializer()) + .setHeaders(new Metadata()) + .setGrpcMetadataProviders(Collections.singletonList(metadataProvider)) + .build(); + } + + private static GrpcMetadataProvider mergeMetadata( + Metadata profileHeaders, + Iterable profileProviders, + Metadata testHeaders, + Iterable testProviders) { + List profileProviderList = new ArrayList<>(); + profileProviders.forEach(profileProviderList::add); + return () -> { + Metadata metadata = new Metadata(); + if (testHeaders != null) { + metadata.merge(testHeaders); + } + testProviders.forEach(provider -> metadata.merge(provider.getMetadata())); + Metadata profileMetadata = new Metadata(); + if (profileHeaders != null) { + profileMetadata.merge(profileHeaders); + } + profileProviderList.forEach(provider -> profileMetadata.merge(provider.getMetadata())); + for (String keyName : profileMetadata.keys()) { + if (keyName.endsWith("-bin")) { + metadata.discardAll(Metadata.Key.of(keyName, Metadata.BINARY_BYTE_MARSHALLER)); + } else { + metadata.discardAll(Metadata.Key.of(keyName, Metadata.ASCII_STRING_MARSHALLER)); + } + } + metadata.merge(profileMetadata); + return metadata; + }; + } + + static WorkflowClientOptions configure( + WorkflowClientOptions workflowClientOptions, Map environment) { + ClientConfigProfile profile = loadEnvConfigProfile(environment); + return profile == null + ? workflowClientOptions + : WorkflowClientOptions.newBuilder(workflowClientOptions) + .setNamespace(profile.getNamespace()) + .build(); + } + + static ActivityClientOptions configure( + ActivityClientOptions activityClientOptions, Map environment) { + ClientConfigProfile profile = loadEnvConfigProfile(environment); + return profile == null + ? activityClientOptions + : ActivityClientOptions.newBuilder(activityClientOptions) + .setNamespace(profile.getNamespace()) + .build(); + } + static boolean isUseExternalService(Map environment) { return readBooleanFlag(environment, TEMPORAL_TEST_ENV_CONFIG_SERVER) || readBooleanFlag(environment, USE_EXTERNAL_SERVICE); diff --git a/temporal-testing/src/main/java/io/temporal/testing/internal/SDKTestWorkflowRule.java b/temporal-testing/src/main/java/io/temporal/testing/internal/SDKTestWorkflowRule.java index 9c81461212..c6109c5ec5 100644 --- a/temporal-testing/src/main/java/io/temporal/testing/internal/SDKTestWorkflowRule.java +++ b/temporal-testing/src/main/java/io/temporal/testing/internal/SDKTestWorkflowRule.java @@ -31,6 +31,7 @@ import io.temporal.serviceclient.WorkflowServiceStubsOptions; import io.temporal.testing.TestWorkflowEnvironment; import io.temporal.testing.TestWorkflowRule; +import io.temporal.testing.internal.devserver.SdkJavaTestServerProfile; import io.temporal.worker.*; import io.temporal.workflow.Functions; import java.io.File; @@ -40,6 +41,7 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.UUID; import java.util.concurrent.*; import javax.annotation.Nonnull; @@ -91,7 +93,9 @@ private SDKTestWorkflowRule(SDKTestWorkflowRule.Builder builder) { : null; testWorkflowRule = - ExternalServiceTestConfigurator.configure(builder.testWorkflowRuleBuilder).build(); + ExternalServiceTestConfigurator.configureConnection( + builder.testWorkflowRuleBuilder, builder.environment, builder.devServerTarget) + .build(); } public static Builder newBuilder() { @@ -104,24 +108,43 @@ public static class Builder { private boolean workerFactoryOptionsAreSet = false; private boolean workerOptionsAreSet = false; private final TestWorkflowRule.Builder testWorkflowRuleBuilder; + private final Map environment; + @Nullable private final String devServerTarget; public Builder() { - testWorkflowRuleBuilder = TestWorkflowRule.newBuilder(); + this(System.getenv(), SdkJavaTestServerProfile.getTarget()); + } + + Builder(Map environment, @Nullable String devServerTarget) { + this.environment = environment; + this.devServerTarget = devServerTarget; + testWorkflowRuleBuilder = + ExternalServiceTestConfigurator.configure( + TestWorkflowRule.newBuilder(), environment, devServerTarget); } public Builder setWorkflowServiceStubsOptions( WorkflowServiceStubsOptions workflowServiceStubsOptions) { - testWorkflowRuleBuilder.setWorkflowServiceStubsOptions(workflowServiceStubsOptions); + if (workflowServiceStubsOptions != null) { + testWorkflowRuleBuilder.setWorkflowServiceStubsOptions( + ExternalServiceTestConfigurator.configure(workflowServiceStubsOptions, environment)); + } return this; } public Builder setWorkflowClientOptions(WorkflowClientOptions workflowClientOptions) { - testWorkflowRuleBuilder.setWorkflowClientOptions(workflowClientOptions); + if (workflowClientOptions != null) { + testWorkflowRuleBuilder.setWorkflowClientOptions( + ExternalServiceTestConfigurator.configure(workflowClientOptions, environment)); + } return this; } public Builder setActivityClientOptions(ActivityClientOptions activityClientOptions) { - testWorkflowRuleBuilder.setActivityClientOptions(activityClientOptions); + if (activityClientOptions != null) { + testWorkflowRuleBuilder.setActivityClientOptions( + ExternalServiceTestConfigurator.configure(activityClientOptions, environment)); + } return this; } diff --git a/temporal-testing/src/test/java/io/temporal/testing/internal/ExternalServiceTestConfiguratorTest.java b/temporal-testing/src/test/java/io/temporal/testing/internal/ExternalServiceTestConfiguratorTest.java index d02c28479f..0432605634 100644 --- a/temporal-testing/src/test/java/io/temporal/testing/internal/ExternalServiceTestConfiguratorTest.java +++ b/temporal-testing/src/test/java/io/temporal/testing/internal/ExternalServiceTestConfiguratorTest.java @@ -6,10 +6,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import io.grpc.Metadata; +import io.temporal.client.WorkflowClientOptions; import io.temporal.serviceclient.WorkflowServiceStubsOptions; import io.temporal.testing.TestEnvironmentOptions; import io.temporal.testing.TestWorkflowRule; import java.io.File; +import java.time.Duration; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -27,7 +29,6 @@ public void configureTestWorkflowRuleFromEnvConfig() { ExternalServiceTestConfigurator.configure(TestWorkflowRule.newBuilder(), environment) .build(); try { - assertTrue(rule.isUseExternalService()); assertEquals( "envconfig-address:7233", rule.getWorkflowServiceStubs().getOptions().getTarget()); assertEquals("envconfig-namespace", rule.getWorkflowClient().getOptions().getNamespace()); @@ -89,6 +90,95 @@ public void preserveDevServerProfilePrecedence() { assertEquals("envconfig-namespace", envConfigOptions.getWorkflowClientOptions().getNamespace()); } + @Test + public void preserveTestOptionsWhenApplyingEnvConfigConnection() { + Map environment = newEnvConfigEnvironment(); + Metadata.Key customHeader = + Metadata.Key.of("custom-option-header", Metadata.ASCII_STRING_MARSHALLER); + Metadata.Key authorizationHeader = + Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER); + Metadata.Key profileHeader = + Metadata.Key.of("test-header", Metadata.ASCII_STRING_MARSHALLER); + Metadata.Key fixedHeader = + Metadata.Key.of("custom-fixed-header", Metadata.ASCII_STRING_MARSHALLER); + Metadata fixedHeaders = new Metadata(); + fixedHeaders.put(fixedHeader, "custom-fixed-value"); + fixedHeaders.put(authorizationHeader, "Bearer stale-fixed-api-key"); + fixedHeaders.put(profileHeader, "stale-fixed-metadata-value"); + WorkflowServiceStubsOptions serviceOptions = + WorkflowServiceStubsOptions.newBuilder() + .setRpcTimeout(Duration.ofSeconds(17)) + .setHeaders(fixedHeaders) + .addGrpcMetadataProvider( + () -> { + Metadata metadata = new Metadata(); + metadata.put(customHeader, "custom-option-value"); + metadata.put(authorizationHeader, "Bearer stale-api-key"); + metadata.put(profileHeader, "stale-metadata-value"); + return metadata; + }) + .build(); + + WorkflowServiceStubsOptions configuredServiceOptions = + ExternalServiceTestConfigurator.configure(serviceOptions, environment); + assertEquals("envconfig-address:7233", configuredServiceOptions.getTarget()); + assertEquals(Duration.ofSeconds(17), configuredServiceOptions.getRpcTimeout()); + assertTrue(configuredServiceOptions.getEnableHttps()); + Metadata metadata = new Metadata(); + configuredServiceOptions + .getGrpcMetadataProviders() + .forEach(provider -> metadata.merge(provider.getMetadata())); + assertEquals("custom-option-value", metadata.get(customHeader)); + assertEquals("custom-fixed-value", metadata.get(fixedHeader)); + assertEquals("metadata-value", metadata.get(profileHeader)); + assertEquals("Bearer api-key", metadata.get(authorizationHeader)); + int authorizationValues = 0; + for (String ignored : metadata.getAll(authorizationHeader)) { + authorizationValues++; + } + assertEquals(1, authorizationValues); + + WorkflowClientOptions clientOptions = + WorkflowClientOptions.newBuilder() + .setNamespace("test-option-namespace") + .setIdentity("test-option-identity") + .build(); + WorkflowClientOptions configuredClientOptions = + ExternalServiceTestConfigurator.configure(clientOptions, environment); + assertEquals("envconfig-namespace", configuredClientOptions.getNamespace()); + assertEquals("test-option-identity", configuredClientOptions.getIdentity()); + } + + @Test + public void sdkRuleReappliesEnvConfigConnectionAfterTestCustomization() { + Map environment = newEnvConfigEnvironment(); + SDKTestWorkflowRule rule = + new SDKTestWorkflowRule.Builder(environment, null) + .setUseExternalService(false) + .setTarget("test-option-address:7233") + .setNamespace("test-option-namespace") + .setWorkflowServiceStubsOptions( + WorkflowServiceStubsOptions.newBuilder() + .setRpcTimeout(Duration.ofSeconds(17)) + .build()) + .setWorkflowClientOptions( + WorkflowClientOptions.newBuilder() + .setNamespace("test-option-namespace") + .setIdentity("test-option-identity") + .build()) + .build(); + try { + assertEquals( + "envconfig-address:7233", rule.getWorkflowServiceStubs().getOptions().getTarget()); + assertEquals( + Duration.ofSeconds(17), rule.getWorkflowServiceStubs().getOptions().getRpcTimeout()); + assertEquals("envconfig-namespace", rule.getWorkflowClient().getOptions().getNamespace()); + assertEquals("test-option-identity", rule.getWorkflowClient().getOptions().getIdentity()); + } finally { + rule.getTestEnvironment().close(); + } + } + @Test public void requireAddressAndNamespaceInEnvConfigMode() { Map environment = new HashMap<>(); From fdf3b7645679680ceb48a80f147ba48c801d200c Mon Sep 17 00:00:00 2001 From: Thomas Hardy Date: Mon, 24 Aug 2026 17:23:00 -0400 Subject: [PATCH 4/5] Handle envconfig profiles without metadata --- .../internal/ExternalServiceTestConfigurator.java | 8 ++++++-- .../ExternalServiceTestConfiguratorTest.java | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/temporal-testing/src/main/java/io/temporal/testing/internal/ExternalServiceTestConfigurator.java b/temporal-testing/src/main/java/io/temporal/testing/internal/ExternalServiceTestConfigurator.java index 15982b8bec..95c213f219 100644 --- a/temporal-testing/src/main/java/io/temporal/testing/internal/ExternalServiceTestConfigurator.java +++ b/temporal-testing/src/main/java/io/temporal/testing/internal/ExternalServiceTestConfigurator.java @@ -163,13 +163,17 @@ private static GrpcMetadataProvider mergeMetadata( Metadata testHeaders, Iterable testProviders) { List profileProviderList = new ArrayList<>(); - profileProviders.forEach(profileProviderList::add); + if (profileProviders != null) { + profileProviders.forEach(profileProviderList::add); + } return () -> { Metadata metadata = new Metadata(); if (testHeaders != null) { metadata.merge(testHeaders); } - testProviders.forEach(provider -> metadata.merge(provider.getMetadata())); + if (testProviders != null) { + testProviders.forEach(provider -> metadata.merge(provider.getMetadata())); + } Metadata profileMetadata = new Metadata(); if (profileHeaders != null) { profileMetadata.merge(profileHeaders); diff --git a/temporal-testing/src/test/java/io/temporal/testing/internal/ExternalServiceTestConfiguratorTest.java b/temporal-testing/src/test/java/io/temporal/testing/internal/ExternalServiceTestConfiguratorTest.java index 0432605634..cd7a72114e 100644 --- a/temporal-testing/src/test/java/io/temporal/testing/internal/ExternalServiceTestConfiguratorTest.java +++ b/temporal-testing/src/test/java/io/temporal/testing/internal/ExternalServiceTestConfiguratorTest.java @@ -149,6 +149,21 @@ public void preserveTestOptionsWhenApplyingEnvConfigConnection() { assertEquals("test-option-identity", configuredClientOptions.getIdentity()); } + @Test + public void preserveTestOptionsWhenEnvConfigHasNoMetadataProviders() { + Map environment = newEnvConfigEnvironment(); + environment.remove("TEMPORAL_API_KEY"); + environment.remove("TEMPORAL_GRPC_META_TEST_HEADER"); + WorkflowServiceStubsOptions serviceOptions = + WorkflowServiceStubsOptions.newBuilder().setRpcTimeout(Duration.ofSeconds(17)).build(); + + WorkflowServiceStubsOptions configuredServiceOptions = + ExternalServiceTestConfigurator.configure(serviceOptions, environment); + + assertEquals("envconfig-address:7233", configuredServiceOptions.getTarget()); + assertEquals(Duration.ofSeconds(17), configuredServiceOptions.getRpcTimeout()); + } + @Test public void sdkRuleReappliesEnvConfigConnectionAfterTestCustomization() { Map environment = newEnvConfigEnvironment(); From 2b903d2db2db5c82cacbfef49f0919ee7d51f701 Mon Sep 17 00:00:00 2001 From: Thomas Hardy Date: Tue, 25 Aug 2026 12:06:58 -0400 Subject: [PATCH 5/5] Simplify envconfig test harness integration --- temporal-sdk/build.gradle | 1 + temporal-testing/build.gradle | 4 +- .../docker/RegisterTestNamespace.java | 3 +- .../ExternalServiceTestConfigurator.java | 258 ++++-------------- .../testing/internal/SDKTestWorkflowRule.java | 33 +-- .../ExternalServiceTestConfiguratorTest.java | 238 ++++------------ 6 files changed, 121 insertions(+), 416 deletions(-) diff --git a/temporal-sdk/build.gradle b/temporal-sdk/build.gradle index 594a7660e0..7fe5441252 100644 --- a/temporal-sdk/build.gradle +++ b/temporal-sdk/build.gradle @@ -25,6 +25,7 @@ dependencies { } testImplementation project(':temporal-testing') + // The optional envconfig-backed test harness is loaded only while SDK tests are running. testRuntimeOnly project(':temporal-envconfig') testImplementation "junit:junit:${junitVersion}" testImplementation "org.mockito:mockito-core:${mockitoVersion}" diff --git a/temporal-testing/build.gradle b/temporal-testing/build.gradle index 10a7ddc6a1..3715605b8c 100644 --- a/temporal-testing/build.gradle +++ b/temporal-testing/build.gradle @@ -16,6 +16,7 @@ java { dependencies { api project(':temporal-sdk') api project(':temporal-test-server') + // Envconfig is optional for consumers of temporal-testing. compileOnly project(':temporal-envconfig') implementation 'org.apache.commons:commons-compress:1.28.0' @@ -33,7 +34,8 @@ dependencies { junit5Api 'org.junit.jupiter:junit-jupiter-api' testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter' - testRuntimeOnly project(':temporal-envconfig') + // Tests construct envconfig profiles directly. + testImplementation project(':temporal-envconfig') testRuntimeOnly group: 'ch.qos.logback', name: 'logback-classic', version: "${logbackVersion}" } diff --git a/temporal-testing/src/main/java/io/temporal/internal/docker/RegisterTestNamespace.java b/temporal-testing/src/main/java/io/temporal/internal/docker/RegisterTestNamespace.java index b43a9ca3cf..cc45b8b614 100644 --- a/temporal-testing/src/main/java/io/temporal/internal/docker/RegisterTestNamespace.java +++ b/temporal-testing/src/main/java/io/temporal/internal/docker/RegisterTestNamespace.java @@ -21,7 +21,8 @@ public class RegisterTestNamespace { private static final String serviceAddress = System.getenv("TEMPORAL_SERVICE_ADDRESS"); public static void main(String[] args) throws InterruptedException { - if (!useExternalService || useEnvConfig) { + // Envconfig mode connects to an existing namespace and must not register UnitTest. + if (useEnvConfig || !useExternalService) { return; } diff --git a/temporal-testing/src/main/java/io/temporal/testing/internal/ExternalServiceTestConfigurator.java b/temporal-testing/src/main/java/io/temporal/testing/internal/ExternalServiceTestConfigurator.java index 95c213f219..01ca6b2fb4 100644 --- a/temporal-testing/src/main/java/io/temporal/testing/internal/ExternalServiceTestConfigurator.java +++ b/temporal-testing/src/main/java/io/temporal/testing/internal/ExternalServiceTestConfigurator.java @@ -1,94 +1,53 @@ package io.temporal.testing.internal; -import io.grpc.Metadata; -import io.temporal.client.ActivityClientOptions; -import io.temporal.client.WorkflowClientOptions; import io.temporal.envconfig.ClientConfigProfile; -import io.temporal.envconfig.LoadClientConfigProfileOptions; -import io.temporal.serviceclient.GrpcMetadataProvider; -import io.temporal.serviceclient.WorkflowServiceStubsOptions; +import io.temporal.internal.common.env.EnvironmentVariableUtils; import io.temporal.testing.TestEnvironmentOptions; import io.temporal.testing.TestWorkflowRule; import io.temporal.testing.internal.devserver.SdkJavaTestServerProfile; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; import javax.annotation.Nonnull; public class ExternalServiceTestConfigurator { - static final String TEMPORAL_TEST_ENV_CONFIG_SERVER = "TEMPORAL_TEST_ENV_CONFIG_SERVER"; - private static final String USE_EXTERNAL_SERVICE = "USE_EXTERNAL_SERVICE"; - private static final String TEMPORAL_SERVICE_ADDRESS = "TEMPORAL_SERVICE_ADDRESS"; - private static final String USE_VIRTUAL_THREADS = "USE_VIRTUAL_THREADS"; + private static boolean USE_ENV_CONFIG = + EnvironmentVariableUtils.readBooleanFlag("TEMPORAL_TEST_ENV_CONFIG_SERVER"); + private static boolean USE_EXTERNAL_SERVICE = + EnvironmentVariableUtils.readBooleanFlag("USE_EXTERNAL_SERVICE"); + private static String TEMPORAL_SERVICE_ADDRESS = + EnvironmentVariableUtils.readString("TEMPORAL_SERVICE_ADDRESS"); + private static boolean USE_VIRTUAL_THREADS = + EnvironmentVariableUtils.readBooleanFlag("USE_VIRTUAL_THREADS"); public static boolean isUseExternalService() { - return isUseExternalService(System.getenv()) || SdkJavaTestServerProfile.isActive(); + return USE_ENV_CONFIG || USE_EXTERNAL_SERVICE || SdkJavaTestServerProfile.isActive(); } public static boolean isUseVirtualThreads() { - return readBooleanFlag(System.getenv(), USE_VIRTUAL_THREADS); + return USE_VIRTUAL_THREADS; } public static String getTemporalServiceAddress() { - Map environment = System.getenv(); - if (readBooleanFlag(environment, TEMPORAL_TEST_ENV_CONFIG_SERVER)) { - return getTemporalServiceAddress(environment); + if (USE_ENV_CONFIG) { + return loadEnvConfigProfile().getAddress(); } - String devServerTarget = SdkJavaTestServerProfile.getTarget(); - if (devServerTarget != null) { - return devServerTarget; + if (SdkJavaTestServerProfile.isActive()) { + return SdkJavaTestServerProfile.getTarget(); } - return getTemporalServiceAddress(environment); + return USE_EXTERNAL_SERVICE + ? (TEMPORAL_SERVICE_ADDRESS != null ? TEMPORAL_SERVICE_ADDRESS : "127.0.0.1:7233") + : null; } public static TestWorkflowRule.Builder configure( @Nonnull TestWorkflowRule.Builder testWorkflowRule) { - return configure(testWorkflowRule, System.getenv(), SdkJavaTestServerProfile.getTarget()); - } - - static TestWorkflowRule.Builder configure( - TestWorkflowRule.Builder testWorkflowRule, Map environment) { - return configure(testWorkflowRule, environment, null); - } - - static TestWorkflowRule.Builder configure( - TestWorkflowRule.Builder testWorkflowRule, - Map environment, - String devServerTarget) { - return configure(testWorkflowRule, environment, devServerTarget, true); - } - - static TestWorkflowRule.Builder configureConnection( - TestWorkflowRule.Builder testWorkflowRule, - Map environment, - String devServerTarget) { - return configure(testWorkflowRule, environment, devServerTarget, false); - } - - private static TestWorkflowRule.Builder configure( - TestWorkflowRule.Builder testWorkflowRule, - Map environment, - String devServerTarget, - boolean configureOptions) { - ClientConfigProfile profile = loadEnvConfigProfile(environment); - if (profile != null) { - testWorkflowRule.setUseExternalService(true); - testWorkflowRule.setTarget(profile.getAddress()); - testWorkflowRule.setNamespace(profile.getNamespace()); - if (configureOptions) { - testWorkflowRule.setWorkflowServiceStubsOptions(profile.toWorkflowServiceStubsOptions()); - testWorkflowRule.setWorkflowClientOptions(profile.toWorkflowClientOptions()); - } - } else if (devServerTarget != null) { - testWorkflowRule.setUseExternalService(true); - testWorkflowRule.setTarget(devServerTarget); - } else if (readBooleanFlag(environment, USE_EXTERNAL_SERVICE)) { + if (USE_ENV_CONFIG) { + return configureFromEnvConfig(testWorkflowRule, loadEnvConfigProfile()); + } + if (isUseExternalService()) { testWorkflowRule.setUseExternalService(true); - String serviceAddress = environment.get(TEMPORAL_SERVICE_ADDRESS); - if (serviceAddress != null) { - testWorkflowRule.setTarget(serviceAddress); + String target = getTemporalServiceAddress(); + if (target != null) { + testWorkflowRule.setTarget(target); } } return testWorkflowRule; @@ -96,33 +55,14 @@ private static TestWorkflowRule.Builder configure( public static TestEnvironmentOptions.Builder configure( @Nonnull TestEnvironmentOptions.Builder testEnvironmentOptions) { - return configure(testEnvironmentOptions, System.getenv(), SdkJavaTestServerProfile.getTarget()); - } - - static TestEnvironmentOptions.Builder configure( - TestEnvironmentOptions.Builder testEnvironmentOptions, Map environment) { - return configure(testEnvironmentOptions, environment, null); - } - - static TestEnvironmentOptions.Builder configure( - TestEnvironmentOptions.Builder testEnvironmentOptions, - Map environment, - String devServerTarget) { - ClientConfigProfile profile = loadEnvConfigProfile(environment); - if (profile != null) { - testEnvironmentOptions.setUseExternalService(true); - testEnvironmentOptions.setTarget(profile.getAddress()); - testEnvironmentOptions.setWorkflowServiceStubsOptions( - profile.toWorkflowServiceStubsOptions()); - testEnvironmentOptions.setWorkflowClientOptions(profile.toWorkflowClientOptions()); - } else if (devServerTarget != null) { - testEnvironmentOptions.setUseExternalService(true); - testEnvironmentOptions.setTarget(devServerTarget); - } else if (readBooleanFlag(environment, USE_EXTERNAL_SERVICE)) { + if (USE_ENV_CONFIG) { + return configureFromEnvConfig(testEnvironmentOptions, loadEnvConfigProfile()); + } + if (isUseExternalService()) { testEnvironmentOptions.setUseExternalService(true); - String serviceAddress = environment.get(TEMPORAL_SERVICE_ADDRESS); - if (serviceAddress != null) { - testEnvironmentOptions.setTarget(serviceAddress); + String target = getTemporalServiceAddress(); + if (target != null) { + testEnvironmentOptions.setTarget(target); } } return testEnvironmentOptions; @@ -132,131 +72,45 @@ public static TestEnvironmentOptions.Builder configuredTestEnvironmentOptions() return configure(TestEnvironmentOptions.newBuilder()); } - static WorkflowServiceStubsOptions configure( - WorkflowServiceStubsOptions workflowServiceStubsOptions, Map environment) { - ClientConfigProfile profile = loadEnvConfigProfile(environment); - if (profile == null) { - return workflowServiceStubsOptions; - } - - WorkflowServiceStubsOptions profileOptions = profile.toWorkflowServiceStubsOptions(); - GrpcMetadataProvider metadataProvider = - mergeMetadata( - profileOptions.getHeaders(), - profileOptions.getGrpcMetadataProviders(), - workflowServiceStubsOptions.getHeaders(), - workflowServiceStubsOptions.getGrpcMetadataProviders()); - return WorkflowServiceStubsOptions.newBuilder(workflowServiceStubsOptions) - .setChannel(null) - .setTarget(profileOptions.getTarget()) - .setEnableHttps(profileOptions.getEnableHttps()) - .setSslContext(profileOptions.getSslContext()) - .setChannelInitializer(profileOptions.getChannelInitializer()) - .setHeaders(new Metadata()) - .setGrpcMetadataProviders(Collections.singletonList(metadataProvider)) - .build(); - } - - private static GrpcMetadataProvider mergeMetadata( - Metadata profileHeaders, - Iterable profileProviders, - Metadata testHeaders, - Iterable testProviders) { - List profileProviderList = new ArrayList<>(); - if (profileProviders != null) { - profileProviders.forEach(profileProviderList::add); - } - return () -> { - Metadata metadata = new Metadata(); - if (testHeaders != null) { - metadata.merge(testHeaders); - } - if (testProviders != null) { - testProviders.forEach(provider -> metadata.merge(provider.getMetadata())); - } - Metadata profileMetadata = new Metadata(); - if (profileHeaders != null) { - profileMetadata.merge(profileHeaders); - } - profileProviderList.forEach(provider -> profileMetadata.merge(provider.getMetadata())); - for (String keyName : profileMetadata.keys()) { - if (keyName.endsWith("-bin")) { - metadata.discardAll(Metadata.Key.of(keyName, Metadata.BINARY_BYTE_MARSHALLER)); - } else { - metadata.discardAll(Metadata.Key.of(keyName, Metadata.ASCII_STRING_MARSHALLER)); - } - } - metadata.merge(profileMetadata); - return metadata; - }; - } - - static WorkflowClientOptions configure( - WorkflowClientOptions workflowClientOptions, Map environment) { - ClientConfigProfile profile = loadEnvConfigProfile(environment); - return profile == null - ? workflowClientOptions - : WorkflowClientOptions.newBuilder(workflowClientOptions) - .setNamespace(profile.getNamespace()) - .build(); - } - - static ActivityClientOptions configure( - ActivityClientOptions activityClientOptions, Map environment) { - ClientConfigProfile profile = loadEnvConfigProfile(environment); - return profile == null - ? activityClientOptions - : ActivityClientOptions.newBuilder(activityClientOptions) - .setNamespace(profile.getNamespace()) - .build(); - } - - static boolean isUseExternalService(Map environment) { - return readBooleanFlag(environment, TEMPORAL_TEST_ENV_CONFIG_SERVER) - || readBooleanFlag(environment, USE_EXTERNAL_SERVICE); + static TestWorkflowRule.Builder configureFromEnvConfig( + TestWorkflowRule.Builder testWorkflowRule, ClientConfigProfile profile) { + validateEnvConfigProfile(profile); + testWorkflowRule.setUseExternalService(true); + testWorkflowRule.setTarget(profile.getAddress()); + testWorkflowRule.setNamespace(profile.getNamespace()); + testWorkflowRule.setWorkflowServiceStubsOptions(profile.toWorkflowServiceStubsOptions()); + testWorkflowRule.setWorkflowClientOptions(profile.toWorkflowClientOptions()); + return testWorkflowRule; } - static String getTemporalServiceAddress(Map environment) { - ClientConfigProfile profile = loadEnvConfigProfile(environment); - if (profile != null) { - return profile.getAddress(); - } - return readBooleanFlag(environment, USE_EXTERNAL_SERVICE) - ? (environment.get(TEMPORAL_SERVICE_ADDRESS) != null - ? environment.get(TEMPORAL_SERVICE_ADDRESS) - : "127.0.0.1:7233") - : null; + static TestEnvironmentOptions.Builder configureFromEnvConfig( + TestEnvironmentOptions.Builder testEnvironmentOptions, ClientConfigProfile profile) { + validateEnvConfigProfile(profile); + testEnvironmentOptions.setUseExternalService(true); + testEnvironmentOptions.setTarget(profile.getAddress()); + testEnvironmentOptions.setWorkflowServiceStubsOptions(profile.toWorkflowServiceStubsOptions()); + testEnvironmentOptions.setWorkflowClientOptions(profile.toWorkflowClientOptions()); + return testEnvironmentOptions; } - private static ClientConfigProfile loadEnvConfigProfile(Map environment) { - if (!readBooleanFlag(environment, TEMPORAL_TEST_ENV_CONFIG_SERVER)) { - return null; - } - + private static ClientConfigProfile loadEnvConfigProfile() { ClientConfigProfile profile; try { - profile = - ClientConfigProfile.load( - LoadClientConfigProfileOptions.newBuilder().setEnvOverrides(environment).build()); + profile = ClientConfigProfile.load(); } catch (IOException e) { throw new IllegalStateException( "Unable to load client configuration for the Temporal test harness.", e); } + validateEnvConfigProfile(profile); + return profile; + } + + private static void validateEnvConfigProfile(ClientConfigProfile profile) { if (profile.getAddress() == null || profile.getAddress().isEmpty()) { throw new IllegalStateException("Envconfig test harness requires a Temporal server address."); } if (profile.getNamespace() == null || profile.getNamespace().isEmpty()) { throw new IllegalStateException("Envconfig test harness requires a Temporal namespace."); } - return profile; - } - - private static boolean readBooleanFlag(Map environment, String variableName) { - String value = environment.get(variableName); - if (value == null) { - return false; - } - value = value.trim(); - return !Boolean.FALSE.toString().equalsIgnoreCase(value) && !"0".equals(value); } } diff --git a/temporal-testing/src/main/java/io/temporal/testing/internal/SDKTestWorkflowRule.java b/temporal-testing/src/main/java/io/temporal/testing/internal/SDKTestWorkflowRule.java index c6109c5ec5..9c81461212 100644 --- a/temporal-testing/src/main/java/io/temporal/testing/internal/SDKTestWorkflowRule.java +++ b/temporal-testing/src/main/java/io/temporal/testing/internal/SDKTestWorkflowRule.java @@ -31,7 +31,6 @@ import io.temporal.serviceclient.WorkflowServiceStubsOptions; import io.temporal.testing.TestWorkflowEnvironment; import io.temporal.testing.TestWorkflowRule; -import io.temporal.testing.internal.devserver.SdkJavaTestServerProfile; import io.temporal.worker.*; import io.temporal.workflow.Functions; import java.io.File; @@ -41,7 +40,6 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.UUID; import java.util.concurrent.*; import javax.annotation.Nonnull; @@ -93,9 +91,7 @@ private SDKTestWorkflowRule(SDKTestWorkflowRule.Builder builder) { : null; testWorkflowRule = - ExternalServiceTestConfigurator.configureConnection( - builder.testWorkflowRuleBuilder, builder.environment, builder.devServerTarget) - .build(); + ExternalServiceTestConfigurator.configure(builder.testWorkflowRuleBuilder).build(); } public static Builder newBuilder() { @@ -108,43 +104,24 @@ public static class Builder { private boolean workerFactoryOptionsAreSet = false; private boolean workerOptionsAreSet = false; private final TestWorkflowRule.Builder testWorkflowRuleBuilder; - private final Map environment; - @Nullable private final String devServerTarget; public Builder() { - this(System.getenv(), SdkJavaTestServerProfile.getTarget()); - } - - Builder(Map environment, @Nullable String devServerTarget) { - this.environment = environment; - this.devServerTarget = devServerTarget; - testWorkflowRuleBuilder = - ExternalServiceTestConfigurator.configure( - TestWorkflowRule.newBuilder(), environment, devServerTarget); + testWorkflowRuleBuilder = TestWorkflowRule.newBuilder(); } public Builder setWorkflowServiceStubsOptions( WorkflowServiceStubsOptions workflowServiceStubsOptions) { - if (workflowServiceStubsOptions != null) { - testWorkflowRuleBuilder.setWorkflowServiceStubsOptions( - ExternalServiceTestConfigurator.configure(workflowServiceStubsOptions, environment)); - } + testWorkflowRuleBuilder.setWorkflowServiceStubsOptions(workflowServiceStubsOptions); return this; } public Builder setWorkflowClientOptions(WorkflowClientOptions workflowClientOptions) { - if (workflowClientOptions != null) { - testWorkflowRuleBuilder.setWorkflowClientOptions( - ExternalServiceTestConfigurator.configure(workflowClientOptions, environment)); - } + testWorkflowRuleBuilder.setWorkflowClientOptions(workflowClientOptions); return this; } public Builder setActivityClientOptions(ActivityClientOptions activityClientOptions) { - if (activityClientOptions != null) { - testWorkflowRuleBuilder.setActivityClientOptions( - ExternalServiceTestConfigurator.configure(activityClientOptions, environment)); - } + testWorkflowRuleBuilder.setActivityClientOptions(activityClientOptions); return this; } diff --git a/temporal-testing/src/test/java/io/temporal/testing/internal/ExternalServiceTestConfiguratorTest.java b/temporal-testing/src/test/java/io/temporal/testing/internal/ExternalServiceTestConfiguratorTest.java index cd7a72114e..6dd808a18d 100644 --- a/temporal-testing/src/test/java/io/temporal/testing/internal/ExternalServiceTestConfiguratorTest.java +++ b/temporal-testing/src/test/java/io/temporal/testing/internal/ExternalServiceTestConfiguratorTest.java @@ -1,227 +1,97 @@ package io.temporal.testing.internal; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import io.grpc.Metadata; -import io.temporal.client.WorkflowClientOptions; +import io.temporal.envconfig.ClientConfigProfile; import io.temporal.serviceclient.WorkflowServiceStubsOptions; import io.temporal.testing.TestEnvironmentOptions; import io.temporal.testing.TestWorkflowRule; -import java.io.File; -import java.time.Duration; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; import org.junit.jupiter.api.Test; public class ExternalServiceTestConfiguratorTest { @Test public void configureTestWorkflowRuleFromEnvConfig() { - Map environment = newEnvConfigEnvironment(); - environment.put("USE_EXTERNAL_SERVICE", "true"); - environment.put("TEMPORAL_SERVICE_ADDRESS", "legacy-address:7233"); - TestWorkflowRule rule = - ExternalServiceTestConfigurator.configure(TestWorkflowRule.newBuilder(), environment) + ExternalServiceTestConfigurator.configureFromEnvConfig( + TestWorkflowRule.newBuilder(), newProfile()) .build(); try { assertEquals( "envconfig-address:7233", rule.getWorkflowServiceStubs().getOptions().getTarget()); assertEquals("envconfig-namespace", rule.getWorkflowClient().getOptions().getNamespace()); - - WorkflowServiceStubsOptions stubsOptions = rule.getWorkflowServiceStubs().getOptions(); - assertTrue(stubsOptions.getEnableHttps()); - Metadata metadata = new Metadata(); - stubsOptions - .getGrpcMetadataProviders() - .forEach(provider -> metadata.merge(provider.getMetadata())); - assertEquals( - "metadata-value", - metadata.get(Metadata.Key.of("test-header", Metadata.ASCII_STRING_MARSHALLER))); - assertEquals( - "Bearer api-key", - metadata.get(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER))); } finally { rule.getTestEnvironment().close(); } } @Test - public void preserveLegacyAndLocalModes() { - Map environment = new HashMap<>(); - TestEnvironmentOptions localOptions = - ExternalServiceTestConfigurator.configure(TestEnvironmentOptions.newBuilder(), environment) - .build(); - assertFalse(localOptions.isUseExternalService()); - - environment.put("USE_EXTERNAL_SERVICE", "true"); - environment.put("TEMPORAL_SERVICE_ADDRESS", "legacy-address:7233"); - TestEnvironmentOptions externalOptions = - ExternalServiceTestConfigurator.configure(TestEnvironmentOptions.newBuilder(), environment) - .build(); - assertTrue(externalOptions.isUseExternalService()); - assertEquals("legacy-address:7233", externalOptions.getTarget()); - } - - @Test - public void preserveDevServerProfilePrecedence() { - Map environment = new HashMap<>(); - environment.put("USE_EXTERNAL_SERVICE", "true"); - environment.put("TEMPORAL_SERVICE_ADDRESS", "legacy-address:7233"); - - TestEnvironmentOptions devServerOptions = - ExternalServiceTestConfigurator.configure( - TestEnvironmentOptions.newBuilder(), environment, "dev-server-address:7233") - .build(); - assertTrue(devServerOptions.isUseExternalService()); - assertEquals("dev-server-address:7233", devServerOptions.getTarget()); - - TestEnvironmentOptions envConfigOptions = - ExternalServiceTestConfigurator.configure( - TestEnvironmentOptions.newBuilder(), - newEnvConfigEnvironment(), - "dev-server-address:7233") - .build(); - assertEquals("envconfig-address:7233", envConfigOptions.getTarget()); - assertEquals("envconfig-namespace", envConfigOptions.getWorkflowClientOptions().getNamespace()); - } - - @Test - public void preserveTestOptionsWhenApplyingEnvConfigConnection() { - Map environment = newEnvConfigEnvironment(); - Metadata.Key customHeader = - Metadata.Key.of("custom-option-header", Metadata.ASCII_STRING_MARSHALLER); - Metadata.Key authorizationHeader = - Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER); - Metadata.Key profileHeader = - Metadata.Key.of("test-header", Metadata.ASCII_STRING_MARSHALLER); - Metadata.Key fixedHeader = - Metadata.Key.of("custom-fixed-header", Metadata.ASCII_STRING_MARSHALLER); - Metadata fixedHeaders = new Metadata(); - fixedHeaders.put(fixedHeader, "custom-fixed-value"); - fixedHeaders.put(authorizationHeader, "Bearer stale-fixed-api-key"); - fixedHeaders.put(profileHeader, "stale-fixed-metadata-value"); - WorkflowServiceStubsOptions serviceOptions = - WorkflowServiceStubsOptions.newBuilder() - .setRpcTimeout(Duration.ofSeconds(17)) - .setHeaders(fixedHeaders) - .addGrpcMetadataProvider( - () -> { - Metadata metadata = new Metadata(); - metadata.put(customHeader, "custom-option-value"); - metadata.put(authorizationHeader, "Bearer stale-api-key"); - metadata.put(profileHeader, "stale-metadata-value"); - return metadata; - }) - .build(); - - WorkflowServiceStubsOptions configuredServiceOptions = - ExternalServiceTestConfigurator.configure(serviceOptions, environment); - assertEquals("envconfig-address:7233", configuredServiceOptions.getTarget()); - assertEquals(Duration.ofSeconds(17), configuredServiceOptions.getRpcTimeout()); - assertTrue(configuredServiceOptions.getEnableHttps()); - Metadata metadata = new Metadata(); - configuredServiceOptions - .getGrpcMetadataProviders() - .forEach(provider -> metadata.merge(provider.getMetadata())); - assertEquals("custom-option-value", metadata.get(customHeader)); - assertEquals("custom-fixed-value", metadata.get(fixedHeader)); - assertEquals("metadata-value", metadata.get(profileHeader)); - assertEquals("Bearer api-key", metadata.get(authorizationHeader)); - int authorizationValues = 0; - for (String ignored : metadata.getAll(authorizationHeader)) { - authorizationValues++; - } - assertEquals(1, authorizationValues); - - WorkflowClientOptions clientOptions = - WorkflowClientOptions.newBuilder() - .setNamespace("test-option-namespace") - .setIdentity("test-option-identity") + public void configureTestEnvironmentFromEnvConfig() { + TestEnvironmentOptions options = + ExternalServiceTestConfigurator.configureFromEnvConfig( + TestEnvironmentOptions.newBuilder(), newProfile()) .build(); - WorkflowClientOptions configuredClientOptions = - ExternalServiceTestConfigurator.configure(clientOptions, environment); - assertEquals("envconfig-namespace", configuredClientOptions.getNamespace()); - assertEquals("test-option-identity", configuredClientOptions.getIdentity()); - } - - @Test - public void preserveTestOptionsWhenEnvConfigHasNoMetadataProviders() { - Map environment = newEnvConfigEnvironment(); - environment.remove("TEMPORAL_API_KEY"); - environment.remove("TEMPORAL_GRPC_META_TEST_HEADER"); - WorkflowServiceStubsOptions serviceOptions = - WorkflowServiceStubsOptions.newBuilder().setRpcTimeout(Duration.ofSeconds(17)).build(); - - WorkflowServiceStubsOptions configuredServiceOptions = - ExternalServiceTestConfigurator.configure(serviceOptions, environment); - - assertEquals("envconfig-address:7233", configuredServiceOptions.getTarget()); - assertEquals(Duration.ofSeconds(17), configuredServiceOptions.getRpcTimeout()); - } - @Test - public void sdkRuleReappliesEnvConfigConnectionAfterTestCustomization() { - Map environment = newEnvConfigEnvironment(); - SDKTestWorkflowRule rule = - new SDKTestWorkflowRule.Builder(environment, null) - .setUseExternalService(false) - .setTarget("test-option-address:7233") - .setNamespace("test-option-namespace") - .setWorkflowServiceStubsOptions( - WorkflowServiceStubsOptions.newBuilder() - .setRpcTimeout(Duration.ofSeconds(17)) - .build()) - .setWorkflowClientOptions( - WorkflowClientOptions.newBuilder() - .setNamespace("test-option-namespace") - .setIdentity("test-option-identity") - .build()) - .build(); - try { - assertEquals( - "envconfig-address:7233", rule.getWorkflowServiceStubs().getOptions().getTarget()); - assertEquals( - Duration.ofSeconds(17), rule.getWorkflowServiceStubs().getOptions().getRpcTimeout()); - assertEquals("envconfig-namespace", rule.getWorkflowClient().getOptions().getNamespace()); - assertEquals("test-option-identity", rule.getWorkflowClient().getOptions().getIdentity()); - } finally { - rule.getTestEnvironment().close(); - } + assertTrue(options.isUseExternalService()); + assertEquals("envconfig-address:7233", options.getTarget()); + assertEquals("envconfig-address:7233", options.getWorkflowServiceStubsOptions().getTarget()); + assertEquals("envconfig-namespace", options.getWorkflowClientOptions().getNamespace()); + assertTrue(options.getWorkflowServiceStubsOptions().getEnableHttps()); + + Metadata metadata = metadata(options.getWorkflowServiceStubsOptions()); + assertEquals( + "metadata-value", + metadata.get(Metadata.Key.of("test-header", Metadata.ASCII_STRING_MARSHALLER))); + assertEquals( + "Bearer api-key", + metadata.get(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER))); } @Test public void requireAddressAndNamespaceInEnvConfigMode() { - Map environment = new HashMap<>(); - environment.put(ExternalServiceTestConfigurator.TEMPORAL_TEST_ENV_CONFIG_SERVER, "true"); - environment.put("TEMPORAL_CONFIG_FILE", nonExistentConfigFile()); - environment.put("TEMPORAL_ADDRESS", "envconfig-address:7233"); - - IllegalStateException exception = + ClientConfigProfile missingAddress = + ClientConfigProfile.newBuilder().setNamespace("envconfig-namespace").build(); + IllegalStateException missingAddressException = assertThrows( IllegalStateException.class, () -> - ExternalServiceTestConfigurator.configure( - TestEnvironmentOptions.newBuilder(), environment)); - assertEquals("Envconfig test harness requires a Temporal namespace.", exception.getMessage()); + ExternalServiceTestConfigurator.configureFromEnvConfig( + TestEnvironmentOptions.newBuilder(), missingAddress)); + assertEquals( + "Envconfig test harness requires a Temporal server address.", + missingAddressException.getMessage()); + + ClientConfigProfile missingNamespace = + ClientConfigProfile.newBuilder().setAddress("envconfig-address:7233").build(); + IllegalStateException missingNamespaceException = + assertThrows( + IllegalStateException.class, + () -> + ExternalServiceTestConfigurator.configureFromEnvConfig( + TestEnvironmentOptions.newBuilder(), missingNamespace)); + assertEquals( + "Envconfig test harness requires a Temporal namespace.", + missingNamespaceException.getMessage()); } - private static Map newEnvConfigEnvironment() { - Map environment = new HashMap<>(); - environment.put(ExternalServiceTestConfigurator.TEMPORAL_TEST_ENV_CONFIG_SERVER, "true"); - environment.put("TEMPORAL_CONFIG_FILE", nonExistentConfigFile()); - environment.put("TEMPORAL_ADDRESS", "envconfig-address:7233"); - environment.put("TEMPORAL_NAMESPACE", "envconfig-namespace"); - environment.put("TEMPORAL_API_KEY", "api-key"); - environment.put("TEMPORAL_GRPC_META_TEST_HEADER", "metadata-value"); - return environment; + private static ClientConfigProfile newProfile() { + Metadata metadata = new Metadata(); + metadata.put( + Metadata.Key.of("test-header", Metadata.ASCII_STRING_MARSHALLER), "metadata-value"); + return ClientConfigProfile.newBuilder() + .setAddress("envconfig-address:7233") + .setNamespace("envconfig-namespace") + .setApiKey("api-key") + .setMetadata(metadata) + .build(); } - private static String nonExistentConfigFile() { - return new File("build/non-existent-envconfig-" + UUID.randomUUID()).getAbsolutePath(); + private static Metadata metadata(WorkflowServiceStubsOptions options) { + Metadata metadata = new Metadata(); + options.getGrpcMetadataProviders().forEach(provider -> metadata.merge(provider.getMetadata())); + return metadata; } }