feat: trace task sources to fixed workflow activations and rounds
This commit is contained in:
@@ -137,6 +137,18 @@ func workflowAPIError(t *testing.T, err error, code string) {
|
||||
}
|
||||
}
|
||||
|
||||
func assertWorkflowSourceSDK(t *testing.T, ctx context.Context, api *Client, v WorkflowInstance, task string, sequence, round uint32, parent string) {
|
||||
t.Helper()
|
||||
source, err := api.GetTaskSource(ctx, task)
|
||||
if err != nil || source.Workflow == nil || source.Kind != "WORKFLOW" {
|
||||
t.Fatal("workflow source", err)
|
||||
}
|
||||
w := source.Workflow
|
||||
if w.WorkflowID != v.ID || w.DefinitionID != v.DefinitionID || w.DefinitionVersionID != v.DefinitionVersionID || w.Version != v.Version || w.ActivationSequence != sequence || w.PollRound != round || parent == "" && w.RetryOf != nil || parent != "" && (w.RetryOf == nil || *w.RetryOf != parent) {
|
||||
t.Fatal("fixed source mismatch", w)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegrationWorkflowInstancesSDK(t *testing.T) {
|
||||
api, queue, token := integrationWorkerClient(t)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
|
||||
@@ -212,6 +224,7 @@ func TestIntegrationWorkflowInstancesSDK(t *testing.T) {
|
||||
w := workflowWorkerSDK(t, ctx, api, queue, handler)
|
||||
defer w.Close()
|
||||
first := finishWorkflowSDK(t, ctx, w, WorkerSucceeded, workflowExact)
|
||||
assertWorkflowSourceSDK(t, ctx, api, started.Instance, first.Task.ID, 1, 0, "")
|
||||
work, err := api.GetWorkflowStep(ctx, id, "work")
|
||||
if err != nil || work.Step.TaskID == nil || *work.Step.TaskID != first.Task.ID || work.Step.ActivationSequence != 1 {
|
||||
t.Fatal("step metadata", err)
|
||||
@@ -349,6 +362,8 @@ func TestIntegrationWorkflowInstancesSDK(t *testing.T) {
|
||||
if err != nil || !repeated.Deduplicated || repeated.Task.ID != child.Task.ID {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertWorkflowSourceSDK(t, ctx, api, v, child.Task.ID, 2, 0, *child.Task.RetryOf)
|
||||
assertWorkflowSourceSDK(t, ctx, api, v, *child.Task.RetryOf, 1, 0, "")
|
||||
continueWorkflowSDK(t, ctx, api, v.ID)
|
||||
a := finishWorkflowSDK(t, ctx, w, WorkerSucceeded, workflowExact)
|
||||
if a.Task.ID != child.Task.ID {
|
||||
@@ -374,6 +389,7 @@ func TestIntegrationWorkflowInstancesSDK(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertWorkflowSourceSDK(t, ctx, api, v, source.ID, 1, 1, "")
|
||||
reviewed, err := api.ReviewWorkflowTask(ctx, v.ID, workerTestID(t), ReviewWorkflowTaskRequest{WorkflowRecoveryDecision: workflowDecision(step).WorkflowRecoveryDecision, TaskID: source.ID, ExpectedTaskRevision: source.Revision, ExecutionSettled: true, Outcome: TaskSucceeded, Result: json.RawMessage(`"PENDING"`)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -390,13 +406,36 @@ func TestIntegrationWorkflowInstancesSDK(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
continueWorkflowSDK(t, ctx, api, v.ID)
|
||||
finishWorkflowSDK(t, ctx, w, WorkerSucceeded, json.RawMessage(`"DONE"`))
|
||||
last := finishWorkflowSDK(t, ctx, w, WorkerSucceeded, json.RawMessage(`"DONE"`))
|
||||
assertWorkflowSourceSDK(t, ctx, api, v, last.Task.ID, 1, 2, "")
|
||||
assertWorkflowSourceSDK(t, ctx, api, v, source.ID, 1, 1, "")
|
||||
waitWorkflowSDK(t, ctx, api, v.ID, "SUCCEEDED")
|
||||
final, err := api.GetWorkflowStep(ctx, v.ID, "query")
|
||||
if err != nil || final.Poll.UsedRounds != 2 || final.Poll.DeadlineAt != step.Poll.DeadlineAt {
|
||||
t.Fatal("poll budget changed", err)
|
||||
}
|
||||
})
|
||||
t.Run("poll_recovery_source_keeps_business_round", func(t *testing.T) {
|
||||
spec := workflowSDKSpec(queue, handler)
|
||||
task := *spec.Body.Steps[0].Task
|
||||
task.ResultType = WorkflowType{Kind: "STRING", Enum: []string{"PENDING", "DONE"}}
|
||||
spec.Body = WorkflowBlock{Steps: []WorkflowStep{{Key: "query", Kind: "POLL", Poll: &WorkflowPoll{Task: task, MaxRounds: 3, TimeoutMS: 60000, IntervalMS: 1000, Pending: []string{"PENDING"}, Succeeded: []string{"DONE"}}}}, OutputType: task.ResultType, Output: WorkflowRef("query")}
|
||||
v := startWorkflowSDK(t, ctx, api, spec)
|
||||
w := workflowWorkerSDK(t, ctx, api, queue, handler)
|
||||
defer w.Close()
|
||||
old := finishWorkflowSDK(t, ctx, w, WorkerFailed, json.RawMessage(`"failure"`))
|
||||
step := waitWorkflowStepSDK(t, ctx, api, v.ID, "query", func(v WorkflowStepDetail) bool { return v.Step.State == "NEEDS_REVIEW" })
|
||||
child, err := api.RetryWorkflowStep(ctx, v.ID, workerTestID(t), RetryWorkflowStepRequest{WorkflowStepDecision: workflowDecision(step), SafeToRetry: true})
|
||||
if err != nil || child.Task == nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertWorkflowSourceSDK(t, ctx, api, v, child.Task.ID, 2, 1, old.Task.ID)
|
||||
assertWorkflowSourceSDK(t, ctx, api, v, old.Task.ID, 1, 1, "")
|
||||
continueWorkflowSDK(t, ctx, api, v.ID)
|
||||
finishWorkflowSDK(t, ctx, w, WorkerSucceeded, json.RawMessage(`"DONE"`))
|
||||
waitWorkflowSDK(t, ctx, api, v.ID, "SUCCEEDED")
|
||||
assertWorkflowSourceSDK(t, ctx, api, v, child.Task.ID, 2, 1, old.Task.ID)
|
||||
})
|
||||
if !t.Failed() {
|
||||
t.Log("public workflow start/query/control/signal, all six recovery commands, audit and real Worker execution verified without server imports")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user