@@ -10,6 +10,8 @@ import (
1010 "fmt"
1111 "net"
1212 "net/http"
13+ "os"
14+ "path/filepath"
1315 "regexp"
1416 "strings"
1517 "testing"
@@ -29,10 +31,12 @@ import (
2931 "google.golang.org/protobuf/types/known/durationpb"
3032
3133 "go.temporal.io/api/enums/v1"
34+ historypb "go.temporal.io/api/history/v1"
3235 "go.temporal.io/api/operatorservice/v1"
3336 "go.temporal.io/api/serviceerror"
3437 "go.temporal.io/api/workflowservice/v1"
3538 "go.temporal.io/sdk/client"
39+ "google.golang.org/protobuf/encoding/protojson"
3640)
3741
3842const (
@@ -428,6 +432,53 @@ system.forceSearchAttributesCacheRefreshOnRead:
428432 infra .t .Log ("Temporal client connected" )
429433}
430434
435+ // SaveWorkflowHistory fetches the complete event history for a completed workflow
436+ // execution and writes it as a protojson file at destPath. The file can later be
437+ // loaded by a WorkflowReplayer replay test to guard against non-determinism.
438+ //
439+ // Typical usage (at the end of an integration test, after the workflow has finished):
440+ //
441+ // infra.SaveWorkflowHistory(t, ctx, workflowID, "", "testdata/my-case/history.json")
442+ func (infra * TestInfrastructure ) SaveWorkflowHistory (t * testing.T , ctx context.Context , workflowID , runID , destPath string ) {
443+ t .Helper ()
444+
445+ iter := infra .TemporalClient .GetWorkflowHistory (ctx , workflowID , runID , false , enums .HISTORY_EVENT_FILTER_TYPE_ALL_EVENT )
446+
447+ var events []* historypb.HistoryEvent
448+ for iter .HasNext () {
449+ ev , err := iter .Next ()
450+ if err != nil {
451+ t .Logf ("Warning: error reading history event for %s: %v; skipping history save" , workflowID , err )
452+ return
453+ }
454+ events = append (events , ev )
455+ }
456+
457+ if len (events ) == 0 {
458+ t .Logf ("Warning: no history events found for workflow %s — skipping history save" , workflowID )
459+ return
460+ }
461+
462+ history := & historypb.History {Events : events }
463+ jsonBytes , err := protojson.MarshalOptions {Multiline : true }.Marshal (history )
464+ if err != nil {
465+ t .Logf ("Warning: failed to marshal history for %s: %v" , workflowID , err )
466+ return
467+ }
468+
469+ if err := os .MkdirAll (filepath .Dir (destPath ), 0o755 ); err != nil {
470+ t .Logf ("Warning: failed to create history directory %s: %v" , filepath .Dir (destPath ), err )
471+ return
472+ }
473+
474+ if err := os .WriteFile (destPath , jsonBytes , 0o600 ); err != nil {
475+ t .Logf ("Warning: failed to write history file %s: %v" , destPath , err )
476+ return
477+ }
478+
479+ t .Logf ("Saved workflow history (%d events) → %s" , len (events ), destPath )
480+ }
481+
431482// RegisterCleanup adds a cleanup callback that will be called before container teardown.
432483// Use this to gracefully shutdown Temporal workers and other resources.
433484func (infra * TestInfrastructure ) RegisterCleanup (cleanup func ()) {
0 commit comments