From 19f5de8926a5f660b5c1a6e7a961d6e980d10040 Mon Sep 17 00:00:00 2001 From: Chad Retz Date: Wed, 26 Jun 2024 10:52:22 -0500 Subject: [PATCH] Accept search attributes for dev server Fixes #282 --- src/Temporalio/Bridge/EphemeralServer.cs | 4 +- src/Temporalio/Bridge/OptionsExtensions.cs | 13 ++++- src/Temporalio/Testing/WorkflowEnvironment.cs | 2 +- .../WorkflowEnvironmentStartLocalOptions.cs | 8 +++ .../Testing/WorkflowEnvironmentTests.cs | 51 +++++++++++++++++++ 5 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/Temporalio/Bridge/EphemeralServer.cs b/src/Temporalio/Bridge/EphemeralServer.cs index 15f60f43..a5969bff 100644 --- a/src/Temporalio/Bridge/EphemeralServer.cs +++ b/src/Temporalio/Bridge/EphemeralServer.cs @@ -40,12 +40,12 @@ internal class EphemeralServer : SafeHandle public bool HasTestService { get; private init; } /// - /// Start Temporalite. + /// Start dev server. /// /// Runtime to use. /// Options to use. /// Started server. - public static async Task StartTemporaliteAsync( + public static async Task StartDevServerAsync( Runtime runtime, Testing.WorkflowEnvironmentStartLocalOptions options) { diff --git a/src/Temporalio/Bridge/OptionsExtensions.cs b/src/Temporalio/Bridge/OptionsExtensions.cs index 0746fe94..e7680984 100644 --- a/src/Temporalio/Bridge/OptionsExtensions.cs +++ b/src/Temporalio/Bridge/OptionsExtensions.cs @@ -323,6 +323,17 @@ internal static class OptionsExtensions // Use TargetHost to get IP + Port options.ParseTargetHost(out string? ip, out int? port); ip ??= "127.0.0.1"; + + // If there are search attributes, prepend them to the args + var args = options.DevServerOptions.ExtraArgs; + if (options.SearchAttributes is { } attrs && attrs.Count > 0) + { + args = attrs. + SelectMany(v => new[] { "--search-attribute", $"{v.Name}={v.ValueType}" }). + Concat(args ?? Enumerable.Empty()). + ToArray(); + } + return new Interop.DevServerOptions() { test_server = scope.Pointer( @@ -334,7 +345,7 @@ internal static class OptionsExtensions download_version = scope.ByteArray(options.DevServerOptions.DownloadVersion), download_dest_dir = scope.ByteArray(options.DownloadDirectory), port = (ushort)(port ?? 0), - extra_args = scope.NewlineDelimited(options.DevServerOptions.ExtraArgs), + extra_args = scope.NewlineDelimited(args), }), namespace_ = scope.ByteArray(options.Namespace), ip = scope.ByteArray(ip), diff --git a/src/Temporalio/Testing/WorkflowEnvironment.cs b/src/Temporalio/Testing/WorkflowEnvironment.cs index e26d88d6..6dc67d01 100644 --- a/src/Temporalio/Testing/WorkflowEnvironment.cs +++ b/src/Temporalio/Testing/WorkflowEnvironment.cs @@ -67,7 +67,7 @@ public WorkflowEnvironment(ITemporalClient client) { options ??= new(); var runtime = options.Runtime ?? TemporalRuntime.Default; - var server = await Bridge.EphemeralServer.StartTemporaliteAsync( + var server = await Bridge.EphemeralServer.StartDevServerAsync( runtime.Runtime, options).ConfigureAwait(false); return await StartEphemeralAsync(server, options).ConfigureAwait(false); diff --git a/src/Temporalio/Testing/WorkflowEnvironmentStartLocalOptions.cs b/src/Temporalio/Testing/WorkflowEnvironmentStartLocalOptions.cs index 4bc33f54..6e3db018 100644 --- a/src/Temporalio/Testing/WorkflowEnvironmentStartLocalOptions.cs +++ b/src/Temporalio/Testing/WorkflowEnvironmentStartLocalOptions.cs @@ -1,3 +1,6 @@ +using System.Collections.Generic; +using Temporalio.Common; + namespace Temporalio.Testing { /// @@ -18,6 +21,11 @@ public class WorkflowEnvironmentStartLocalOptions : Client.TemporalClientConnect /// public bool UI { get; set; } + /// + /// Gets or sets search attributes registered on the dev server on start. + /// + public IReadOnlyCollection? SearchAttributes { get; set; } + /// /// Gets or sets unstable dev server options. /// diff --git a/tests/Temporalio.Tests/Testing/WorkflowEnvironmentTests.cs b/tests/Temporalio.Tests/Testing/WorkflowEnvironmentTests.cs index 3bb60884..045100bd 100644 --- a/tests/Temporalio.Tests/Testing/WorkflowEnvironmentTests.cs +++ b/tests/Temporalio.Tests/Testing/WorkflowEnvironmentTests.cs @@ -7,6 +7,7 @@ namespace Temporalio.Tests.Testing; using Temporalio.Activities; using Temporalio.Api.Enums.V1; using Temporalio.Client; +using Temporalio.Common; using Temporalio.Exceptions; using Temporalio.Testing; using Temporalio.Worker; @@ -199,4 +200,54 @@ public async Task StartTimeSkippingAsync_AutoTimeSkippingDisabled_SleepsFullTime }); }); } + + [Fact] + public async Task StartLocal_SearchAttributes_ProperlyRegistered() + { + // Prepare attrs + var attrBool = SearchAttributeKey.CreateBool("DotNetTemporalTestBool"); + var attrDateTime = SearchAttributeKey.CreateDateTimeOffset("DotNetTemporalTestDateTime"); + var attrDouble = SearchAttributeKey.CreateDouble("DotNetTemporalTestDouble"); + var attrKeyword = SearchAttributeKey.CreateKeyword("DotNetTemporalTestKeyword"); + var attrKeywordList = SearchAttributeKey.CreateKeywordList("DotNetTemporalTestKeywordList"); + var attrLong = SearchAttributeKey.CreateLong("DotNetTemporalTestLong"); + var attrText = SearchAttributeKey.CreateText("DotNetTemporalTestText"); + var attrVals = new SearchAttributeCollection.Builder(). + Set(attrBool, true). + Set(attrDateTime, new DateTimeOffset(2001, 1, 1, 0, 0, 0, TimeSpan.Zero)). + Set(attrDouble, 123.45). + Set(attrKeyword, "SomeKeyword"). + Set(attrKeywordList, new[] { "SomeKeyword1", "SomeKeyword2" }). + Set(attrLong, 678). + Set(attrText, "SomeText"). + ToSearchAttributeCollection(); + var attrs = new SearchAttributeKey[] + { + attrBool, attrDateTime, attrDouble, attrKeyword, attrKeywordList, attrLong, attrText, + }; + + // Confirm that when used in env without SAs it fails + await using var env1 = await WorkflowEnvironment.StartLocalAsync(); + var exc = await Assert.ThrowsAsync( + () => env1.Client.StartWorkflowAsync( + "my-workflow", + Array.Empty(), + new(id: $"wf-{Guid.NewGuid()}", taskQueue: $"tq-{Guid.NewGuid()}") + { + TypedSearchAttributes = attrVals, + })); + Assert.Contains("no mapping defined", exc.Message); + + // Confirm that when used in env with SAs it succeeds + await using var env2 = await WorkflowEnvironment.StartLocalAsync( + new() { SearchAttributes = attrs }); + var handle = await env2.Client.StartWorkflowAsync( + "my-workflow", + Array.Empty(), + new(id: $"wf-{Guid.NewGuid()}", taskQueue: $"tq-{Guid.NewGuid()}") + { + TypedSearchAttributes = attrVals, + }); + Assert.Equal(attrVals, (await handle.DescribeAsync()).TypedSearchAttributes); + } } \ No newline at end of file