@@ -973,6 +973,7 @@ var (
973973 recvIdempotencyWf = WithWorkflow (receiveIdempotencyWorkflow )
974974 receiveIdempotencyStartEvent = NewEvent ()
975975 receiveIdempotencyStopEvent = NewEvent ()
976+ sendWithinStepWf = WithWorkflow (workflowThatCallsSendInStep )
976977 numConcurrentRecvWfs = 5
977978 concurrentRecvReadyEvents = make ([]* Event , numConcurrentRecvWfs )
978979 concurrentRecvStartEvent = NewEvent ()
@@ -1063,6 +1064,22 @@ func receiveIdempotencyWorkflow(ctx context.Context, topic string) (string, erro
10631064 return msg , nil
10641065}
10651066
1067+ func stepThatCallsSend (ctx context.Context , input sendWorkflowInput ) (string , error ) {
1068+ err := Send (ctx , WorkflowSendInput {
1069+ DestinationID : input .DestinationID ,
1070+ Topic : input .Topic ,
1071+ Message : "message-from-step" ,
1072+ })
1073+ if err != nil {
1074+ return "" , err
1075+ }
1076+ return "send-completed" , nil
1077+ }
1078+
1079+ func workflowThatCallsSendInStep (ctx context.Context , input sendWorkflowInput ) (string , error ) {
1080+ return RunAsStep (ctx , stepThatCallsSend , input )
1081+ }
1082+
10661083type sendRecvType struct {
10671084 Value string
10681085}
@@ -1185,13 +1202,13 @@ func TestSendRecv(t *testing.T) {
11851202 }
11861203 })
11871204
1188- t .Run ("SendRecvMustRunInsideWorkflows " , func (t * testing.T ) {
1205+ t .Run ("RecvMustRunInsideWorkflows " , func (t * testing.T ) {
11891206 ctx := context .Background ()
11901207
1191- // Attempt to run Send outside of a workflow context
1192- err := Send (ctx , WorkflowSendInput { DestinationID : "test-id" , Topic : "test-topic" , Message : "test-message" })
1208+ // Attempt to run Recv outside of a workflow context
1209+ _ , err := Recv [ string ] (ctx , WorkflowRecvInput { Topic : "test-topic" , Timeout : 1 * time . Second })
11931210 if err == nil {
1194- t .Fatal ("expected error when running Send outside of workflow context, but got none" )
1211+ t .Fatal ("expected error when running Recv outside of workflow context, but got none" )
11951212 }
11961213
11971214 // Check the error type
@@ -1209,26 +1226,35 @@ func TestSendRecv(t *testing.T) {
12091226 if ! strings .Contains (err .Error (), expectedMessagePart ) {
12101227 t .Fatalf ("expected error message to contain %q, but got %q" , expectedMessagePart , err .Error ())
12111228 }
1229+ })
12121230
1213- // Attempt to run Recv outside of a workflow context
1214- _ , err = Recv [string ](ctx , WorkflowRecvInput {Topic : "test-topic" , Timeout : 1 * time .Second })
1215- if err == nil {
1216- t .Fatal ("expected error when running Recv outside of workflow context, but got none" )
1231+ t .Run ("SendOutsideWorkflow" , func (t * testing.T ) {
1232+ // Start a receive workflow to have a valid destination
1233+ receiveHandle , err := receiveWf (context .Background (), "outside-workflow-topic" )
1234+ if err != nil {
1235+ t .Fatalf ("failed to start receive workflow: %v" , err )
12171236 }
12181237
1219- // Check the error type
1220- dbosErr , ok = err .(* DBOSError )
1221- if ! ok {
1222- t .Fatalf ("expected error to be of type *DBOSError, got %T" , err )
1238+ // Send messages from outside a workflow context (should work now)
1239+ ctx := context .Background ()
1240+ for i := range 3 {
1241+ err = Send (ctx , WorkflowSendInput {
1242+ DestinationID : receiveHandle .GetWorkflowID (),
1243+ Topic : "outside-workflow-topic" ,
1244+ Message : fmt .Sprintf ("message%d" , i + 1 ),
1245+ })
1246+ if err != nil {
1247+ t .Fatalf ("failed to send message%d from outside workflow: %v" , i + 1 , err )
1248+ }
12231249 }
12241250
1225- if dbosErr .Code != StepExecutionError {
1226- t .Fatalf ("expected error code to be StepExecutionError, got %v" , dbosErr .Code )
1251+ // Verify the receive workflow gets all messages
1252+ result , err := receiveHandle .GetResult (context .Background ())
1253+ if err != nil {
1254+ t .Fatalf ("failed to get result from receive workflow: %v" , err )
12271255 }
1228-
1229- // Test the specific message from the error
1230- if ! strings .Contains (err .Error (), expectedMessagePart ) {
1231- t .Fatalf ("expected error message to contain %q, but got %q" , expectedMessagePart , err .Error ())
1256+ if result != "message1-message2-message3" {
1257+ t .Fatalf ("expected result to be 'message1-message2-message3', got '%s'" , result )
12321258 }
12331259 })
12341260 t .Run ("SendRecvIdempotency" , func (t * testing.T ) {
@@ -1292,6 +1318,45 @@ func TestSendRecv(t *testing.T) {
12921318 }
12931319 })
12941320
1321+ t .Run ("SendCannotBeCalledWithinStep" , func (t * testing.T ) {
1322+ // Start a receive workflow to have a valid destination
1323+ receiveHandle , err := receiveWf (context .Background (), "send-within-step-topic" )
1324+ if err != nil {
1325+ t .Fatalf ("failed to start receive workflow: %v" , err )
1326+ }
1327+
1328+ // Execute the workflow that tries to call Send within a step
1329+ handle , err := sendWithinStepWf (context .Background (), sendWorkflowInput {
1330+ DestinationID : receiveHandle .GetWorkflowID (),
1331+ Topic : "send-within-step-topic" ,
1332+ })
1333+ if err != nil {
1334+ t .Fatalf ("failed to start workflow: %v" , err )
1335+ }
1336+
1337+ // Expect the workflow to fail with the specific error
1338+ _ , err = handle .GetResult (context .Background ())
1339+ if err == nil {
1340+ t .Fatal ("expected error when calling Send within a step, but got none" )
1341+ }
1342+
1343+ // Check the error type
1344+ dbosErr , ok := err .(* DBOSError )
1345+ if ! ok {
1346+ t .Fatalf ("expected error to be of type *DBOSError, got %T" , err )
1347+ }
1348+
1349+ if dbosErr .Code != StepExecutionError {
1350+ t .Fatalf ("expected error code to be StepExecutionError, got %v" , dbosErr .Code )
1351+ }
1352+
1353+ // Test the specific message from the error
1354+ expectedMessagePart := "cannot call Send within a step"
1355+ if ! strings .Contains (err .Error (), expectedMessagePart ) {
1356+ t .Fatalf ("expected error message to contain %q, but got %q" , expectedMessagePart , err .Error ())
1357+ }
1358+ })
1359+
12951360 t .Run ("ConcurrentRecv" , func (t * testing.T ) {
12961361 // Test concurrent receivers - only 1 should timeout, others should get errors
12971362 receiveTopic := "concurrent-recv-topic"
0 commit comments