@@ -213,7 +213,154 @@ func TestAdminServer(t *testing.T) {
213213 }
214214 })
215215
216+ t .Run ("List workflows input/output values" , func (t * testing.T ) {
217+ resetTestDatabase (t , databaseURL )
218+ ctx , err := NewDBOSContext (Config {
219+ DatabaseURL : databaseURL ,
220+ AppName : "test-app" ,
221+ AdminServer : true ,
222+ })
223+ require .NoError (t , err )
224+
225+ // Define a custom struct for testing
226+ type TestStruct struct {
227+ Name string `json:"name"`
228+ Value int `json:"value"`
229+ }
230+
231+ // Test workflow with int input/output
232+ intWorkflow := func (dbosCtx DBOSContext , input int ) (int , error ) {
233+ return input * 2 , nil
234+ }
235+ RegisterWorkflow (ctx , intWorkflow )
236+
237+ // Test workflow with empty string input/output
238+ emptyStringWorkflow := func (dbosCtx DBOSContext , input string ) (string , error ) {
239+ return "" , nil
240+ }
241+ RegisterWorkflow (ctx , emptyStringWorkflow )
242+
243+ // Test workflow with struct input/output
244+ structWorkflow := func (dbosCtx DBOSContext , input TestStruct ) (TestStruct , error ) {
245+ return TestStruct {Name : "output-" + input .Name , Value : input .Value * 2 }, nil
246+ }
247+ RegisterWorkflow (ctx , structWorkflow )
248+
249+ err = ctx .Launch ()
250+ require .NoError (t , err )
251+
252+ // Ensure cleanup
253+ defer func () {
254+ if ctx != nil {
255+ ctx .Cancel ()
256+ }
257+ }()
258+
259+ // Give the server a moment to start
260+ time .Sleep (100 * time .Millisecond )
261+
262+ client := & http.Client {Timeout : 5 * time .Second }
263+ endpoint := fmt .Sprintf ("http://localhost:3001/%s" , strings .TrimPrefix (_WORKFLOWS_PATTERN , "POST /" ))
264+
265+ // Create workflows with different input/output types
266+ // 1. Integer workflow
267+ intHandle , err := RunAsWorkflow (ctx , intWorkflow , 42 )
268+ require .NoError (t , err , "Failed to create int workflow" )
269+ intResult , err := intHandle .GetResult ()
270+ require .NoError (t , err , "Failed to get int workflow result" )
271+ assert .Equal (t , 84 , intResult )
272+
273+ // 2. Empty string workflow
274+ emptyStringHandle , err := RunAsWorkflow (ctx , emptyStringWorkflow , "" )
275+ require .NoError (t , err , "Failed to create empty string workflow" )
276+ emptyStringResult , err := emptyStringHandle .GetResult ()
277+ require .NoError (t , err , "Failed to get empty string workflow result" )
278+ assert .Equal (t , "" , emptyStringResult )
279+
280+ // 3. Struct workflow
281+ structInput := TestStruct {Name : "test" , Value : 10 }
282+ structHandle , err := RunAsWorkflow (ctx , structWorkflow , structInput )
283+ require .NoError (t , err , "Failed to create struct workflow" )
284+ structResult , err := structHandle .GetResult ()
285+ require .NoError (t , err , "Failed to get struct workflow result" )
286+ assert .Equal (t , TestStruct {Name : "output-test" , Value : 20 }, structResult )
287+
288+ // Query workflows with input/output loading enabled
289+ // Filter by the workflow IDs we just created to avoid interference from other tests
290+ reqBody := map [string ]any {
291+ "workflow_uuids" : []string {
292+ intHandle .GetWorkflowID (),
293+ emptyStringHandle .GetWorkflowID (),
294+ structHandle .GetWorkflowID (),
295+ },
296+ "load_input" : true ,
297+ "load_output" : true ,
298+ "limit" : 10 ,
299+ }
300+ req , err := http .NewRequest (http .MethodPost , endpoint , bytes .NewBuffer (mustMarshal (reqBody )))
301+ require .NoError (t , err , "Failed to create request" )
302+ req .Header .Set ("Content-Type" , "application/json" )
303+
304+ resp , err := client .Do (req )
305+ require .NoError (t , err , "Failed to make request" )
306+ defer resp .Body .Close ()
307+
308+ assert .Equal (t , http .StatusOK , resp .StatusCode )
309+
310+ var workflows []map [string ]any
311+ err = json .NewDecoder (resp .Body ).Decode (& workflows )
312+ require .NoError (t , err , "Failed to decode workflows response" )
313+
314+ // Should have exactly 3 workflows
315+ assert .Equal (t , 3 , len (workflows ), "Expected exactly 3 workflows" )
316+
317+ // Verify each workflow's input/output marshaling
318+ for _ , wf := range workflows {
319+ wfID := wf ["WorkflowUUID" ].(string )
320+
321+ // Check input and output fields exist and are strings (JSON marshaled)
322+ if wfID == intHandle .GetWorkflowID () {
323+ // Integer workflow: input and output should be marshaled as JSON strings
324+ inputStr , ok := wf ["Input" ].(string )
325+ require .True (t , ok , "Int workflow Input should be a string" )
326+ assert .Equal (t , "42" , inputStr , "Int workflow input should be marshaled as '42'" )
327+
328+ outputStr , ok := wf ["Output" ].(string )
329+ require .True (t , ok , "Int workflow Output should be a string" )
330+ assert .Equal (t , "84" , outputStr , "Int workflow output should be marshaled as '84'" )
331+
332+ } else if wfID == emptyStringHandle .GetWorkflowID () {
333+ // Empty string workflow: both input and output are empty strings
334+ // According to the logic, empty strings should not have Input/Output fields
335+ input , hasInput := wf ["Input" ]
336+ require .Equal (t , "" , input )
337+ require .True (t , hasInput , "Empty string workflow should have Input field" )
338+
339+ output , hasOutput := wf ["Output" ]
340+ require .True (t , hasOutput , "Empty string workflow should have Output field" )
341+ require .Equal (t , "" , output )
342+
343+ } else if wfID == structHandle .GetWorkflowID () {
344+ // Struct workflow: input and output should be marshaled as JSON strings
345+ inputStr , ok := wf ["Input" ].(string )
346+ require .True (t , ok , "Struct workflow Input should be a string" )
347+ var inputStruct TestStruct
348+ err = json .Unmarshal ([]byte (inputStr ), & inputStruct )
349+ require .NoError (t , err , "Failed to unmarshal struct workflow input" )
350+ assert .Equal (t , structInput , inputStruct , "Struct workflow input should match" )
351+
352+ outputStr , ok := wf ["Output" ].(string )
353+ require .True (t , ok , "Struct workflow Output should be a string" )
354+ var outputStruct TestStruct
355+ err = json .Unmarshal ([]byte (outputStr ), & outputStruct )
356+ require .NoError (t , err , "Failed to unmarshal struct workflow output" )
357+ assert .Equal (t , TestStruct {Name : "output-test" , Value : 20 }, outputStruct , "Struct workflow output should match" )
358+ }
359+ }
360+ })
361+
216362 t .Run ("List endpoints time filtering" , func (t * testing.T ) {
363+ resetTestDatabase (t , databaseURL )
217364 ctx , err := NewDBOSContext (Config {
218365 DatabaseURL : databaseURL ,
219366 AppName : "test-app" ,
@@ -308,7 +455,6 @@ func TestAdminServer(t *testing.T) {
308455 "start_time" : timeBetween .Format (time .RFC3339Nano ),
309456 "limit" : 10 ,
310457 }
311- fmt .Println ("Request body 2:" , reqBody2 , "timebetween" , timeBetween .UnixMilli ())
312458 req2 , err := http .NewRequest (http .MethodPost , endpoint , bytes .NewBuffer (mustMarshal (reqBody2 )))
313459 require .NoError (t , err , "Failed to create request 2" )
314460 req2 .Header .Set ("Content-Type" , "application/json" )
@@ -322,7 +468,6 @@ func TestAdminServer(t *testing.T) {
322468 var workflows2 []map [string ]any
323469 err = json .NewDecoder (resp2 .Body ).Decode (& workflows2 )
324470 require .NoError (t , err , "Failed to decode workflows response 2" )
325- fmt .Println (workflows2 )
326471
327472 // Should have exactly 1 workflow (the second one)
328473 assert .Equal (t , 1 , len (workflows2 ), "Expected exactly 1 workflow with start_time after timeBetween" )
0 commit comments