@@ -2,8 +2,10 @@ package dbos
22
33import (
44 "context"
5+ "encoding/gob"
56 "errors"
67 "fmt"
8+ "strings"
79 "testing"
810 "time"
911
@@ -44,6 +46,11 @@ type SimpleStruct struct {
4446 B int
4547}
4648
49+ type PointerResultStruct struct {
50+ Value string
51+ Count int
52+ }
53+
4754func encodingWorkflowStruct (ctx DBOSContext , input WorkflowInputStruct ) (StepOutputStruct , error ) {
4855 return RunAsStep (ctx , func (context context.Context ) (StepOutputStruct , error ) {
4956 return encodingStepStruct (context , StepInputStruct {
@@ -60,12 +67,76 @@ func encodingStepStruct(_ context.Context, input StepInputStruct) (StepOutputStr
6067 }, nil
6168}
6269
70+ // Test nil pointer is ignored during encode
71+ func encodingWorkflowNilReturn (ctx DBOSContext , shouldReturnNil bool ) (string , error ) {
72+ pointerResult , err := RunAsStep (ctx , func (context context.Context ) (* PointerResultStruct , error ) {
73+ if shouldReturnNil {
74+ return nil , nil
75+ }
76+ return & PointerResultStruct {
77+ Value : "pointer result" ,
78+ Count : 42 ,
79+ }, nil
80+ })
81+ if err != nil {
82+ return "" , fmt .Errorf ("pointer step failed: %w" , err )
83+ }
84+ // Build result summary
85+ var summary []string
86+ if shouldReturnNil {
87+ summary = append (summary , "All nil types handled successfully" )
88+ } else {
89+ if pointerResult != nil {
90+ summary = append (summary , fmt .Sprintf ("ptr:%s" , pointerResult .Value ))
91+ }
92+ }
93+ return strings .Join (summary , ", " ), nil
94+ }
95+
96+ // Interface types for testing manual interface registration
97+ type ResponseInterface interface {
98+ GetMessage () string
99+ GetCode () int
100+ }
101+
102+ type ConcreteResponse struct {
103+ Message string
104+ Code int
105+ }
106+
107+ func (c ConcreteResponse ) GetMessage () string {
108+ return c .Message
109+ }
110+
111+ func (c ConcreteResponse ) GetCode () int {
112+ return c .Code
113+ }
114+
115+ func encodingWorkflowInterface (ctx DBOSContext , input string ) (ResponseInterface , error ) {
116+ result , err := RunAsStep (ctx , func (context context.Context ) (ResponseInterface , error ) {
117+ return encodingStepInterface (context , input )
118+ })
119+ if err != nil {
120+ return nil , fmt .Errorf ("interface step failed: %w" , err )
121+ }
122+ return result , nil
123+ }
124+
125+ func encodingStepInterface (_ context.Context , input string ) (ResponseInterface , error ) {
126+ return ConcreteResponse {
127+ Message : fmt .Sprintf ("Processed: %s" , input ),
128+ Code : 200 ,
129+ }, nil
130+ }
131+
63132func TestWorkflowEncoding (t * testing.T ) {
64133 executor := setupDBOS (t , true , true )
65134
66135 // Register workflows with executor
67136 RegisterWorkflow (executor , encodingWorkflowBuiltinTypes )
68137 RegisterWorkflow (executor , encodingWorkflowStruct )
138+ RegisterWorkflow (executor , encodingWorkflowNilReturn )
139+ RegisterWorkflow (executor , encodingWorkflowInterface )
69140
70141 err := Launch (executor )
71142 require .NoError (t , err )
@@ -188,6 +259,109 @@ func TestWorkflowEncoding(t *testing.T) {
188259 assert .Equal (t , "processed by encodingStepStruct" , stepOutput .B )
189260 assert .Nil (t , step .Error )
190261 })
262+
263+ t .Run ("NilableTypes" , func (t * testing.T ) {
264+ // Test with non-nil values for all types
265+ directHandle , err := RunWorkflow (executor , encodingWorkflowNilReturn , false )
266+ require .NoError (t , err )
267+
268+ // Test result from direct handle
269+ directResult , err := directHandle .GetResult ()
270+ require .NoError (t , err )
271+ require .NotNil (t , directResult )
272+ // Verify that we got results for all types
273+ assert .Contains (t , directResult , "ptr:pointer result" )
274+
275+ // Test result from polling handle
276+ retrieveHandler , err := RetrieveWorkflow [string ](executor .(* dbosContext ), directHandle .GetWorkflowID ())
277+ require .NoError (t , err )
278+ retrievedResult , err := retrieveHandler .GetResult ()
279+ require .NoError (t , err )
280+ assert .Contains (t , retrievedResult , "ptr:pointer result" )
281+
282+ // Test with nil values for all types
283+ nilHandle , err := RunWorkflow (executor , encodingWorkflowNilReturn , true )
284+ require .NoError (t , err )
285+
286+ // Test nil result from direct handle
287+ nilDirectResult , err := nilHandle .GetResult ()
288+ require .NoError (t , err )
289+ assert .Equal (t , "All nil types handled successfully" , nilDirectResult )
290+
291+ // Test nil result from polling handle
292+ nilRetrieveHandler , err := RetrieveWorkflow [string ](executor .(* dbosContext ), nilHandle .GetWorkflowID ())
293+ require .NoError (t , err )
294+ nilRetrievedResult , err := nilRetrieveHandler .GetResult ()
295+ require .NoError (t , err )
296+ assert .Equal (t , "All nil types handled successfully" , nilRetrievedResult )
297+
298+ // Test results from GetWorkflowSteps to ensure all steps executed
299+ steps , err := GetWorkflowSteps (executor , directHandle .GetWorkflowID ())
300+ require .NoError (t , err )
301+ assert .Equal (t , 1 , len (steps ), "Expected 1 step for nil-able types" )
302+ for _ , step := range steps {
303+ assert .Nil (t , step .Error , "No step should have errors" )
304+ }
305+ })
306+
307+ t .Run ("ManualInterfaceRegistration" , func (t * testing.T ) {
308+ // Manually register the concrete type for interface testing
309+ gob .Register (ConcreteResponse {})
310+
311+ // Test a workflow that returns an interface with manually registered concrete type
312+ directHandle , err := RunWorkflow (executor , encodingWorkflowInterface , "test-interface" )
313+ require .NoError (t , err )
314+
315+ // Test result from direct handle
316+ directResult , err := directHandle .GetResult ()
317+ require .NoError (t , err )
318+ require .NotNil (t , directResult )
319+ assert .Equal (t , "Processed: test-interface" , directResult .GetMessage ())
320+ assert .Equal (t , 200 , directResult .GetCode ())
321+
322+ // Test result from polling handle
323+ retrieveHandler , err := RetrieveWorkflow [ResponseInterface ](executor .(* dbosContext ), directHandle .GetWorkflowID ())
324+ require .NoError (t , err )
325+ retrievedResult , err := retrieveHandler .GetResult ()
326+ require .NoError (t , err )
327+ require .NotNil (t , retrievedResult )
328+ assert .Equal (t , "Processed: test-interface" , retrievedResult .GetMessage ())
329+ assert .Equal (t , 200 , retrievedResult .GetCode ())
330+
331+ // Test results from ListWorkflows
332+ workflows , err := ListWorkflows (
333+ executor ,
334+ WithWorkflowIDs ([]string {directHandle .GetWorkflowID ()}),
335+ WithLoadInput (true ),
336+ WithLoadOutput (true ),
337+ )
338+ require .NoError (t , err )
339+ require .Len (t , workflows , 1 )
340+ workflow := workflows [0 ]
341+ require .NotNil (t , workflow .Input )
342+ workflowInput , ok := workflow .Input .(string )
343+ require .True (t , ok , "expected workflow input to be of type string, got %T" , workflow .Input )
344+ assert .Equal (t , "test-interface" , workflowInput )
345+ require .NotNil (t , workflow .Output )
346+ // The output should be deserialized as ConcreteResponse since we registered it
347+ workflowOutput , ok := workflow .Output .(ConcreteResponse )
348+ require .True (t , ok , "expected workflow output to be of type ConcreteResponse, got %T" , workflow .Output )
349+ assert .Equal (t , "Processed: test-interface" , workflowOutput .Message )
350+ assert .Equal (t , 200 , workflowOutput .Code )
351+
352+ // Test results from GetWorkflowSteps
353+ steps , err := GetWorkflowSteps (executor , directHandle .GetWorkflowID ())
354+ require .NoError (t , err )
355+ require .Len (t , steps , 1 )
356+ step := steps [0 ]
357+ require .NotNil (t , step .Output )
358+ // The step output should also be ConcreteResponse
359+ stepOutput , ok := step .Output .(ConcreteResponse )
360+ require .True (t , ok , "expected step output to be of type ConcreteResponse, got %T" , step .Output )
361+ assert .Equal (t , "Processed: test-interface" , stepOutput .Message )
362+ assert .Equal (t , 200 , stepOutput .Code )
363+ assert .Nil (t , step .Error )
364+ })
191365}
192366
193367type UserDefinedEventData struct {
0 commit comments