From 108b0126b4509a00cdac91b18eaaf7190ebcc086 Mon Sep 17 00:00:00 2001 From: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com> Date: Fri, 25 Sep 2026 15:59:56 -0700 Subject: [PATCH] feat: Support the flag-overrides capability in the contract test service The test service advertises the flag-overrides capability and accepts the overrides block of the SDK configuration: filePaths, duplicateKeysHandling, changeDetection, and pollIntervalMs map onto the FileOverrides.Source() options, and the source is attached to the FDv2 data system builder. The service supplies a YamlDotNet parser so YAML documents can be tested. An optional first command line argument selects the port. --- .../server/contract-tests/Representations.cs | 9 ++++ .../server/contract-tests/SdkClientEntity.cs | 50 +++++++++++++++++++ pkgs/sdk/server/contract-tests/TestService.cs | 7 ++- .../server/contract-tests/TestService.csproj | 1 + 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/pkgs/sdk/server/contract-tests/Representations.cs b/pkgs/sdk/server/contract-tests/Representations.cs index 2a08744d9..9efeb5ce7 100644 --- a/pkgs/sdk/server/contract-tests/Representations.cs +++ b/pkgs/sdk/server/contract-tests/Representations.cs @@ -35,6 +35,15 @@ public class SdkConfigParams public SdkTagParams Tags { get; set; } public SdkHookParams Hooks { get; set; } public SdkConfigDataSystemParams DataSystem { get; set; } + public SdkConfigOverridesParams Overrides { get; set; } + } + + public class SdkConfigOverridesParams + { + public string[] FilePaths { get; set; } + public string DuplicateKeysHandling { get; set; } + public string ChangeDetection { get; set; } + public long? PollIntervalMs { get; set; } } public class SdkTagParams diff --git a/pkgs/sdk/server/contract-tests/SdkClientEntity.cs b/pkgs/sdk/server/contract-tests/SdkClientEntity.cs index 239c1b93b..ac3dfa00d 100644 --- a/pkgs/sdk/server/contract-tests/SdkClientEntity.cs +++ b/pkgs/sdk/server/contract-tests/SdkClientEntity.cs @@ -11,6 +11,7 @@ using LaunchDarkly.Sdk.Server.Interfaces; using LaunchDarkly.Sdk.Server.Migrations; using LaunchDarkly.Sdk.Server.Subsystems; +using YamlDotNet.Serialization; namespace TestService { @@ -663,12 +664,61 @@ private static Configuration BuildSdkConfig(SdkConfigParams sdkParams, ILogAdapt dataSystemBuilder.FDv1FallbackSynchronizer(fdv1FallbackBuilder); } + // The override source is an option of the FDv2 data system. + if (sdkParams.Overrides != null) + { + dataSystemBuilder.Overrides(BuildOverrideSource(sdkParams.Overrides)); + } + builder.DataSystem(dataSystemBuilder); } + else if (sdkParams.Overrides != null) + { + throw new ArgumentException("the overrides configuration requires a dataSystem configuration"); + } return builder.Build(); } + private static FileOverrideSourceBuilder BuildOverrideSource(SdkConfigOverridesParams p) + { + var yaml = new DeserializerBuilder().WithAttemptingUnquotedStringTypeDeserialization().Build(); + var source = FileOverrides.Source() + .FilePaths(p.FilePaths ?? new string[0]) + .Parser(s => yaml.Deserialize(s)); + switch (p.DuplicateKeysHandling) + { + case null: + break; + case "fail": + source.DuplicateKeysHandling(FileOverrideTypes.DuplicateKeysHandling.Fail); + break; + case "ignore": + source.DuplicateKeysHandling(FileOverrideTypes.DuplicateKeysHandling.Ignore); + break; + default: + throw new ArgumentException("unrecognized duplicateKeysHandling: " + p.DuplicateKeysHandling); + } + switch (p.ChangeDetection) + { + case null: + break; + case "polling": + source.ChangeDetection(FileOverrideTypes.ChangeDetection.Polling); + break; + case "watching": + source.ChangeDetection(FileOverrideTypes.ChangeDetection.Watching); + break; + default: + throw new ArgumentException("unrecognized changeDetection: " + p.ChangeDetection); + } + if (p.PollIntervalMs.HasValue) + { + source.PollInterval(TimeSpan.FromMilliseconds(p.PollIntervalMs.Value)); + } + return source; + } + private static IComponentConfigurer CreateSynchronizer( SdkConfigDataSynchronizerParams synchronizer, string payloadFilter) diff --git a/pkgs/sdk/server/contract-tests/TestService.cs b/pkgs/sdk/server/contract-tests/TestService.cs index a59374eb0..9ce3be0b2 100644 --- a/pkgs/sdk/server/contract-tests/TestService.cs +++ b/pkgs/sdk/server/contract-tests/TestService.cs @@ -12,9 +12,11 @@ public class Program { public static void Main(string[] args) { + // An optional first argument selects the port, so that several test services can run at once. + var port = args.Length > 0 ? int.Parse(args[0]) : 8000; var quitSignal = new EventWaitHandle(false, EventResetMode.AutoReset); var app = new Webapp(quitSignal); - var server = HttpServer.Start(8000, app.Handler); + var server = HttpServer.Start(port, app.Handler); server.Recorder.Enabled = false; quitSignal.WaitOne(); server.Dispose(); @@ -49,7 +51,8 @@ public class Webapp "fdv1-fallback", "instance-id", "retry-conformance-fdv1-streaming", - "retry-conformance-fdv1-polling" + "retry-conformance-fdv1-polling", + "flag-overrides" }; public readonly Handler Handler; diff --git a/pkgs/sdk/server/contract-tests/TestService.csproj b/pkgs/sdk/server/contract-tests/TestService.csproj index 7b311dfaa..8123e1389 100644 --- a/pkgs/sdk/server/contract-tests/TestService.csproj +++ b/pkgs/sdk/server/contract-tests/TestService.csproj @@ -17,6 +17,7 @@ +