feat: expose workflow state events and persist signal transitions
This commit is contained in:
80
sdk/go/workflow_events_integration_test.go
Normal file
80
sdk/go/workflow_events_integration_test.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package orbit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestIntegrationWorkflowEventsSDK(t *testing.T) {
|
||||
api, queue, _ := integrationWorkerClient(t)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
operation := workerTestID(t)
|
||||
spec := workflowSDKSpec(queue, "events."+operation)
|
||||
spec.Body = WorkflowBlock{Steps: []WorkflowStep{
|
||||
{Key: "receipt", Kind: "SIGNAL", Signal: &WorkflowSignal{Name: "done", PayloadVersion: "v1", Correlation: WorkflowValue{Kind: "LITERAL", Literal: json.RawMessage(`"` + operation + `"`)}, PayloadType: spec.InputType, TimeoutMS: 60000}},
|
||||
{Key: "delay", Kind: "TIMER", Timer: &WorkflowTimer{DelayMS: 60000}},
|
||||
}, OutputType: WorkflowType{Kind: "NULL"}, Output: WorkflowRef("delay")}
|
||||
v := startWorkflowSDK(t, ctx, api, spec)
|
||||
sent, err := api.SendWorkflowSignal(ctx, v.ID, operation, SendWorkflowSignalRequest{Name: "done", PayloadVersion: "v1", TargetStepKey: "receipt", TargetActivationSequence: 1, CorrelationKey: operation, Payload: workflowExact, Reason: "verify timeline"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
waitWorkflowStepSDK(t, ctx, api, v.ID, "receipt", func(d WorkflowStepDetail) bool { return d.Step.State == "SUCCEEDED" })
|
||||
current, err := api.GetWorkflow(ctx, v.ID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err = api.ControlWorkflow(ctx, v.ID, operation, WorkflowControlRequest{Operation: "CANCEL", ExpectedControlRevision: current.ControlRevision, Reason: "timeline cleanup"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
waitWorkflowSDK(t, ctx, api, v.ID, "CANCELLED")
|
||||
seen := map[string]bool{}
|
||||
types := map[string]bool{}
|
||||
cursor := ""
|
||||
pages := 0
|
||||
for {
|
||||
page, err := api.ListWorkflowEvents(ctx, v.ID, HistoryPageRequest{Limit: 2, Cursor: cursor})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pages++
|
||||
raw, _ := json.Marshal(page)
|
||||
if strings.Contains(string(raw), `"payload"`) || strings.Contains(string(raw), `9007199254740993`) {
|
||||
t.Fatal("payload in summaries")
|
||||
}
|
||||
for _, e := range page.Items {
|
||||
if seen[e.ID] || e.WorkflowID != v.ID || e.StepKey == "$root" {
|
||||
t.Fatal("event identity", e)
|
||||
}
|
||||
seen[e.ID] = true
|
||||
types[e.Type] = true
|
||||
detail, err := api.GetWorkflowEvent(ctx, v.ID, e.ID)
|
||||
if err != nil || detail.WorkflowEventSummary != e {
|
||||
t.Fatal("detail", detail, err)
|
||||
}
|
||||
if e.AggregateKind == "workflow_signal" && (e.AggregateID != sent.Signal.ID || e.State != "CONSUMED" || e.StateScope != "SIGNAL") {
|
||||
t.Fatal("signal transition", e)
|
||||
}
|
||||
}
|
||||
if page.NextCursor == "" {
|
||||
break
|
||||
}
|
||||
cursor = page.NextCursor
|
||||
if pages > 20 {
|
||||
t.Fatal("unbounded page repetition")
|
||||
}
|
||||
}
|
||||
for _, kind := range []string{"workflow.started", "workflow.step.changed", "workflow.signal.accepted", "workflow.signal.changed", "workflow.controlled", "workflow.stopped"} {
|
||||
if !types[kind] {
|
||||
t.Fatal("event missing", kind, types)
|
||||
}
|
||||
}
|
||||
if len(seen) < 7 || pages < 4 {
|
||||
t.Fatal("incomplete timeline", len(seen), pages)
|
||||
}
|
||||
t.Logf("real HTTP event timeline: %d immutable events over %d pages; signal transition and cancelled workflow retained", len(seen), pages)
|
||||
}
|
||||
Reference in New Issue
Block a user