Files
tasks/tests/links_test.go
Roey BermanandGitHub 63d0fc9343 Propagate links from all supported API requests to events (#6629)
## What changed?

Implement the server side for https://github.com/temporalio/api/pull/455
(later fixed by https://github.com/temporalio/api/pull/460).
Also improved link validation.

Note that batch jobs still don't attach links at the moment, I'll
probably do that in a follow up PR.

## Why?

Support links in various places.

## How did you test it?

Added both unit and functional test coverage.
2024-10-09 10:39:51 -07:00

219 lines
6.7 KiB
Go

// The MIT License
//
// Copyright (c) 2024 Temporal Technologies Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package tests
import (
"context"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/suite"
commonpb "go.temporal.io/api/common/v1"
enumspb "go.temporal.io/api/enums/v1"
"go.temporal.io/api/taskqueue/v1"
"go.temporal.io/api/workflowservice/v1"
"go.temporal.io/sdk/client"
"go.temporal.io/server/common/testing/protorequire"
"go.temporal.io/server/tests/testcore"
)
type LinksSuite struct {
testcore.ClientFunctionalSuite
}
func TestLinksTestSuite(t *testing.T) {
suite.Run(t, new(LinksSuite))
}
var links = []*commonpb.Link{
{
Variant: &commonpb.Link_WorkflowEvent_{
WorkflowEvent: &commonpb.Link_WorkflowEvent{
Namespace: "dont-care",
WorkflowId: "whatever",
RunId: uuid.NewString(),
},
},
},
}
func (s *LinksSuite) TestTerminateWorkflow_LinksAttachedToEvent() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
run, err := s.SdkClient().ExecuteWorkflow(
ctx,
client.StartWorkflowOptions{
TaskQueue: "dont-care",
},
"test-workflow-type",
)
s.NoError(err)
// TODO(bergundy): Use SdkClient if and when it exposes links on TerminateWorkflow.
_, err = s.FrontendClient().TerminateWorkflowExecution(ctx, &workflowservice.TerminateWorkflowExecutionRequest{
Namespace: s.Namespace(),
WorkflowExecution: &commonpb.WorkflowExecution{
WorkflowId: run.GetID(),
},
Reason: "test",
Links: links,
})
s.NoError(err)
history := s.SdkClient().GetWorkflowHistory(ctx, run.GetID(), "", false, enumspb.HISTORY_EVENT_FILTER_TYPE_CLOSE_EVENT)
event, err := history.Next()
s.NoError(err)
protorequire.ProtoSliceEqual(s.T(), links, event.Links)
}
func (s *LinksSuite) TestRequestCancelWorkflow_LinksAttachedToEvent() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
run, err := s.SdkClient().ExecuteWorkflow(
ctx,
client.StartWorkflowOptions{
TaskQueue: "dont-care",
},
"test-workflow-type",
)
s.NoError(err)
// TODO(bergundy): Use SdkClient if and when it exposes links on CancelWorkflow.
_, err = s.FrontendClient().RequestCancelWorkflowExecution(ctx, &workflowservice.RequestCancelWorkflowExecutionRequest{
Namespace: s.Namespace(),
WorkflowExecution: &commonpb.WorkflowExecution{
WorkflowId: run.GetID(),
},
Reason: "test",
Links: links,
})
s.NoError(err)
history := s.SdkClient().GetWorkflowHistory(ctx, run.GetID(), "", false, enumspb.HISTORY_EVENT_FILTER_TYPE_ALL_EVENT)
foundEvent := false
for history.HasNext() {
event, err := history.Next()
s.NoError(err)
if event.EventType != enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_CANCEL_REQUESTED {
continue
}
foundEvent = true
protorequire.ProtoSliceEqual(s.T(), links, event.Links)
}
s.True(foundEvent)
}
func (s *LinksSuite) TestSignalWorkflowExecution_LinksAttachedToEvent() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
run, err := s.SdkClient().ExecuteWorkflow(
ctx,
client.StartWorkflowOptions{
TaskQueue: "dont-care",
},
"test-workflow-type",
)
s.NoError(err)
// TODO(bergundy): Use SdkClient if and when it exposes links on SignalWorkflow.
_, err = s.FrontendClient().SignalWorkflowExecution(ctx, &workflowservice.SignalWorkflowExecutionRequest{
Namespace: s.Namespace(),
WorkflowExecution: &commonpb.WorkflowExecution{
WorkflowId: run.GetID(),
},
SignalName: "dont-care",
Identity: "test",
RequestId: uuid.NewString(),
Links: links,
})
s.NoError(err)
history := s.SdkClient().GetWorkflowHistory(ctx, run.GetID(), "", false, enumspb.HISTORY_EVENT_FILTER_TYPE_ALL_EVENT)
foundEvent := false
for history.HasNext() {
event, err := history.Next()
s.NoError(err)
if event.EventType != enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED {
continue
}
foundEvent = true
protorequire.ProtoSliceEqual(s.T(), links, event.Links)
}
s.True(foundEvent)
}
func (s *LinksSuite) TestSignalWithStartWorkflowExecution_LinksAttachedToRelevantEvents() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
workflowID := testcore.RandomizeStr(s.T().Name())
// TODO(bergundy): Use SdkClient if and when it exposes links on SignalWithStartWorkflow.
request := &workflowservice.SignalWithStartWorkflowExecutionRequest{
Namespace: s.Namespace(),
WorkflowId: workflowID,
WorkflowType: &commonpb.WorkflowType{
Name: "dont-care",
},
SignalName: "dont-care",
Identity: "test",
TaskQueue: &taskqueue.TaskQueue{
Name: "dont-care",
},
RequestId: uuid.NewString(),
Links: links,
}
_, err := s.FrontendClient().SignalWithStartWorkflowExecution(ctx, request)
s.NoError(err)
// Send a second request and verify that the new signal has links attached to it too.
request.RequestId = uuid.NewString()
_, err = s.FrontendClient().SignalWithStartWorkflowExecution(ctx, request)
s.NoError(err)
history := s.SdkClient().GetWorkflowHistory(ctx, workflowID, "", false, enumspb.HISTORY_EVENT_FILTER_TYPE_ALL_EVENT)
foundStartEvent := false
foundFirstSignal := false
foundSecondSignal := false
for history.HasNext() {
event, err := history.Next()
s.NoError(err)
if event.EventType == enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED {
if foundFirstSignal {
foundSecondSignal = true
} else {
foundFirstSignal = true
}
protorequire.ProtoSliceEqual(s.T(), links, event.Links)
}
if event.EventType == enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED {
foundStartEvent = true
protorequire.ProtoSliceEqual(s.T(), links, event.Links)
}
}
s.True(foundStartEvent)
s.True(foundFirstSignal)
s.True(foundSecondSignal)
}