diff --git a/Makefile b/Makefile index 88d01fe4909..4ae6fc07ac7 100644 --- a/Makefile +++ b/Makefile @@ -525,14 +525,18 @@ workflowcheck: $(WORKFLOWCHECK) check: lint shell-check ##### Tests ##### +# verify-test-log reads the whole file, so a log left behind by an earlier run +# would be judged as if it belonged to this one. clean-test-output: @printf $(COLOR) "Delete test output..." @rm -rf $(TEST_OUTPUT_ROOT) + @rm -f test.log @go clean -testcache build-tests: @printf $(COLOR) "Build tests..." - @CGO_ENABLED=$(CGO_ENABLED) go test $(TEST_TAG_FLAG) -exec="true" -count=0 $(TEST_DIRS) + @CGO_ENABLED=$(CGO_ENABLED) go test $(TEST_TAG_FLAG) -exec="true" -count=0 $(filter-out $(MIXED_BRAIN_TEST_ROOT)%,$(TEST_DIRS)) + @cd $(MIXED_BRAIN_TEST_ROOT) && CGO_ENABLED=1 go test $(TEST_TAG_FLAG) -exec="true" -count=0 ./... unit-test: clean-test-output @printf $(COLOR) "Run unit tests..." diff --git a/cmd/tools/getproto/files.go b/cmd/tools/getproto/files.go index 4006de0ad41..3b1e31ca412 100644 --- a/cmd/tools/getproto/files.go +++ b/cmd/tools/getproto/files.go @@ -28,6 +28,7 @@ import ( rules "go.temporal.io/api/rules/v1" schedule "go.temporal.io/api/schedule/v1" sdk "go.temporal.io/api/sdk/v1" + stream "go.temporal.io/api/stream/v1" taskqueue "go.temporal.io/api/taskqueue/v1" update "go.temporal.io/api/update/v1" version "go.temporal.io/api/version/v1" @@ -93,6 +94,7 @@ func init() { importMap["temporal/api/sdk/v1/task_complete_metadata.proto"] = sdk.File_temporal_api_sdk_v1_task_complete_metadata_proto importMap["temporal/api/sdk/v1/user_metadata.proto"] = sdk.File_temporal_api_sdk_v1_user_metadata_proto importMap["temporal/api/sdk/v1/worker_config.proto"] = sdk.File_temporal_api_sdk_v1_worker_config_proto + importMap["temporal/api/stream/v1/message.proto"] = stream.File_temporal_api_stream_v1_message_proto importMap["temporal/api/taskqueue/v1/message.proto"] = taskqueue.File_temporal_api_taskqueue_v1_message_proto importMap["temporal/api/update/v1/message.proto"] = update.File_temporal_api_update_v1_message_proto importMap["temporal/api/version/v1/message.proto"] = version.File_temporal_api_version_v1_message_proto diff --git a/common/metrics/otel_metrics_handler_test.go b/common/metrics/otel_metrics_handler_test.go index a2d0b34eb53..bf08a9460e1 100644 --- a/common/metrics/otel_metrics_handler_test.go +++ b/common/metrics/otel_metrics_handler_test.go @@ -193,9 +193,14 @@ func TestMeter(t *testing.T) { cmpopts.SortSlices(func(x, y metricdata.Metrics) bool { return x.Name < y.Name }), - cmpopts.IgnoreFields(metricdata.DataPoint[int64]{}, "StartTime", "Time"), - cmpopts.IgnoreFields(metricdata.DataPoint[float64]{}, "StartTime", "Time"), - cmpopts.IgnoreFields(metricdata.HistogramDataPoint[int64]{}, "StartTime", "Time", "Bounds"), + // Exemplars are ignored rather than equated empty: the SDK hands back + // either a nil or an allocated-but-empty slice depending on whether + // its reservoir was ever touched, and equating empty globally would + // also hide a nil-versus-empty attribute set or data point. + cmpopts.IgnoreFields(metricdata.DataPoint[int64]{}, "StartTime", "Time", "Exemplars"), + cmpopts.IgnoreFields(metricdata.DataPoint[float64]{}, "StartTime", "Time", "Exemplars"), + cmpopts.IgnoreFields( + metricdata.HistogramDataPoint[int64]{}, "StartTime", "Time", "Bounds", "Exemplars"), ); diff != "" { t.Errorf("mismatch (-want, +got):\n%s", diff) } @@ -264,7 +269,8 @@ func TestMeter_TimerInSeconds(t *testing.T) { cmp.Comparer(func(a1, a2 attribute.Set) bool { return a1.Equals(&a2) }), - cmpopts.IgnoreFields(metricdata.HistogramDataPoint[float64]{}, "StartTime", "Time", "Bounds"), + cmpopts.IgnoreFields( + metricdata.HistogramDataPoint[float64]{}, "StartTime", "Time", "Bounds", "Exemplars"), ); diff != "" { t.Errorf("mismatch (-want, +got):\n%s", diff) } diff --git a/go.mod b/go.mod index b9c7674a01d..cb3edd1f83d 100644 --- a/go.mod +++ b/go.mod @@ -236,3 +236,5 @@ require ( ) tool golang.org/x/perf/cmd/benchstat + +replace go.temporal.io/api => github.com/moedash/api-go v1.63.6-0.20260925231147-63d1722ce567 diff --git a/go.sum b/go.sum index ec1c673c1b4..2eff8776cb5 100644 --- a/go.sum +++ b/go.sum @@ -321,6 +321,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/moedash/api-go v1.63.6-0.20260925231147-63d1722ce567 h1:lKAIMDl+aIBL3Y4GxDXab40rR6eVhGnPDnFbzzSCD2Y= +github.com/moedash/api-go v1.63.6-0.20260925231147-63d1722ce567/go.mod h1:acM0I9WPuYg8W3Pd9jOZvEgi7mRUttUQ4+e7fowKVnM= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= @@ -481,8 +483,6 @@ go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.3.0 h1:R go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.3.0/go.mod h1:I89cynRj8y+383o7tEQVg2SVA6SRgDVIouWPUVXjx0U= go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.3.0 h1:CQvJSldHRUN6Z8jsUeYv8J0lXRvygALXIzsmAeCcZE0= go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.3.0/go.mod h1:xSQ+mEfJe/GjK1LXEyVOoSI1N9JV9ZI923X5kup43W4= -go.temporal.io/api v1.63.6-0.20260909222256-20151aa90480 h1:1w2xEZFpL7R9NagEQeWTh7oZmZU3YUJGogusn9/LfCg= -go.temporal.io/api v1.63.6-0.20260909222256-20151aa90480/go.mod h1:acM0I9WPuYg8W3Pd9jOZvEgi7mRUttUQ4+e7fowKVnM= go.temporal.io/auto-scaled-workers v0.0.0-20260811170210-91f6fe1d10ab h1:99wXW0317BBi49d6xgMdA0EZtvA+xbBUWV4HsTEGEcg= go.temporal.io/auto-scaled-workers v0.0.0-20260811170210-91f6fe1d10ab/go.mod h1:hhHijO9XRPIkAflLJJHix61M9FzbRPqk8fSydkcLkqw= go.temporal.io/sdk v1.48.0 h1:WDctKDVuh0Z8Nf7euAyqs/EwcPg1JTIIq1Fut8Tq118= diff --git a/tests/api_fork_test.go b/tests/api_fork_test.go new file mode 100644 index 00000000000..c4e092ed005 --- /dev/null +++ b/tests/api_fork_test.go @@ -0,0 +1,59 @@ +package tests + +import ( + "testing" + + "github.com/stretchr/testify/require" + commandpb "go.temporal.io/api/command/v1" + enumspb "go.temporal.io/api/enums/v1" + historypb "go.temporal.io/api/history/v1" + streampb "go.temporal.io/api/stream/v1" + "go.temporal.io/api/workflowservice/v1" + "google.golang.org/protobuf/proto" +) + +// The api fork has to round-trip the new shapes over the wire, not merely +// compile. A field added without its descriptor would compile and silently +// drop on marshal. +func TestApiForkCarriesStreamShapes(t *testing.T) { + require.Equal(t, enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS, enumspb.CommandType(19)) + + cmd := &commandpb.Command{ + CommandType: enumspb.COMMAND_TYPE_APPEND_STREAM_RECORDS, + Attributes: &commandpb.Command_AppendStreamRecordsCommandAttributes{ + AppendStreamRecordsCommandAttributes: &commandpb.AppendStreamRecordsCommandAttributes{ + StreamName: "s1", + Records: []*streampb.StreamRecord{{Topic: "tokens"}}, + }, + }, + } + b, err := proto.Marshal(cmd) + require.NoError(t, err) + var back commandpb.Command + require.NoError(t, proto.Unmarshal(b, &back)) + require.Equal(t, "s1", back.GetAppendStreamRecordsCommandAttributes().GetStreamName()) + require.Equal(t, "tokens", + back.GetAppendStreamRecordsCommandAttributes().GetRecords()[0].GetTopic()) + + resp := &workflowservice.PollWorkflowTaskQueueResponse{ + StreamSlices: []*streampb.StreamSlice{{StreamId: "s1", FromOffset: 4, ToOffset: 7}}, + } + rb, err := proto.Marshal(resp) + require.NoError(t, err) + var rback workflowservice.PollWorkflowTaskQueueResponse + require.NoError(t, proto.Unmarshal(rb, &rback)) + require.Equal(t, int64(7), rback.GetStreamSlices()[0].GetToOffset()) + + attrs := &historypb.WorkflowTaskCompletedEventAttributes{ + ConsumedStreamRanges: []*streampb.StreamRange{{StreamId: "s1", FromOffset: 4, ToOffset: 4}}, + } + ab, err := proto.Marshal(attrs) + require.NoError(t, err) + var aback historypb.WorkflowTaskCompletedEventAttributes + require.NoError(t, proto.Unmarshal(ab, &aback)) + // An empty range has to survive the round trip: it is the fact that a + // subscription observed nothing, which replay must reproduce. + require.Len(t, aback.GetConsumedStreamRanges(), 1) + require.Equal(t, + aback.GetConsumedStreamRanges()[0].GetFromOffset(), aback.GetConsumedStreamRanges()[0].GetToOffset()) +}