diff --git a/internal/cmd/build/main.go b/internal/cmd/build/main.go index 0d7de86a8..586d5b3a7 100644 --- a/internal/cmd/build/main.go +++ b/internal/cmd/build/main.go @@ -20,6 +20,7 @@ import ( _ "honnef.co/go/tools/staticcheck" "go.temporal.io/sdk/client" + "go.temporal.io/sdk/temporal" "go.temporal.io/sdk/testsuite" ) @@ -109,6 +110,13 @@ func (b *builder) integrationTest() error { } } + customKeyField := temporal.NewSearchAttributeKeyKeyword("CustomKeywordField") + customStringField := temporal.NewSearchAttributeKeyString("CustomStringField") + searchAttributes := temporal.NewSearchAttributes( + customKeyField.ValueSet("Keyword"), + customStringField.ValueSet("Text"), + ) + // Start dev server if wanted if *devServerFlag { devServer, err := testsuite.StartDevServer(context.Background(), testsuite.DevServerOptions{ @@ -116,13 +124,12 @@ func (b *builder) integrationTest() error { HostPort: "127.0.0.1:7233", Namespace: "integration-test-namespace", }, - DBFilename: "temporal.sqlite", - LogLevel: "warn", + DBFilename: "temporal.sqlite", + LogLevel: "warn", + SearchAttributes: searchAttributes, ExtraArgs: []string{ "--sqlite-pragma", "journal_mode=WAL", "--sqlite-pragma", "synchronous=OFF", - "--search-attribute", "CustomKeywordField=Keyword", - "--search-attribute", "CustomStringField=Text", "--dynamic-config-value", "frontend.enableExecuteMultiOperation=true", "--dynamic-config-value", "frontend.enableUpdateWorkflowExecution=true", "--dynamic-config-value", "frontend.enableUpdateWorkflowExecutionAsyncAccepted=true", @@ -227,7 +234,7 @@ func (b *builder) unitTest() error { testDirMap := map[string]struct{}{} var testDirs []string err := fs.WalkDir(os.DirFS(b.rootDir), ".", func(p string, d fs.DirEntry, err error) error { - if !strings.HasPrefix(p, "test") && strings.HasSuffix(p, "_test.go") { + if (!strings.HasPrefix(p, "test") || strings.HasPrefix(p, "testsuite")) && strings.HasSuffix(p, "_test.go") { dir := path.Dir(p) if _, ok := testDirMap[dir]; !ok { testDirMap[dir] = struct{}{} diff --git a/testsuite/devserver.go b/testsuite/devserver.go index fd2228fc9..d3d5a9a82 100644 --- a/testsuite/devserver.go +++ b/testsuite/devserver.go @@ -23,6 +23,7 @@ import ( "go.temporal.io/sdk/internal" ilog "go.temporal.io/sdk/internal/log" "go.temporal.io/sdk/log" + "go.temporal.io/sdk/temporal" ) // Cached download of the dev server. @@ -56,6 +57,8 @@ type DevServerOptions struct { LogFormat string // Log level - defaults to "warn". LogLevel string + // Search Attributes to register with the dev server. + SearchAttributes temporal.SearchAttributes // Additional arguments to the dev server. ExtraArgs []string // Where to redirect stdout and stderr, if nil they will be redirected to the current process. @@ -142,6 +145,9 @@ func prepareCommand(options *DevServerOptions, host, port, namespace string) []s if options.UIPort != "" { args = append(args, "--ui-port", options.UIPort) } + for searchAttribute := range options.SearchAttributes.GetUntypedValues() { + args = append(args, "--search-attribute", searchAttribute.GetName()+"="+searchAttribute.GetValueType().String()) + } return append(args, options.ExtraArgs...) } diff --git a/testsuite/devserver_test.go b/testsuite/devserver_test.go index f918b7ade..cd647b77e 100644 --- a/testsuite/devserver_test.go +++ b/testsuite/devserver_test.go @@ -2,12 +2,14 @@ package testsuite_test import ( "context" - "testing" - "github.com/stretchr/testify/require" "go.temporal.io/api/workflowservice/v1" "go.temporal.io/sdk/client" + "go.temporal.io/sdk/temporal" "go.temporal.io/sdk/testsuite" + "go.temporal.io/sdk/workflow" + "testing" + "time" ) func TestStartDevServer_Defaults(t *testing.T) { @@ -48,3 +50,66 @@ func TestStartDevServer_FrontendHostPort(t *testing.T) { require.NoError(t, err) require.NotNil(t, info.Capabilities) } + +func TestStartDevServer_SearchAttributes(t *testing.T) { + attrBool := temporal.NewSearchAttributeKeyBool("GoTemporalTestBool") + attrTime := temporal.NewSearchAttributeKeyTime("GoTemporalTestTime") + attrFloat := temporal.NewSearchAttributeKeyFloat64("GoTemporalTestFloat") + attrString := temporal.NewSearchAttributeKeyString("GoTemporalTestString") + attrInt := temporal.NewSearchAttributeKeyInt64("GoTemporalTestInt") + attrKeyword := temporal.NewSearchAttributeKeyKeyword("GoTemporalTestKeyword") + attrKeywordList := temporal.NewSearchAttributeKeyKeywordList("GoTemporalTestKeywordList") + now := time.Now() + sa := temporal.NewSearchAttributes( + attrBool.ValueSet(true), + attrTime.ValueSet(now), + attrFloat.ValueSet(5.4), + attrString.ValueSet("string"), + attrInt.ValueSet(10), + attrKeyword.ValueSet("keyword"), + attrKeywordList.ValueSet([]string{"value1", "value2"}), + ) + + opts := testsuite.DevServerOptions{ + SearchAttributes: sa, + } + + // Confirm that when used in env without SAs it fails + server, err := testsuite.StartDevServer(context.Background(), testsuite.DevServerOptions{}) + require.NoError(t, err) + defer func() { _ = server.Stop() }() + + _, err = server.Client().ExecuteWorkflow(context.Background(), client.StartWorkflowOptions{ + TypedSearchAttributes: sa, + }, func(ctx workflow.Context) error { return nil }) + require.Error(t, err) + + // Confirm that when used in env with SAs it succeeds + server1, err := testsuite.StartDevServer(context.Background(), opts) + require.NoError(t, err) + defer func() { _ = server1.Stop() }() + + c := server1.Client() + + run, err := c.ExecuteWorkflow(context.Background(), client.StartWorkflowOptions{ + TypedSearchAttributes: sa, + TaskQueue: "dev-server-search-attributes-test", + }, func(ctx workflow.Context) error { return nil }) + require.NoError(t, err) + + describe, err := c.DescribeWorkflow(context.Background(), run.GetID(), run.GetRunID()) + require.NoError(t, err) + saTime, found := sa.GetTime(attrTime) + require.True(t, found) + describeTime, found := describe.TypedSearchAttributes.GetTime(attrTime) + require.True(t, found) + // Time in Go must be compared with time.Equal to accurately compare time equality + require.True(t, saTime.Equal(describeTime)) + + untypedSa := sa.GetUntypedValues() + for key, val := range describe.TypedSearchAttributes.GetUntypedValues() { + if key != attrTime { + require.Equal(t, untypedSa[key], val) + } + } +}