diff --git a/README.md b/README.md
index ca098ba9..cb22c4ce 100644
--- a/README.md
+++ b/README.md
@@ -48,6 +48,7 @@ Remember that each namespace requires its own authentication token type as descr
* [Server Selection](#server-selection)
* [Custom HTTP Client](#custom-http-client)
* [Debugging](#debugging)
+ * [Experimental Features and Deprecation Testing](#experimental-features-and-deprecation-testing)
* [Development](#development)
* [Maturity](#maturity)
* [Contributions](#contributions)
@@ -1598,6 +1599,55 @@ __NOTE__: This is a convenience method that calls `HTTPClient.enableDebugLogging
Another option is to set the System property `-Djdk.httpclient.HttpClient.log=all`. However, this second option does not log bodies.
+## Experimental Features and Deprecation Testing
+
+The SDK provides options to test upcoming API changes before they become the default behavior. This is useful for:
+
+- **Testing experimental features** before they are generally available
+- **Preparing for deprecations** by excluding deprecated endpoints ahead of their removal
+
+### Configuration Options
+
+You can configure these options either via environment variables or SDK constructor options:
+
+#### Using Environment Variables
+
+```bash
+# Set environment variables before running your application
+export X_GLEAN_EXCLUDE_DEPRECATED_AFTER="2026-10-15"
+export X_GLEAN_INCLUDE_EXPERIMENTAL="true"
+```
+
+```java
+// Environment variables are automatically read by the SDK
+Glean glean = Glean.builder()
+ .apiToken(System.getenv("GLEAN_API_TOKEN"))
+ .instance("instance-name")
+ .build();
+```
+
+#### Using SDK Constructor Options
+
+```java
+Glean glean = Glean.builder()
+ .apiToken(System.getenv("GLEAN_API_TOKEN"))
+ .instance("instance-name")
+ .excludeDeprecatedAfter("2026-10-15")
+ .includeExperimental(true)
+ .build();
+```
+
+### Option Reference
+
+| Option | Environment Variable | Type | Description |
+| ------ | -------------------- | ---- | ----------- |
+| `excludeDeprecatedAfter` | `X_GLEAN_EXCLUDE_DEPRECATED_AFTER` | `String` (date) | Exclude API endpoints that will be deprecated after this date (format: `YYYY-MM-DD`). Use this to test your integration against upcoming deprecations. |
+| `includeExperimental` | `X_GLEAN_INCLUDE_EXPERIMENTAL` | `boolean` | When `true`, enables experimental API features that are not yet generally available. Use this to preview and test new functionality. |
+
+> **Note:** Environment variables take precedence over SDK constructor options when both are set.
+
+> **Warning:** Experimental features may change or be removed without notice. Do not rely on experimental features in production environments.
+
# Development
diff --git a/build-extras.gradle b/build-extras.gradle
index e104d4f6..3a9e7ba5 100644
--- a/build-extras.gradle
+++ b/build-extras.gradle
@@ -1,4 +1,13 @@
-// This file
+// This file
// * is referred to in an `apply from` command in `build.gradle`
// * can be used to customise `build.gradle`
// * is generated once and not overwritten in SDK generation updates
+
+dependencies {
+ testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
+ testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
+}
+
+test {
+ useJUnitPlatform()
+}
diff --git a/src/main/java/com/glean/api_client/glean_api_client/Glean.java b/src/main/java/com/glean/api_client/glean_api_client/Glean.java
index 24f6ef00..19103561 100644
--- a/src/main/java/com/glean/api_client/glean_api_client/Glean.java
+++ b/src/main/java/com/glean/api_client/glean_api_client/Glean.java
@@ -204,6 +204,35 @@ public Builder instance(String instance) {
return this;
}
+
+ /**
+ * Exclude API endpoints that will be deprecated after this date.
+ * Use this to test your integration against upcoming deprecations.
+ *
+ *
More information: Deprecations Overview
+ *
+ * @param excludeDeprecatedAfter date string in YYYY-MM-DD format (e.g., '2026-10-15')
+ * @return The builder instance.
+ */
+ public Builder excludeDeprecatedAfter(String excludeDeprecatedAfter) {
+ this.sdkConfiguration.setExcludeDeprecatedAfter(Optional.of(excludeDeprecatedAfter));
+ return this;
+ }
+
+ /**
+ * Enable experimental API features that are not yet generally available.
+ * Use this to preview and test new functionality.
+ *
+ *
Warning: Experimental features may change or be removed without notice.
+ * Do not rely on experimental features in production environments.
+ *
+ * @param includeExperimental whether to include experimental features
+ * @return The builder instance.
+ */
+ public Builder includeExperimental(boolean includeExperimental) {
+ this.sdkConfiguration.setIncludeExperimental(Optional.of(includeExperimental));
+ return this;
+ }
// Visible for testing, may be accessed via reflection in tests
Builder _hooks(com.glean.api_client.glean_api_client.utils.Hooks hooks) {
sdkConfiguration.setHooks(hooks);
diff --git a/src/main/java/com/glean/api_client/glean_api_client/SDKConfiguration.java b/src/main/java/com/glean/api_client/glean_api_client/SDKConfiguration.java
index ddcd0f25..24dfee1d 100644
--- a/src/main/java/com/glean/api_client/glean_api_client/SDKConfiguration.java
+++ b/src/main/java/com/glean/api_client/glean_api_client/SDKConfiguration.java
@@ -128,7 +128,7 @@ public Map getServerVariableDefaults() {
return serverVariables.get(this.serverIdx);
}
private Optional retryConfig = Optional.empty();
-
+
public Optional retryConfig() {
return retryConfig;
}
@@ -137,6 +137,50 @@ public void setRetryConfig(Optional retryConfig) {
Utils.checkNotNull(retryConfig, "retryConfig");
this.retryConfig = retryConfig;
}
+
+ private Optional excludeDeprecatedAfter = Optional.empty();
+
+ /**
+ * Gets the date after which deprecated API endpoints should be excluded.
+ *
+ * @return Optional containing the date string (YYYY-MM-DD format) if set
+ */
+ public Optional excludeDeprecatedAfter() {
+ return excludeDeprecatedAfter;
+ }
+
+ /**
+ * Sets the date after which deprecated API endpoints should be excluded.
+ * Use this to test your integration against upcoming deprecations.
+ *
+ * @param excludeDeprecatedAfter date string in YYYY-MM-DD format
+ */
+ public void setExcludeDeprecatedAfter(Optional excludeDeprecatedAfter) {
+ Utils.checkNotNull(excludeDeprecatedAfter, "excludeDeprecatedAfter");
+ this.excludeDeprecatedAfter = excludeDeprecatedAfter;
+ }
+
+ private Optional includeExperimental = Optional.empty();
+
+ /**
+ * Gets whether experimental API features should be enabled.
+ *
+ * @return Optional containing the boolean value if set
+ */
+ public Optional includeExperimental() {
+ return includeExperimental;
+ }
+
+ /**
+ * Sets whether experimental API features should be enabled.
+ * When true, enables experimental API features that are not yet generally available.
+ *
+ * @param includeExperimental whether to include experimental features
+ */
+ public void setIncludeExperimental(Optional includeExperimental) {
+ Utils.checkNotNull(includeExperimental, "includeExperimental");
+ this.includeExperimental = includeExperimental;
+ }
private ScheduledExecutorService retryScheduler = Executors.newSingleThreadScheduledExecutor();
public ScheduledExecutorService retryScheduler() {
diff --git a/src/main/java/com/glean/api_client/glean_api_client/hooks/SDKHooks.java b/src/main/java/com/glean/api_client/glean_api_client/hooks/SDKHooks.java
index c6e578f1..5c2cb97e 100644
--- a/src/main/java/com/glean/api_client/glean_api_client/hooks/SDKHooks.java
+++ b/src/main/java/com/glean/api_client/glean_api_client/hooks/SDKHooks.java
@@ -15,6 +15,9 @@ private SDKHooks() {
public static void initialize(com.glean.api_client.glean_api_client.utils.Hooks hooks) {
hooks.registerAfterError(AgentFileUploadErrorHook.createSyncHook());
+ // Register the X-Glean header hook for experimental features and deprecation testing
+ hooks.registerBeforeRequest(XGleanHeadersHook.createSyncHook());
+
// for more information see
// https://www.speakeasy.com/docs/additional-features/sdk-hooks
}
@@ -22,6 +25,9 @@ public static void initialize(com.glean.api_client.glean_api_client.utils.Hooks
public static void initialize(com.glean.api_client.glean_api_client.utils.AsyncHooks asyncHooks) {
asyncHooks.registerAfterError(AgentFileUploadErrorHook.createAsyncHook());
+ // Register the X-Glean header hook for experimental features and deprecation testing
+ asyncHooks.registerBeforeRequest(XGleanHeadersHook.createAsyncHook());
+
// NOTE: If you have existing synchronous hooks, you can adapt them using HookAdapters:
// asyncHooks.registerAfterError(com.glean.api_client.glean_api_client.utils.HookAdapters.adapt(mySyncHook));
diff --git a/src/main/java/com/glean/api_client/glean_api_client/hooks/XGleanHeadersHook.java b/src/main/java/com/glean/api_client/glean_api_client/hooks/XGleanHeadersHook.java
new file mode 100644
index 00000000..5147f1b5
--- /dev/null
+++ b/src/main/java/com/glean/api_client/glean_api_client/hooks/XGleanHeadersHook.java
@@ -0,0 +1,139 @@
+package com.glean.api_client.glean_api_client.hooks;
+
+import com.glean.api_client.glean_api_client.SDKConfiguration;
+import com.glean.api_client.glean_api_client.utils.AsyncHook;
+import com.glean.api_client.glean_api_client.utils.Helpers;
+import com.glean.api_client.glean_api_client.utils.Hook;
+
+import java.net.http.HttpRequest;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
+
+/**
+ * Hook that adds X-Glean headers for experimental features and deprecation testing.
+ *
+ * This hook sets the following headers based on SDK options or environment variables:
+ *
+ * - {@code X-Glean-Exclude-Deprecated-After} - Exclude API endpoints deprecated after this date
+ * - {@code X-Glean-Experimental} - Enable experimental API features
+ *
+ *
+ * Environment variables take precedence over SDK constructor options:
+ *
+ * - {@code X_GLEAN_EXCLUDE_DEPRECATED_AFTER} - Date in YYYY-MM-DD format
+ * - {@code X_GLEAN_INCLUDE_EXPERIMENTAL} - "true" to enable experimental features
+ *
+ */
+public final class XGleanHeadersHook {
+
+ static final String ENV_EXCLUDE_DEPRECATED_AFTER = "X_GLEAN_EXCLUDE_DEPRECATED_AFTER";
+ static final String ENV_INCLUDE_EXPERIMENTAL = "X_GLEAN_INCLUDE_EXPERIMENTAL";
+
+ static final String HEADER_EXCLUDE_DEPRECATED_AFTER = "X-Glean-Exclude-Deprecated-After";
+ static final String HEADER_EXPERIMENTAL = "X-Glean-Experimental";
+
+ private XGleanHeadersHook() {
+ // prevent instantiation
+ }
+
+ /**
+ * Creates a synchronous BeforeRequest hook for adding X-Glean headers.
+ *
+ * @return the sync hook
+ */
+ public static Hook.BeforeRequest createSyncHook() {
+ return createSyncHook(System::getenv);
+ }
+
+ /**
+ * Creates a synchronous BeforeRequest hook for adding X-Glean headers.
+ * This variant accepts a custom environment variable provider for testing.
+ *
+ * @param envProvider function to get environment variables
+ * @return the sync hook
+ */
+ static Hook.BeforeRequest createSyncHook(Function envProvider) {
+ return (context, request) -> {
+ HttpRequest.Builder builder = Helpers.copy(request);
+ addHeaders(builder, context.sdkConfiguration(), envProvider);
+ return builder.build();
+ };
+ }
+
+ /**
+ * Creates an asynchronous BeforeRequest hook for adding X-Glean headers.
+ *
+ * @return the async hook
+ */
+ public static AsyncHook.BeforeRequest createAsyncHook() {
+ return createAsyncHook(System::getenv);
+ }
+
+ /**
+ * Creates an asynchronous BeforeRequest hook for adding X-Glean headers.
+ * This variant accepts a custom environment variable provider for testing.
+ *
+ * @param envProvider function to get environment variables
+ * @return the async hook
+ */
+ static AsyncHook.BeforeRequest createAsyncHook(Function envProvider) {
+ return (context, request) -> {
+ HttpRequest.Builder builder = Helpers.copy(request);
+ addHeaders(builder, context.sdkConfiguration(), envProvider);
+ return CompletableFuture.completedFuture(builder.build());
+ };
+ }
+
+ private static void addHeaders(HttpRequest.Builder builder, SDKConfiguration config,
+ Function envProvider) {
+ // Get deprecated after value - environment variable takes precedence
+ Optional deprecatedAfterValue = getFirstNonEmpty(
+ getEnv(ENV_EXCLUDE_DEPRECATED_AFTER, envProvider),
+ config.excludeDeprecatedAfter()
+ );
+
+ deprecatedAfterValue.ifPresent(value ->
+ builder.header(HEADER_EXCLUDE_DEPRECATED_AFTER, value)
+ );
+
+ // Get experimental value - environment variable takes precedence
+ Optional experimentalValue = getFirstNonEmpty(
+ getEnvAsBoolean(ENV_INCLUDE_EXPERIMENTAL, envProvider),
+ config.includeExperimental().filter(b -> b).map(b -> "true")
+ );
+
+ experimentalValue.ifPresent(value ->
+ builder.header(HEADER_EXPERIMENTAL, value)
+ );
+ }
+
+ /**
+ * Returns the first non-empty Optional from the provided arguments.
+ */
+ @SafeVarargs
+ private static Optional getFirstNonEmpty(Optional... optionals) {
+ for (Optional opt : optionals) {
+ if (opt.isPresent()) {
+ return opt;
+ }
+ }
+ return Optional.empty();
+ }
+
+ private static Optional getEnv(String name, Function envProvider) {
+ String value = envProvider.apply(name);
+ if (value != null && !value.isEmpty()) {
+ return Optional.of(value);
+ }
+ return Optional.empty();
+ }
+
+ private static Optional getEnvAsBoolean(String name, Function envProvider) {
+ String value = envProvider.apply(name);
+ if ("true".equalsIgnoreCase(value)) {
+ return Optional.of("true");
+ }
+ return Optional.empty();
+ }
+}
diff --git a/src/test/java/com/glean/api_client/glean_api_client/hooks/XGleanHeadersHookTest.java b/src/test/java/com/glean/api_client/glean_api_client/hooks/XGleanHeadersHookTest.java
new file mode 100644
index 00000000..e3182b99
--- /dev/null
+++ b/src/test/java/com/glean/api_client/glean_api_client/hooks/XGleanHeadersHookTest.java
@@ -0,0 +1,252 @@
+package com.glean.api_client.glean_api_client.hooks;
+
+import com.glean.api_client.glean_api_client.SDKConfiguration;
+import com.glean.api_client.glean_api_client.SecuritySource;
+import com.glean.api_client.glean_api_client.utils.Hook;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import java.net.URI;
+import java.net.http.HttpRequest;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class XGleanHeadersHookTest {
+
+ private static final String HEADER_EXCLUDE_DEPRECATED_AFTER = XGleanHeadersHook.HEADER_EXCLUDE_DEPRECATED_AFTER;
+ private static final String HEADER_EXPERIMENTAL = XGleanHeadersHook.HEADER_EXPERIMENTAL;
+
+ private HttpRequest createMockRequest() {
+ return HttpRequest.newBuilder()
+ .uri(URI.create("https://example.com/api/test"))
+ .GET()
+ .build();
+ }
+
+ private Hook.BeforeRequestContext createMockContext(SDKConfiguration config) {
+ return new Hook.BeforeRequestContextImpl(
+ config,
+ "https://example.com",
+ "test-operation",
+ Optional.empty(),
+ Optional.of(SecuritySource.of(null))
+ );
+ }
+
+ private SDKConfiguration createConfig(String excludeDeprecatedAfter, Boolean includeExperimental) {
+ SDKConfiguration config = new SDKConfiguration();
+ if (excludeDeprecatedAfter != null) {
+ config.setExcludeDeprecatedAfter(Optional.of(excludeDeprecatedAfter));
+ }
+ if (includeExperimental != null) {
+ config.setIncludeExperimental(Optional.of(includeExperimental));
+ }
+ return config;
+ }
+
+ /**
+ * Creates a mock environment provider with the given key-value pairs.
+ */
+ private Function createEnvProvider(Map env) {
+ return env::get;
+ }
+
+ /**
+ * Creates an empty environment provider (no environment variables set).
+ */
+ private Function emptyEnvProvider() {
+ return name -> null;
+ }
+
+ @Nested
+ class WhenNeitherOptionsNorEnvironmentVariablesAreSet {
+
+ @Test
+ void shouldNotSetAnyXGleanHeaders() throws Exception {
+ Hook.BeforeRequest hook = XGleanHeadersHook.createSyncHook(emptyEnvProvider());
+ HttpRequest request = createMockRequest();
+ SDKConfiguration config = createConfig(null, null);
+ Hook.BeforeRequestContext context = createMockContext(config);
+
+ HttpRequest result = hook.beforeRequest(context, request);
+
+ assertFalse(result.headers().firstValue(HEADER_EXCLUDE_DEPRECATED_AFTER).isPresent());
+ assertFalse(result.headers().firstValue(HEADER_EXPERIMENTAL).isPresent());
+ }
+ }
+
+ @Nested
+ class WhenUsingSDKConstructorOptions {
+
+ @Test
+ void shouldSetExcludeDeprecatedAfterHeaderFromOption() throws Exception {
+ Hook.BeforeRequest hook = XGleanHeadersHook.createSyncHook(emptyEnvProvider());
+ HttpRequest request = createMockRequest();
+ SDKConfiguration config = createConfig("2026-10-15", null);
+ Hook.BeforeRequestContext context = createMockContext(config);
+
+ HttpRequest result = hook.beforeRequest(context, request);
+
+ assertEquals("2026-10-15", result.headers().firstValue(HEADER_EXCLUDE_DEPRECATED_AFTER).orElse(null));
+ }
+
+ @Test
+ void shouldSetExperimentalHeaderWhenIncludeExperimentalIsTrue() throws Exception {
+ Hook.BeforeRequest hook = XGleanHeadersHook.createSyncHook(emptyEnvProvider());
+ HttpRequest request = createMockRequest();
+ SDKConfiguration config = createConfig(null, true);
+ Hook.BeforeRequestContext context = createMockContext(config);
+
+ HttpRequest result = hook.beforeRequest(context, request);
+
+ assertEquals("true", result.headers().firstValue(HEADER_EXPERIMENTAL).orElse(null));
+ }
+
+ @Test
+ void shouldNotSetExperimentalHeaderWhenIncludeExperimentalIsFalse() throws Exception {
+ Hook.BeforeRequest hook = XGleanHeadersHook.createSyncHook(emptyEnvProvider());
+ HttpRequest request = createMockRequest();
+ SDKConfiguration config = createConfig(null, false);
+ Hook.BeforeRequestContext context = createMockContext(config);
+
+ HttpRequest result = hook.beforeRequest(context, request);
+
+ assertFalse(result.headers().firstValue(HEADER_EXPERIMENTAL).isPresent());
+ }
+
+ @Test
+ void shouldSetBothHeadersWhenBothOptionsAreProvided() throws Exception {
+ Hook.BeforeRequest hook = XGleanHeadersHook.createSyncHook(emptyEnvProvider());
+ HttpRequest request = createMockRequest();
+ SDKConfiguration config = createConfig("2026-10-15", true);
+ Hook.BeforeRequestContext context = createMockContext(config);
+
+ HttpRequest result = hook.beforeRequest(context, request);
+
+ assertEquals("2026-10-15", result.headers().firstValue(HEADER_EXCLUDE_DEPRECATED_AFTER).orElse(null));
+ assertEquals("true", result.headers().firstValue(HEADER_EXPERIMENTAL).orElse(null));
+ }
+ }
+
+ @Nested
+ class WhenUsingEnvironmentVariables {
+
+ @Test
+ void shouldSetExcludeDeprecatedAfterHeaderFromEnvironmentVariable() throws Exception {
+ Map env = new HashMap<>();
+ env.put(XGleanHeadersHook.ENV_EXCLUDE_DEPRECATED_AFTER, "2027-01-01");
+
+ Hook.BeforeRequest hook = XGleanHeadersHook.createSyncHook(createEnvProvider(env));
+ HttpRequest request = createMockRequest();
+ SDKConfiguration config = createConfig(null, null);
+ Hook.BeforeRequestContext context = createMockContext(config);
+
+ HttpRequest result = hook.beforeRequest(context, request);
+
+ assertEquals("2027-01-01", result.headers().firstValue(HEADER_EXCLUDE_DEPRECATED_AFTER).orElse(null));
+ }
+
+ @Test
+ void shouldSetExperimentalHeaderFromEnvironmentVariable() throws Exception {
+ Map env = new HashMap<>();
+ env.put(XGleanHeadersHook.ENV_INCLUDE_EXPERIMENTAL, "true");
+
+ Hook.BeforeRequest hook = XGleanHeadersHook.createSyncHook(createEnvProvider(env));
+ HttpRequest request = createMockRequest();
+ SDKConfiguration config = createConfig(null, null);
+ Hook.BeforeRequestContext context = createMockContext(config);
+
+ HttpRequest result = hook.beforeRequest(context, request);
+
+ assertEquals("true", result.headers().firstValue(HEADER_EXPERIMENTAL).orElse(null));
+ }
+
+ @Test
+ void shouldSetBothHeadersFromEnvironmentVariables() throws Exception {
+ Map env = new HashMap<>();
+ env.put(XGleanHeadersHook.ENV_EXCLUDE_DEPRECATED_AFTER, "2027-06-15");
+ env.put(XGleanHeadersHook.ENV_INCLUDE_EXPERIMENTAL, "true");
+
+ Hook.BeforeRequest hook = XGleanHeadersHook.createSyncHook(createEnvProvider(env));
+ HttpRequest request = createMockRequest();
+ SDKConfiguration config = createConfig(null, null);
+ Hook.BeforeRequestContext context = createMockContext(config);
+
+ HttpRequest result = hook.beforeRequest(context, request);
+
+ assertEquals("2027-06-15", result.headers().firstValue(HEADER_EXCLUDE_DEPRECATED_AFTER).orElse(null));
+ assertEquals("true", result.headers().firstValue(HEADER_EXPERIMENTAL).orElse(null));
+ }
+
+ @Test
+ void shouldIgnoreNonTrueValuesForExperimentalEnvironmentVariable() throws Exception {
+ Map env = new HashMap<>();
+ env.put(XGleanHeadersHook.ENV_INCLUDE_EXPERIMENTAL, "false");
+
+ Hook.BeforeRequest hook = XGleanHeadersHook.createSyncHook(createEnvProvider(env));
+ HttpRequest request = createMockRequest();
+ SDKConfiguration config = createConfig(null, null);
+ Hook.BeforeRequestContext context = createMockContext(config);
+
+ HttpRequest result = hook.beforeRequest(context, request);
+
+ assertFalse(result.headers().firstValue(HEADER_EXPERIMENTAL).isPresent());
+ }
+ }
+
+ @Nested
+ class EnvironmentVariablesTakePrecedenceOverSDKOptions {
+
+ @Test
+ void shouldUseEnvironmentVariableForExcludeDeprecatedAfterWhenBothAreSet() throws Exception {
+ Map env = new HashMap<>();
+ env.put(XGleanHeadersHook.ENV_EXCLUDE_DEPRECATED_AFTER, "2027-12-31");
+
+ Hook.BeforeRequest hook = XGleanHeadersHook.createSyncHook(createEnvProvider(env));
+ HttpRequest request = createMockRequest();
+ SDKConfiguration config = createConfig("2026-01-01", null);
+ Hook.BeforeRequestContext context = createMockContext(config);
+
+ HttpRequest result = hook.beforeRequest(context, request);
+
+ assertEquals("2027-12-31", result.headers().firstValue(HEADER_EXCLUDE_DEPRECATED_AFTER).orElse(null));
+ }
+
+ @Test
+ void shouldUseEnvironmentVariableForIncludeExperimentalWhenBothAreSet() throws Exception {
+ Map env = new HashMap<>();
+ env.put(XGleanHeadersHook.ENV_INCLUDE_EXPERIMENTAL, "true");
+
+ Hook.BeforeRequest hook = XGleanHeadersHook.createSyncHook(createEnvProvider(env));
+ HttpRequest request = createMockRequest();
+ SDKConfiguration config = createConfig(null, false);
+ Hook.BeforeRequestContext context = createMockContext(config);
+
+ HttpRequest result = hook.beforeRequest(context, request);
+
+ assertEquals("true", result.headers().firstValue(HEADER_EXPERIMENTAL).orElse(null));
+ }
+
+ @Test
+ void shouldUseEnvironmentVariablesForBothHeadersWhenAllAreSet() throws Exception {
+ Map env = new HashMap<>();
+ env.put(XGleanHeadersHook.ENV_EXCLUDE_DEPRECATED_AFTER, "2028-01-01");
+ env.put(XGleanHeadersHook.ENV_INCLUDE_EXPERIMENTAL, "true");
+
+ Hook.BeforeRequest hook = XGleanHeadersHook.createSyncHook(createEnvProvider(env));
+ HttpRequest request = createMockRequest();
+ SDKConfiguration config = createConfig("2026-06-01", false);
+ Hook.BeforeRequestContext context = createMockContext(config);
+
+ HttpRequest result = hook.beforeRequest(context, request);
+
+ assertEquals("2028-01-01", result.headers().firstValue(HEADER_EXCLUDE_DEPRECATED_AFTER).orElse(null));
+ assertEquals("true", result.headers().firstValue(HEADER_EXPERIMENTAL).orElse(null));
+ }
+ }
+}