@@ -371,6 +371,26 @@ type TestWorkflowData struct {
371371 Metadata map [string ]string
372372}
373373
374+ // Interface for testing interface-typed workflows
375+ type DataProvider interface {
376+ GetMessage () string
377+ GetValue () int
378+ }
379+
380+ // Concrete implementation of DataProvider
381+ type ConcreteDataProvider struct {
382+ Message string
383+ Value int
384+ }
385+
386+ func (c ConcreteDataProvider ) GetMessage () string {
387+ return c .Message
388+ }
389+
390+ func (c ConcreteDataProvider ) GetValue () int {
391+ return c .Value
392+ }
393+
374394// Test workflows and steps
375395func serializerTestStep (_ context.Context , input TestWorkflowData ) (TestWorkflowData , error ) {
376396 return input , nil
@@ -397,6 +417,12 @@ func serializerAnyValueWorkflow(ctx DBOSContext, input any) (any, error) {
397417 })
398418}
399419
420+ func serializerInterfaceValueWorkflow (ctx DBOSContext , input DataProvider ) (DataProvider , error ) {
421+ return RunAsStep (ctx , func (context context.Context ) (DataProvider , error ) {
422+ return input , nil
423+ })
424+ }
425+
400426func serializerErrorStep (_ context.Context , _ TestWorkflowData ) (TestWorkflowData , error ) {
401427 return TestWorkflowData {}, fmt .Errorf ("step error" )
402428}
@@ -546,6 +572,7 @@ func TestSerializer(t *testing.T) {
546572 RegisterWorkflow (executor , serializerSetEventWorkflow )
547573 RegisterWorkflow (executor , serializerGetEventWorkflow )
548574 RegisterWorkflow (executor , serializerRecoveryWorkflow )
575+ RegisterWorkflow (executor , serializerInterfaceValueWorkflow )
549576 if serializerName == "JSON" {
550577 // Cannot register "any" workflow with Gob serializer
551578 RegisterWorkflow (executor , serializerAnyValueWorkflow )
@@ -598,6 +625,68 @@ func TestSerializer(t *testing.T) {
598625 testComprehensiveWorkflowValues (t , executor , handle , input )
599626 })
600627
628+ // Test workflow with interface type
629+ t .Run ("ComprehensiveInterfaceValues" , func (t * testing.T ) {
630+ input := ConcreteDataProvider {
631+ Message : "interface test message" ,
632+ Value : 123 ,
633+ }
634+
635+ handle , err := RunWorkflow (executor , serializerInterfaceValueWorkflow , DataProvider (input ))
636+ require .NoError (t , err , "Interface workflow execution failed" )
637+
638+ // Get the result
639+ result , err := handle .GetResult ()
640+ require .NoError (t , err , "Failed to get workflow result" )
641+
642+ // For interface types, we need to check the concrete type
643+ concreteResult , ok := result .(ConcreteDataProvider )
644+ require .True (t , ok , "Result should be ConcreteDataProvider type" )
645+ assert .Equal (t , input .Message , concreteResult .Message , "Message should match" )
646+ assert .Equal (t , input .Value , concreteResult .Value , "Value should match" )
647+
648+ // Test with ListWorkflows
649+ workflows , err := ListWorkflows (executor ,
650+ WithWorkflowIDs ([]string {handle .GetWorkflowID ()}),
651+ WithLoadInput (true ),
652+ WithLoadOutput (true ),
653+ )
654+ require .NoError (t , err , "Failed to list workflows" )
655+ require .Len (t , workflows , 1 , "Expected 1 workflow" )
656+
657+ workflow := workflows [0 ]
658+ require .NotNil (t , workflow .Input , "Workflow input should not be nil" )
659+ require .NotNil (t , workflow .Output , "Workflow output should not be nil" )
660+
661+ // For Gob serializer, the concrete type is preserved
662+ // For JSON serializer, we get a map that needs conversion
663+ dbosCtx , ok := executor .(* dbosContext )
664+ require .True (t , ok , "expected dbosContext" )
665+ isJSON := isJSONSerializer (dbosCtx .serializer )
666+
667+ if isJSON {
668+ // JSON serializer returns map[string]any
669+ inputMap , ok := workflow .Input .(map [string ]any )
670+ require .True (t , ok , "Input should be map[string]any for JSON" )
671+ assert .Equal (t , input .Message , inputMap ["Message" ], "Message should match in input" )
672+ assert .Equal (t , float64 (input .Value ), inputMap ["Value" ], "Value should match in input" )
673+
674+ outputMap , ok := workflow .Output .(map [string ]any )
675+ require .True (t , ok , "Output should be map[string]any for JSON" )
676+ assert .Equal (t , input .Message , outputMap ["Message" ], "Message should match in output" )
677+ assert .Equal (t , float64 (input .Value ), outputMap ["Value" ], "Value should match in output" )
678+ } else {
679+ // Gob serializer preserves the concrete type
680+ inputConcrete , ok := workflow .Input .(ConcreteDataProvider )
681+ require .True (t , ok , "Input should be ConcreteDataProvider for Gob" )
682+ assert .Equal (t , input , inputConcrete , "Input should match" )
683+
684+ outputConcrete , ok := workflow .Output .(ConcreteDataProvider )
685+ require .True (t , ok , "Output should be ConcreteDataProvider for Gob" )
686+ assert .Equal (t , input , outputConcrete , "Output should match" )
687+ }
688+ })
689+
601690 // Test nil values with pointer type workflow
602691 t .Run ("NilValuesPointer" , func (t * testing.T ) {
603692 handle , err := RunWorkflow (executor , serializerNilValueWorkflow , (* TestWorkflowData )(nil ))
0 commit comments