From 79b3e4e6f1386c5edb015f70e52734b3bb86e6b2 Mon Sep 17 00:00:00 2001 From: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com> Date: Tue, 3 Nov 2026 17:33:10 +0000 Subject: [PATCH] test: Wire wrapper, context comparison and FDv1 fallback options in the contract test service Declare context-type, context-comparison, wrapper and fdv1-fallback, and drop a suppression that matches no test on the pinned v3 harness. Generated-by: termag --- .../launchdarkly/sdktest/Representations.java | 39 +++++++++++ .../launchdarkly/sdktest/SdkClientEntity.java | 66 +++++++++++++++++-- .../com/launchdarkly/sdktest/TestService.java | 6 +- testharness-suppressions-fdv2.txt | 1 - 4 files changed, 105 insertions(+), 7 deletions(-) diff --git a/contract-tests/src/main/java/com/launchdarkly/sdktest/Representations.java b/contract-tests/src/main/java/com/launchdarkly/sdktest/Representations.java index 5c94b5806..ba409d961 100644 --- a/contract-tests/src/main/java/com/launchdarkly/sdktest/Representations.java +++ b/contract-tests/src/main/java/com/launchdarkly/sdktest/Representations.java @@ -40,6 +40,7 @@ public static class SdkConfigParams { SdkConfigServiceEndpointParams serviceEndpoints; SdkConfigHookParams hooks; SdkConfigDataSystemParams dataSystem; + SdkConfigWrapperParams wrapper; } public static class SdkConfigStreamParams { @@ -68,6 +69,11 @@ public static class SdkConfigTagParams { String applicationVersionName; } + public static class SdkConfigWrapperParams { + String name; + String version; + } + public static class SdkConfigServiceEndpointParams { String streaming; String polling; @@ -109,6 +115,7 @@ public static class SdkConfigDataSystemParams { SdkConfigConnectionModeConfig connectionModeConfig; List initializers; List synchronizers; + SdkConfigPollParams fdv1Fallback; } public static class SdkConfigConnectionModeConfig { @@ -138,6 +145,7 @@ public static class CommandParams { CustomEventParams customEvent; ContextBuildParams contextBuild; ContextConvertParams contextConvert; + ContextComparisonPairParams contextComparison; } public static class EvaluateFlagParams { @@ -209,4 +217,35 @@ public static class ContextBuildResponse { public static class ContextConvertParams { String input; } + + public static class ContextComparisonPairParams { + ContextComparisonParams context1; + ContextComparisonParams context2; + } + + public static class ContextComparisonParams { + ContextComparisonSingleParams single; + ContextComparisonSingleParams[] multi; + } + + public static class ContextComparisonSingleParams { + String kind; + String key; + AttributeDefinition[] attributes; + PrivateAttribute[] privateAttributes; + } + + public static class AttributeDefinition { + String name; + LDValue value; + } + + public static class PrivateAttribute { + String value; + boolean literal; + } + + public static class ContextComparisonResponse { + boolean equals; + } } diff --git a/contract-tests/src/main/java/com/launchdarkly/sdktest/SdkClientEntity.java b/contract-tests/src/main/java/com/launchdarkly/sdktest/SdkClientEntity.java index 2056d47e2..6c133dfee 100644 --- a/contract-tests/src/main/java/com/launchdarkly/sdktest/SdkClientEntity.java +++ b/contract-tests/src/main/java/com/launchdarkly/sdktest/SdkClientEntity.java @@ -2,6 +2,7 @@ import com.launchdarkly.logging.LDLogAdapter; import com.launchdarkly.logging.LDLogger; +import com.launchdarkly.sdk.AttributeRef; import com.launchdarkly.sdk.ContextBuilder; import com.launchdarkly.sdk.ContextMultiBuilder; import com.launchdarkly.sdk.EvaluationDetail; @@ -21,6 +22,7 @@ import com.launchdarkly.sdk.android.integrations.DataSystemBuilder; import com.launchdarkly.sdk.android.integrations.EventProcessorBuilder; import com.launchdarkly.sdk.android.integrations.Hook; +import com.launchdarkly.sdk.android.integrations.HttpConfigurationBuilder; import com.launchdarkly.sdk.android.integrations.InitializerEntry; import com.launchdarkly.sdk.android.integrations.PollingDataSourceBuilder; import com.launchdarkly.sdk.android.integrations.PollingInitializerEntry; @@ -31,10 +33,15 @@ import com.launchdarkly.sdk.android.integrations.SynchronizerEntry; import com.launchdarkly.sdk.json.JsonSerialization; +import com.launchdarkly.sdktest.Representations.AttributeDefinition; import com.launchdarkly.sdktest.Representations.CommandParams; import com.launchdarkly.sdktest.Representations.ContextBuildParams; import com.launchdarkly.sdktest.Representations.ContextBuildResponse; import com.launchdarkly.sdktest.Representations.ContextBuildSingleParams; +import com.launchdarkly.sdktest.Representations.ContextComparisonPairParams; +import com.launchdarkly.sdktest.Representations.ContextComparisonParams; +import com.launchdarkly.sdktest.Representations.ContextComparisonResponse; +import com.launchdarkly.sdktest.Representations.ContextComparisonSingleParams; import com.launchdarkly.sdktest.Representations.ContextConvertParams; import com.launchdarkly.sdktest.Representations.CreateInstanceParams; import com.launchdarkly.sdktest.Representations.CustomEventParams; @@ -46,6 +53,7 @@ import com.launchdarkly.sdktest.Representations.HookData; import com.launchdarkly.sdktest.Representations.HookErrors; import com.launchdarkly.sdktest.Representations.IdentifyEventParams; +import com.launchdarkly.sdktest.Representations.PrivateAttribute; import com.launchdarkly.sdktest.Representations.SdkConfigParams; import com.launchdarkly.sdktest.Representations.SdkConfigDataSystemParams; import com.launchdarkly.sdktest.Representations.SdkConfigConnectionModeConfig; @@ -128,6 +136,8 @@ public Object doCommand(CommandParams params) throws TestService.BadRequestExcep return doContextBuild(params.contextBuild); case "contextConvert": return doContextConvert(params.contextConvert); + case "contextComparison": + return doContextComparison(params.contextComparison); default: throw new TestService.BadRequestException("unknown command: " + params.command); } @@ -268,6 +278,41 @@ private ContextBuildResponse doContextConvert(ContextConvertParams params) { return resp; } + private ContextComparisonResponse doContextComparison(ContextComparisonPairParams params) { + LDContext c1 = doContextComparisonBuild(params.context1); + LDContext c2 = doContextComparisonBuild(params.context2); + ContextComparisonResponse resp = new ContextComparisonResponse(); + resp.equals = c1.equals(c2); + return resp; + } + + private LDContext doContextComparisonBuild(ContextComparisonParams params) { + if (params.multi == null) { + return doContextComparisonBuildSingle(params.single); + } + ContextMultiBuilder b = LDContext.multiBuilder(); + for (ContextComparisonSingleParams s: params.multi) { + b.add(doContextComparisonBuildSingle(s)); + } + return b.build(); + } + + private LDContext doContextComparisonBuildSingle(ContextComparisonSingleParams params) { + ContextBuilder b = LDContext.builder(params.key).kind(params.kind); + if (params.attributes != null) { + for (AttributeDefinition attr: params.attributes) { + b.set(attr.name, LDValue.normalize(attr.value)); + } + } + if (params.privateAttributes != null) { + for (PrivateAttribute attr: params.privateAttributes) { + b.privateAttributes(attr.literal ? + AttributeRef.fromLiteral(attr.value) : AttributeRef.fromPath(attr.value)); + } + } + return b.build(); + } + private LDConfig buildSdkConfig(SdkConfigParams params, LDLogAdapter logAdapter, String tag) { LDConfig.Builder builder = new LDConfig.Builder(params.clientSide.includeEnvironmentAttributes ? LDConfig.Builder.AutoEnvAttributes.Enabled : LDConfig.Builder.AutoEnvAttributes.Disabled); @@ -284,6 +329,11 @@ private LDConfig buildSdkConfig(SdkConfigParams params, LDLogAdapter logAdapter, ConfigHelper.configureIsolatedInMemoryPersistence(builder); if (params.dataSystem != null) { + // The SDK's FDv1 fallback synchronizer polls the top-level polling endpoint. It has + // no option for its poll interval, so pollIntervalMs is not used. + if (params.dataSystem.fdv1Fallback != null && params.dataSystem.fdv1Fallback.baseUri != null) { + endpoints.polling(params.dataSystem.fdv1Fallback.baseUri); + } configureDataSystem(builder, params.dataSystem); } else { if (params.polling != null && params.polling.baseUri != null) { @@ -328,11 +378,17 @@ private LDConfig buildSdkConfig(SdkConfigParams params, LDLogAdapter logAdapter, } builder.evaluationReasons(params.clientSide.evaluationReasons); - builder.http( - Components.httpConfiguration() - .useReport(params.clientSide.useReport) - .usePost(params.clientSide.usePost) - ); + HttpConfigurationBuilder httpBuilder = Components.httpConfiguration() + .useReport(params.clientSide.useReport) + .usePost(params.clientSide.usePost); + // The harness sends an empty string for an unset wrapper name or version. The SDK + // would put an empty string in the header as given, so treat it as unset. + if (params.wrapper != null && params.wrapper.name != null && !params.wrapper.name.isEmpty()) { + String wrapperVersion = params.wrapper.version != null && !params.wrapper.version.isEmpty() ? + params.wrapper.version : null; + httpBuilder.wrapper(params.wrapper.name, wrapperVersion); + } + builder.http(httpBuilder); if (params.tags != null) { ApplicationInfoBuilder ab = Components.applicationInfo(); diff --git a/contract-tests/src/main/java/com/launchdarkly/sdktest/TestService.java b/contract-tests/src/main/java/com/launchdarkly/sdktest/TestService.java index 246388bc7..e9ef3e010 100644 --- a/contract-tests/src/main/java/com/launchdarkly/sdktest/TestService.java +++ b/contract-tests/src/main/java/com/launchdarkly/sdktest/TestService.java @@ -33,7 +33,10 @@ public class TestService extends NanoHTTPD { "service-endpoints", "singleton", "strongly-typed", + "context-type", + "context-comparison", "tags", + "wrapper", "auto-env-attributes", "inline-context-all", "anonymous-redaction", @@ -46,7 +49,8 @@ public class TestService extends NanoHTTPD { "client-event-source-http-errors", "retry-conformance-fdv1-streaming", "retry-conformance-fdv1-polling", - "client-use-post" + "client-use-post", + "fdv1-fallback" }; private static final String MIME_JSON = "application/json"; static final Gson gson = new GsonBuilder() diff --git a/testharness-suppressions-fdv2.txt b/testharness-suppressions-fdv2.txt index ceb41bc53..7b94f9b12 100644 --- a/testharness-suppressions-fdv2.txt +++ b/testharness-suppressions-fdv2.txt @@ -1,4 +1,3 @@ -streaming/fdv2/fallback to FDv1 handling events/feature events/single-kind anonymous context redacts all attributes events/feature events/multi-kind with anonymous context redacts attributes appropriately events/context properties/single-kind, allAttributesPrivate, slash-prefixed attribute name