From 628ef0035fe20d81ea0f04c4ce168e27032ec1ca Mon Sep 17 00:00:00 2001 From: maxdml Date: Wed, 13 Aug 2025 12:20:29 -0700 Subject: [PATCH 01/10] move to testify for testing --- dbos/admin_server_test.go | 90 ++---- dbos/client_test.go | 502 +++++++++------------------------ dbos/dbos_test.go | 55 ++-- dbos/logger_test.go | 40 +-- dbos/queues_test.go | 548 ++++++++++--------------------------- dbos/serialization_test.go | 304 ++++++-------------- dbos/utils_test.go | 21 +- dbos/workflows_test.go | 253 ++++++----------- go.mod | 7 +- go.sum | 8 + 10 files changed, 507 insertions(+), 1321 deletions(-) diff --git a/dbos/admin_server_test.go b/dbos/admin_server_test.go index 11765de3..2ec425d9 100644 --- a/dbos/admin_server_test.go +++ b/dbos/admin_server_test.go @@ -8,6 +8,9 @@ import ( "strings" "testing" "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestAdminServer(t *testing.T) { @@ -19,13 +22,9 @@ func TestAdminServer(t *testing.T) { DatabaseURL: databaseURL, AppName: "test-app", }) - if err != nil { - t.Skipf("Failed to initialize DBOS: %v", err) - } + require.NoError(t, err) err = ctx.Launch() - if err != nil { - t.Skipf("Failed to initialize DBOS: %v", err) - } + require.NoError(t, err) // Ensure cleanup defer func() { @@ -40,19 +39,13 @@ func TestAdminServer(t *testing.T) { // Verify admin server is not running client := &http.Client{Timeout: 1 * time.Second} _, err = client.Get("http://localhost:3001" + healthCheckPath) - if err == nil { - t.Error("Expected request to fail when admin server is not started, but it succeeded") - } + require.Error(t, err, "Expected request to fail when admin server is not started") // Verify the DBOS executor doesn't have an admin server instance - if ctx == nil { - t.Fatal("Expected DBOS instance to be created") - } + require.NotNil(t, ctx, "Expected DBOS instance to be created") exec := ctx.(*dbosContext) - if exec.adminServer != nil { - t.Error("Expected admin server to be nil when not configured") - } + require.Nil(t, exec.adminServer, "Expected admin server to be nil when not configured") }) t.Run("Admin server endpoints", func(t *testing.T) { @@ -62,13 +55,9 @@ func TestAdminServer(t *testing.T) { AppName: "test-app", AdminServer: true, }) - if err != nil { - t.Skipf("Failed to initialize DBOS with admin server: %v", err) - } + require.NoError(t, err) err = ctx.Launch() - if err != nil { - t.Skipf("Failed to initialize DBOS with admin server: %v", err) - } + require.NoError(t, err) // Ensure cleanup defer func() { @@ -81,14 +70,10 @@ func TestAdminServer(t *testing.T) { time.Sleep(100 * time.Millisecond) // Verify the DBOS executor has an admin server instance - if ctx == nil { - t.Fatal("Expected DBOS instance to be created") - } + require.NotNil(t, ctx, "Expected DBOS instance to be created") exec := ctx.(*dbosContext) - if exec.adminServer == nil { - t.Fatal("Expected admin server to be created in DBOS instance") - } + require.NotNil(t, exec.adminServer, "Expected admin server to be created in DBOS instance") client := &http.Client{Timeout: 5 * time.Second} @@ -116,12 +101,9 @@ func TestAdminServer(t *testing.T) { expectedStatus: http.StatusOK, validateResp: func(t *testing.T, resp *http.Response) { var workflowIDs []string - if err := json.NewDecoder(resp.Body).Decode(&workflowIDs); err != nil { - t.Errorf("Failed to decode response as JSON array: %v", err) - } - if workflowIDs == nil { - t.Error("Expected non-nil workflow IDs array") - } + err := json.NewDecoder(resp.Body).Decode(&workflowIDs) + require.NoError(t, err, "Failed to decode response as JSON array") + assert.NotNil(t, workflowIDs, "Expected non-nil workflow IDs array") }, }, { @@ -145,36 +127,23 @@ func TestAdminServer(t *testing.T) { expectedStatus: http.StatusOK, validateResp: func(t *testing.T, resp *http.Response) { var queueMetadata []WorkflowQueue - if err := json.NewDecoder(resp.Body).Decode(&queueMetadata); err != nil { - t.Errorf("Failed to decode response as QueueMetadata array: %v", err) - } - if queueMetadata == nil { - t.Error("Expected non-nil queue metadata array") - } + err := json.NewDecoder(resp.Body).Decode(&queueMetadata) + require.NoError(t, err, "Failed to decode response as QueueMetadata array") + assert.NotNil(t, queueMetadata, "Expected non-nil queue metadata array") // Should contain at least the internal queue - if len(queueMetadata) == 0 { - t.Error("Expected at least one queue in metadata") - } + assert.Greater(t, len(queueMetadata), 0, "Expected at least one queue in metadata") // Verify internal queue fields foundInternalQueue := false for _, queue := range queueMetadata { if queue.Name == _DBOS_INTERNAL_QUEUE_NAME { // Internal queue name foundInternalQueue = true - if queue.GlobalConcurrency != nil { - t.Errorf("Expected internal queue to have no concurrency limit, but got %v", *queue.GlobalConcurrency) - } - if queue.WorkerConcurrency != nil { - t.Errorf("Expected internal queue to have no worker concurrency limit, but got %v", *queue.WorkerConcurrency) - } - if queue.RateLimit != nil { - t.Error("Expected internal queue to have no rate limit") - } + assert.Nil(t, queue.GlobalConcurrency, "Expected internal queue to have no concurrency limit") + assert.Nil(t, queue.WorkerConcurrency, "Expected internal queue to have no worker concurrency limit") + assert.Nil(t, queue.RateLimit, "Expected internal queue to have no rate limit") break } } - if !foundInternalQueue { - t.Error("Expected to find internal queue in metadata") - } + assert.True(t, foundInternalQueue, "Expected to find internal queue in metadata") }, }, { @@ -195,24 +164,17 @@ func TestAdminServer(t *testing.T) { } else { req, err = http.NewRequest(tt.method, tt.endpoint, nil) } - if err != nil { - t.Fatalf("Failed to create request: %v", err) - } + require.NoError(t, err, "Failed to create request") if tt.contentType != "" { req.Header.Set("Content-Type", tt.contentType) } resp, err := client.Do(req) - if err != nil { - t.Fatalf("Failed to make request: %v", err) - } + require.NoError(t, err, "Failed to make request") defer resp.Body.Close() - if resp.StatusCode != tt.expectedStatus { - body, _ := io.ReadAll(resp.Body) - t.Errorf("Expected status code %d, got %d. Response: %s", tt.expectedStatus, resp.StatusCode, string(body)) - } + assert.Equal(t, tt.expectedStatus, resp.StatusCode) if tt.validateResp != nil { tt.validateResp(t, resp) diff --git a/dbos/client_test.go b/dbos/client_test.go index 6741492a..c2cbd4c7 100644 --- a/dbos/client_test.go +++ b/dbos/client_test.go @@ -6,6 +6,9 @@ import ( "strings" "testing" "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestEnqueue(t *testing.T) { @@ -40,9 +43,7 @@ func TestEnqueue(t *testing.T) { // Launch the server context to start processing tasks err := serverCtx.Launch() - if err != nil { - t.Fatalf("failed to launch server DBOS instance: %v", err) - } + require.NoError(t, err) // Setup client context - this will enqueue tasks clientCtx := setupDBOS(t, false, false) // Don't drop DB, don't check for leaks @@ -55,48 +56,28 @@ func TestEnqueue(t *testing.T) { WorkflowInput: wfInput{Input: "test-input"}, ApplicationVersion: serverCtx.GetApplicationVersion(), }) - if err != nil { - t.Fatalf("failed to enqueue workflow from client: %v", err) - } + require.NoError(t, err) // Verify we got a polling handle _, ok := handle.(*workflowPollingHandle[string]) - if !ok { - t.Fatalf("expected handle to be of type workflowPollingHandle, got %T", handle) - } + require.True(t, ok, "expected handle to be of type workflowPollingHandle, got %T", handle) // Client retrieves the result result, err := handle.GetResult() - if err != nil { - t.Fatalf("failed to get result from enqueued workflow: %v", err) - } + require.NoError(t, err) expectedResult := "processed: test-input" - if result != expectedResult { - t.Fatalf("expected result to be '%s', got '%s'", expectedResult, result) - } + assert.Equal(t, expectedResult, result) // Verify the workflow status status, err := handle.GetStatus() - if err != nil { - t.Fatalf("failed to get workflow status: %v", err) - } + require.NoError(t, err) - if status.Status != WorkflowStatusSuccess { - t.Fatalf("expected workflow status to be SUCCESS, got %v", status.Status) - } + assert.Equal(t, WorkflowStatusSuccess, status.Status) + assert.Equal(t, "ServerWorkflow", status.Name) + assert.Equal(t, queue.Name, status.QueueName) - if status.Name != "ServerWorkflow" { - t.Fatalf("expected workflow name to be 'ServerWorkflow', got '%s'", status.Name) - } - - if status.QueueName != queue.Name { - t.Fatalf("expected queue name to be '%s', got '%s'", queue.Name, status.QueueName) - } - - if !queueEntriesAreCleanedUp(serverCtx) { - t.Fatal("expected queue entries to be cleaned up after global concurrency test") - } + assert.True(t, queueEntriesAreCleanedUp(serverCtx), "expected queue entries to be cleaned up after global concurrency test") }) t.Run("EnqueueWithCustomWorkflowID", func(t *testing.T) { @@ -109,28 +90,18 @@ func TestEnqueue(t *testing.T) { WorkflowID: customWorkflowID, WorkflowInput: wfInput{Input: "test-input"}, }) - if err != nil { - t.Fatalf("failed to enqueue workflow with custom ID: %v", err) - } + require.NoError(t, err) // Verify the workflow ID is what we set retrieveHandle, err := RetrieveWorkflow[string](clientCtx, customWorkflowID) - if err != nil { - t.Fatalf("failed to retrieve workflow by custom ID: %v", err) - } + require.NoError(t, err) result, err := retrieveHandle.GetResult() - if err != nil { - t.Fatalf("failed to get result from retrieved workflow: %v", err) - } + require.NoError(t, err) - if result != "processed: test-input" { - t.Fatalf("expected retrieved workflow result to be 'processed: test-input', got '%s'", result) - } + assert.Equal(t, "processed: test-input", result) - if !queueEntriesAreCleanedUp(serverCtx) { - t.Fatal("expected queue entries to be cleaned up after global concurrency test") - } + assert.True(t, queueEntriesAreCleanedUp(serverCtx), "expected queue entries to be cleaned up after global concurrency test") }) t.Run("EnqueueWithTimeout", func(t *testing.T) { @@ -140,40 +111,26 @@ func TestEnqueue(t *testing.T) { WorkflowInput: "blocking-input", WorkflowTimeout: 500 * time.Millisecond, }) - if err != nil { - t.Fatalf("failed to enqueue blocking workflow: %v", err) - } + require.NoError(t, err) // Should timeout when trying to get result _, err = handle.GetResult() - if err == nil { - t.Fatal("expected timeout error, but got none") - } + require.Error(t, err, "expected timeout error, but got none") dbosErr, ok := err.(*DBOSError) - if !ok { - t.Fatalf("expected error to be of type *DBOSError, got %T", err) - } + require.True(t, ok, "expected error to be of type *DBOSError, got %T", err) - if dbosErr.Code != AwaitedWorkflowCancelled { - t.Fatalf("expected error code to be AwaitedWorkflowCancelled, got %v", dbosErr.Code) - } + assert.Equal(t, AwaitedWorkflowCancelled, dbosErr.Code) // Verify workflow is cancelled status, err := handle.GetStatus() - if err != nil { - t.Fatalf("failed to get workflow status: %v", err) - } + require.NoError(t, err) - if status.Status != WorkflowStatusCancelled { - t.Fatalf("expected workflow status to be CANCELLED, got %v", status.Status) - } + assert.Equal(t, WorkflowStatusCancelled, status.Status) }) // Verify all queue entries are cleaned up - if !queueEntriesAreCleanedUp(serverCtx) { - t.Fatal("expected queue entries to be cleaned up after client tests") - } + require.True(t, queueEntriesAreCleanedUp(serverCtx), "expected queue entries to be cleaned up after client tests") } func TestCancelResume(t *testing.T) { @@ -235,9 +192,7 @@ func TestCancelResume(t *testing.T) { // Launch the server context to start processing tasks err := serverCtx.Launch() - if err != nil { - t.Fatalf("failed to launch server DBOS instance: %v", err) - } + require.NoError(t, err) // Setup client context - this will enqueue tasks clientCtx := setupDBOS(t, false, false) // Don't drop DB, don't check for leaks @@ -256,95 +211,61 @@ func TestCancelResume(t *testing.T) { WorkflowInput: input, ApplicationVersion: serverCtx.GetApplicationVersion(), }) - if err != nil { - t.Fatalf("failed to enqueue workflow from client: %v", err) - } + require.NoError(t, err, "failed to enqueue workflow from client") // Wait for workflow to signal it has started and step one completed workflowStarted.Wait() // Verify step one completed but step two hasn't - if stepsCompleted != 1 { - t.Fatalf("expected steps completed to be 1, got %d", stepsCompleted) - } + assert.Equal(t, 1, stepsCompleted, "expected steps completed to be 1") // Cancel the workflow err = CancelWorkflow(clientCtx, workflowID) - if err != nil { - t.Fatalf("failed to cancel workflow: %v", err) - } + require.NoError(t, err, "failed to cancel workflow") // Verify workflow is cancelled cancelStatus, err := handle.GetStatus() - if err != nil { - t.Fatalf("failed to get workflow status: %v", err) - } + require.NoError(t, err, "failed to get workflow status") - if cancelStatus.Status != WorkflowStatusCancelled { - t.Fatalf("expected workflow status to be CANCELLED, got %v", cancelStatus.Status) - } + assert.Equal(t, WorkflowStatusCancelled, cancelStatus.Status, "expected workflow status to be CANCELLED") // Resume the workflow resumeHandle, err := ResumeWorkflow[int](clientCtx, workflowID) - if err != nil { - t.Fatalf("failed to resume workflow: %v", err) - } + require.NoError(t, err, "failed to resume workflow") // Wait for workflow completion proceedSignal.Set() // Allow the workflow to proceed to step two result, err := resumeHandle.GetResult() - if err != nil { - t.Fatalf("failed to get result from resumed workflow: %v", err) - } + require.NoError(t, err, "failed to get result from resumed workflow") // Verify the result - if result != input { - t.Fatalf("expected result to be %d, got %d", input, result) - } + assert.Equal(t, input, result, "expected result to match input") // Verify both steps completed - if stepsCompleted != 2 { - t.Fatalf("expected steps completed to be 2, got %d", stepsCompleted) - } + assert.Equal(t, 2, stepsCompleted, "expected steps completed to be 2") // Check final status finalStatus, err := resumeHandle.GetStatus() - if err != nil { - t.Fatalf("failed to get final workflow status: %v", err) - } + require.NoError(t, err, "failed to get final workflow status") - if finalStatus.Status != WorkflowStatusSuccess { - t.Fatalf("expected final workflow status to be SUCCESS, got %v", finalStatus.Status) - } + assert.Equal(t, WorkflowStatusSuccess, finalStatus.Status, "expected final workflow status to be SUCCESS") // After resume, the queue name should change to the internal queue name - if finalStatus.QueueName != _DBOS_INTERNAL_QUEUE_NAME { - t.Fatalf("expected queue name to be %s, got '%s'", _DBOS_INTERNAL_QUEUE_NAME, finalStatus.QueueName) - } + assert.Equal(t, _DBOS_INTERNAL_QUEUE_NAME, finalStatus.QueueName, "expected queue name to be %s", _DBOS_INTERNAL_QUEUE_NAME) // Resume the workflow again - should not run again resumeAgainHandle, err := ResumeWorkflow[int](clientCtx, workflowID) - if err != nil { - t.Fatalf("failed to resume workflow again: %v", err) - } + require.NoError(t, err, "failed to resume workflow again") resultAgain, err := resumeAgainHandle.GetResult() - if err != nil { - t.Fatalf("failed to get result from second resume: %v", err) - } + require.NoError(t, err, "failed to get result from second resume") - if resultAgain != input { - t.Fatalf("expected second resume result to be %d, got %d", input, resultAgain) - } + assert.Equal(t, input, resultAgain, "expected second resume result to match input") // Verify steps didn't run again - if stepsCompleted != 2 { - t.Fatalf("expected steps completed to remain 2 after second resume, got %d", stepsCompleted) - } + assert.Equal(t, 2, stepsCompleted, "expected steps completed to remain 2 after second resume") - if !queueEntriesAreCleanedUp(serverCtx) { - t.Fatal("expected queue entries to be cleaned up after cancel/resume test") - } + require.True(t, queueEntriesAreCleanedUp(serverCtx), "expected queue entries to be cleaned up after cancel/resume test") }) t.Run("CancelAndResumeTimeout", func(t *testing.T) { @@ -360,28 +281,20 @@ func TestCancelResume(t *testing.T) { WorkflowTimeout: workflowTimeout, ApplicationVersion: serverCtx.GetApplicationVersion(), }) - if err != nil { - t.Fatalf("failed to enqueue timeout blocking workflow: %v", err) - } + require.NoError(t, err, "failed to enqueue timeout blocking workflow") // Wait 500ms (well before the timeout expires) time.Sleep(500 * time.Millisecond) // Cancel the workflow before timeout expires err = CancelWorkflow(clientCtx, workflowID) - if err != nil { - t.Fatalf("failed to cancel workflow: %v", err) - } + require.NoError(t, err, "failed to cancel workflow") // Verify workflow is cancelled cancelStatus, err := handle.GetStatus() - if err != nil { - t.Fatalf("failed to get workflow status after cancel: %v", err) - } + require.NoError(t, err, "failed to get workflow status after cancel") - if cancelStatus.Status != WorkflowStatusCancelled { - t.Fatalf("expected workflow status to be CANCELLED, got %v", cancelStatus.Status) - } + assert.Equal(t, WorkflowStatusCancelled, cancelStatus.Status, "expected workflow status to be CANCELLED") // Record the original deadline before resume originalDeadline := cancelStatus.Deadline @@ -389,58 +302,36 @@ func TestCancelResume(t *testing.T) { // Resume the workflow resumeStart := time.Now() resumeHandle, err := ResumeWorkflow[string](clientCtx, workflowID) - if err != nil { - t.Fatalf("failed to resume workflow: %v", err) - } + require.NoError(t, err, "failed to resume workflow") // Get status after resume to check the deadline resumeStatus, err := resumeHandle.GetStatus() - if err != nil { - t.Fatalf("failed to get workflow status after resume: %v", err) - } + require.NoError(t, err, "failed to get workflow status after resume") // Verify the deadline was reset (should be different from original) - if resumeStatus.Deadline.Equal(originalDeadline) { - t.Fatalf("expected deadline to be reset after resume, but it remained the same: %v", originalDeadline) - } + assert.False(t, resumeStatus.Deadline.Equal(originalDeadline), "expected deadline to be reset after resume, but it remained the same: %v", originalDeadline) // The new deadline should be after resumeStart + workflowTimeout expectedDeadline := resumeStart.Add(workflowTimeout) - if resumeStatus.Deadline.Before(expectedDeadline) { - t.Fatalf("deadline %v is too early (expected around %v)", resumeStatus.Deadline, expectedDeadline) - } + assert.False(t, resumeStatus.Deadline.Before(expectedDeadline), "deadline %v is too early (expected around %v)", resumeStatus.Deadline, expectedDeadline) // Wait for the workflow to complete _, err = resumeHandle.GetResult() - if err == nil { - t.Fatal("expected timeout error, but got none") - } + require.Error(t, err, "expected timeout error, but got none") dbosErr, ok := err.(*DBOSError) - if !ok { - t.Fatalf("expected error to be of type *DBOSError, got %T", err) - } + require.True(t, ok, "expected error to be of type *DBOSError, got %T", err) - if dbosErr.Code != AwaitedWorkflowCancelled { - t.Fatalf("expected error code to be AwaitedWorkflowCancelled (8), got %v", dbosErr.Code) - } + assert.Equal(t, AwaitedWorkflowCancelled, dbosErr.Code, "expected error code to be AwaitedWorkflowCancelled") - if !strings.Contains(dbosErr.Error(), "test-cancel-resume-timeout-workflow was cancelled") { - t.Fatalf("expected error message to contain 'test-cancel-resume-timeout-workflow was cancelled', got: %v", dbosErr.Error()) - } + assert.Contains(t, dbosErr.Error(), "test-cancel-resume-timeout-workflow was cancelled", "expected error message to contain cancellation text") finalStatus, err := resumeHandle.GetStatus() - if err != nil { - t.Fatalf("failed to get final workflow status: %v", err) - } + require.NoError(t, err, "failed to get final workflow status") - if finalStatus.Status != WorkflowStatusCancelled { - t.Fatalf("expected final workflow status to be CANCELLED, got %v", finalStatus.Status) - } + assert.Equal(t, WorkflowStatusCancelled, finalStatus.Status, "expected final workflow status to be CANCELLED") - if !queueEntriesAreCleanedUp(serverCtx) { - t.Fatal("expected queue entries to be cleaned up after cancel/resume timeout test") - } + require.True(t, queueEntriesAreCleanedUp(serverCtx), "expected queue entries to be cleaned up after cancel/resume timeout test") }) t.Run("CancelNonExistentWorkflow", func(t *testing.T) { @@ -448,23 +339,15 @@ func TestCancelResume(t *testing.T) { // Try to cancel a non-existent workflow err := CancelWorkflow(clientCtx, nonExistentWorkflowID) - if err == nil { - t.Fatal("expected error when canceling non-existent workflow, but got none") - } + require.Error(t, err, "expected error when canceling non-existent workflow, but got none") // Verify error type and code dbosErr, ok := err.(*DBOSError) - if !ok { - t.Fatalf("expected error to be of type *DBOSError, got %T", err) - } + require.True(t, ok, "expected error to be of type *DBOSError, got %T", err) - if dbosErr.Code != NonExistentWorkflowError { - t.Fatalf("expected error code to be NonExistentWorkflowError, got %v", dbosErr.Code) - } + assert.Equal(t, NonExistentWorkflowError, dbosErr.Code, "expected error code to be NonExistentWorkflowError") - if dbosErr.DestinationID != nonExistentWorkflowID { - t.Fatalf("expected DestinationID to be %s, got %s", nonExistentWorkflowID, dbosErr.DestinationID) - } + assert.Equal(t, nonExistentWorkflowID, dbosErr.DestinationID, "expected DestinationID to match") }) t.Run("ResumeNonExistentWorkflow", func(t *testing.T) { @@ -472,23 +355,15 @@ func TestCancelResume(t *testing.T) { // Try to resume a non-existent workflow _, err := ResumeWorkflow[int](clientCtx, nonExistentWorkflowID) - if err == nil { - t.Fatal("expected error when resuming non-existent workflow, but got none") - } + require.Error(t, err, "expected error when resuming non-existent workflow, but got none") // Verify error type and code dbosErr, ok := err.(*DBOSError) - if !ok { - t.Fatalf("expected error to be of type *DBOSError, got %T", err) - } + require.True(t, ok, "expected error to be of type *DBOSError, got %T", err) - if dbosErr.Code != NonExistentWorkflowError { - t.Fatalf("expected error code to be NonExistentWorkflowError, got %v", dbosErr.Code) - } + assert.Equal(t, NonExistentWorkflowError, dbosErr.Code, "expected error code to be NonExistentWorkflowError") - if dbosErr.DestinationID != nonExistentWorkflowID { - t.Fatalf("expected DestinationID to be %s, got %s", nonExistentWorkflowID, dbosErr.DestinationID) - } + assert.Equal(t, nonExistentWorkflowID, dbosErr.DestinationID, "expected DestinationID to match") }) } @@ -566,9 +441,7 @@ func TestForkWorkflow(t *testing.T) { // Launch the server context to start processing tasks err := serverCtx.Launch() - if err != nil { - t.Fatalf("failed to launch server DBOS instance: %v", err) - } + require.NoError(t, err) // Setup client context clientCtx := setupDBOS(t, false, false) @@ -587,25 +460,20 @@ func TestForkWorkflow(t *testing.T) { WorkflowInput: "test", ApplicationVersion: serverCtx.GetApplicationVersion(), }) - if err != nil { - t.Fatalf("failed to enqueue original workflow: %v", err) - } + require.NoError(t, err, "failed to enqueue original workflow") // Wait for the original workflow to complete result, err := handle.GetResult() - if err != nil { - t.Fatalf("failed to get result from original workflow: %v", err) - } + require.NoError(t, err, "failed to get result from original workflow") expectedResult := "step1-test+step2-test+child1-test+child2-test" - if result != expectedResult { - t.Fatalf("expected result to be '%s', got '%s'", expectedResult, result) - } + assert.Equal(t, expectedResult, result, "expected result to match") // Verify all counters are 1 after original workflow - if stepCount1 != 1 || stepCount2 != 1 || child1Count != 1 || child2Count != 1 { - t.Fatalf("expected counters to be (step1:1, step2:1, child1:1, child2:1), got (step1:%d, step2:%d, child1:%d, child2:%d)", stepCount1, stepCount2, child1Count, child2Count) - } + assert.Equal(t, 1, stepCount1, "step1 counter should be 1") + assert.Equal(t, 1, stepCount2, "step2 counter should be 1") + assert.Equal(t, 1, child1Count, "child1 counter should be 1") + assert.Equal(t, 1, child2Count, "child2 counter should be 1") // 2. Fork from each step 1 to 6 and verify results // Note: there's 6 steps: 2 steps 2 children and 2 GetResults @@ -618,64 +486,42 @@ func TestForkWorkflow(t *testing.T) { ForkedWorkflowID: customForkedWorkflowID, StartStep: uint(step - 1), }) - if err != nil { - t.Fatalf("failed to fork workflow at step %d: %v", step, err) - } + require.NoError(t, err, "failed to fork workflow at step %d", step) forkedWorkflowID := forkedHandle.GetWorkflowID() - if forkedWorkflowID != customForkedWorkflowID { - t.Fatalf("expected forked workflow ID to be '%s', got '%s'", customForkedWorkflowID, forkedWorkflowID) - } + assert.Equal(t, customForkedWorkflowID, forkedWorkflowID, "expected forked workflow ID to match") forkedResult, err := forkedHandle.GetResult() - if err != nil { - t.Fatalf("failed to get result from forked workflow at step %d: %v", step, err) - } + require.NoError(t, err, "failed to get result from forked workflow at step %d", step) // 1) Verify workflow result is correct - if forkedResult != expectedResult { - t.Fatalf("forked workflow at step %d: expected result '%s', got '%s'", step, expectedResult, forkedResult) - } + assert.Equal(t, expectedResult, forkedResult, "forked workflow at step %d: expected result to match", step) // 2) Verify counters are at expected totals based on the step where we're forking t.Logf("Step %d: actual counters - step1:%d, step2:%d, child1:%d, child2:%d", step, stepCount1, stepCount2, child1Count, child2Count) // First step is executed only once - if stepCount1 != 1+1 { - t.Fatalf("forked workflow at step %d: step1 counter should be 2, got %d", step, stepCount1) - } + assert.Equal(t, 2, stepCount1, "forked workflow at step %d: step1 counter should be 2", step) // First child will be executed twice if step < 3 { - if child1Count != 1+step { - t.Fatalf("forked workflow at step %d: child1 counter should be %d, got %d", step, 1+step, child1Count) - } + assert.Equal(t, 1+step, child1Count, "forked workflow at step %d: child1 counter should be %d", step, 1+step) } else { - if child1Count != 1+2 { - t.Fatalf("forked workflow at step %d: child2 counter should be 3, got %d", step, child1Count) - } + assert.Equal(t, 3, child1Count, "forked workflow at step %d: child1 counter should be 3", step) } // Second step (in reality step 4) will be executed 4 times if step < 5 { - if stepCount2 != 1+step { - t.Fatalf("forked workflow at step %d: step2 counter should be %d, got %d", step, 1+step, stepCount2) - } + assert.Equal(t, 1+step, stepCount2, "forked workflow at step %d: step2 counter should be %d", step, 1+step) } else { - if stepCount2 != 1+4 { - t.Fatalf("forked workflow at step %d: step2 counter should be 5, got %d", step, stepCount2) - } + assert.Equal(t, 5, stepCount2, "forked workflow at step %d: step2 counter should be 5", step) } // Second child will be executed 5 times if step < 6 { - if child2Count != 1+step { - t.Fatalf("forked workflow at step %d: child2 counter should be %d, got %d", step, 1+step, child2Count) - } + assert.Equal(t, 1+step, child2Count, "forked workflow at step %d: child2 counter should be %d", step, 1+step) } else { - if child2Count != 1+5 { - t.Fatalf("forked workflow at step %d: child2 counter should be 6, got %d", step, child2Count) - } + assert.Equal(t, 6, child2Count, "forked workflow at step %d: child2 counter should be 6", step) } t.Logf("Step %d: all counter totals verified correctly", step) @@ -692,29 +538,19 @@ func TestForkWorkflow(t *testing.T) { OriginalWorkflowID: nonExistentWorkflowID, StartStep: 1, }) - if err == nil { - t.Fatal("expected error when forking non-existent workflow, but got none") - } + require.Error(t, err, "expected error when forking non-existent workflow, but got none") // Verify error type dbosErr, ok := err.(*DBOSError) - if !ok { - t.Fatalf("expected error to be of type *DBOSError, got %T", err) - } + require.True(t, ok, "expected error to be of type *DBOSError, got %T", err) - if dbosErr.Code != NonExistentWorkflowError { - t.Fatalf("expected error code to be NonExistentWorkflowError, got %v", dbosErr.Code) - } + assert.Equal(t, NonExistentWorkflowError, dbosErr.Code, "expected error code to be NonExistentWorkflowError") - if dbosErr.DestinationID != nonExistentWorkflowID { - t.Fatalf("expected DestinationID to be %s, got %s", nonExistentWorkflowID, dbosErr.DestinationID) - } + assert.Equal(t, nonExistentWorkflowID, dbosErr.DestinationID, "expected DestinationID to match") }) // Verify all queue entries are cleaned up - if !queueEntriesAreCleanedUp(serverCtx) { - t.Fatal("expected queue entries to be cleaned up after fork workflow tests") - } + require.True(t, queueEntriesAreCleanedUp(serverCtx), "expected queue entries to be cleaned up after fork workflow tests") } func TestListWorkflows(t *testing.T) { @@ -740,9 +576,7 @@ func TestListWorkflows(t *testing.T) { // Launch server err := serverCtx.Launch() - if err != nil { - t.Fatalf("failed to launch server DBOS instance: %v", err) - } + require.NoError(t, err) // Setup client context clientCtx := setupDBOS(t, false, false) @@ -785,9 +619,7 @@ func TestListWorkflows(t *testing.T) { }) } - if err != nil { - t.Fatalf("failed to enqueue workflow %d: %v", i, err) - } + require.NoError(t, err, "failed to enqueue workflow %d", i) workflowIDs = append(workflowIDs, workflowID) handles = append(handles, handle) @@ -801,93 +633,61 @@ func TestListWorkflows(t *testing.T) { _, err := handle.GetResult() if i < 8 { // First 8 should succeed - if err != nil { - t.Fatalf("workflow %d should have succeeded but got error: %v", i, err) - } + require.NoError(t, err, "workflow %d should have succeeded", i) } else { // Last 2 should fail - if err == nil { - t.Fatalf("workflow %d should have failed but succeeded", i) - } + require.Error(t, err, "workflow %d should have failed", i) } } // Test 1: List all workflows (no filters) allWorkflows, err := ListWorkflows(clientCtx) - if err != nil { - t.Fatalf("failed to list all workflows: %v", err) - } - if len(allWorkflows) < 10 { - t.Fatalf("expected at least 10 workflows, got %d", len(allWorkflows)) - } + require.NoError(t, err, "failed to list all workflows") + assert.GreaterOrEqual(t, len(allWorkflows), 10, "expected at least 10 workflows") // Test 2: Filter by workflow IDs expectedIDs := workflowIDs[:3] specificWorkflows, err := ListWorkflows(clientCtx, WithWorkflowIDs(expectedIDs)) - if err != nil { - t.Fatalf("failed to list workflows by IDs: %v", err) - } - if len(specificWorkflows) != 3 { - t.Fatalf("expected 3 workflows, got %d", len(specificWorkflows)) - } + require.NoError(t, err, "failed to list workflows by IDs") + assert.Len(t, specificWorkflows, 3, "expected 3 workflows") // Verify returned workflow IDs match expected returnedIDs := make(map[string]bool) for _, wf := range specificWorkflows { returnedIDs[wf.ID] = true } for _, expectedID := range expectedIDs { - if !returnedIDs[expectedID] { - t.Fatalf("expected workflow ID %s not found in results", expectedID) - } + assert.True(t, returnedIDs[expectedID], "expected workflow ID %s not found in results", expectedID) } // Test 3: Filter by workflow ID prefix batchWorkflows, err := ListWorkflows(clientCtx, WithWorkflowIDPrefix("test-batch-")) - if err != nil { - t.Fatalf("failed to list workflows by prefix: %v", err) - } - if len(batchWorkflows) != 5 { - t.Fatalf("expected 5 batch workflows, got %d", len(batchWorkflows)) - } + require.NoError(t, err, "failed to list workflows by prefix") + assert.Len(t, batchWorkflows, 5, "expected 5 batch workflows") // Verify all returned workflow IDs have the correct prefix for _, wf := range batchWorkflows { - if !strings.HasPrefix(wf.ID, "test-batch-") { - t.Fatalf("workflow ID %s does not have expected prefix 'test-batch-'", wf.ID) - } + assert.True(t, strings.HasPrefix(wf.ID, "test-batch-"), "workflow ID %s does not have expected prefix 'test-batch-'", wf.ID) } // Test 4: Filter by status - SUCCESS successWorkflows, err := ListWorkflows(clientCtx, WithWorkflowIDPrefix("test-"), // Only our test workflows WithStatus([]WorkflowStatusType{WorkflowStatusSuccess})) - if err != nil { - t.Fatalf("failed to list successful workflows: %v", err) - } - if len(successWorkflows) != 8 { - t.Fatalf("expected 8 successful workflows, got %d", len(successWorkflows)) - } + require.NoError(t, err, "failed to list successful workflows") + assert.Len(t, successWorkflows, 8, "expected 8 successful workflows") // Verify all returned workflows have SUCCESS status for _, wf := range successWorkflows { - if wf.Status != WorkflowStatusSuccess { - t.Fatalf("workflow %s has status %s, expected SUCCESS", wf.ID, wf.Status) - } + assert.Equal(t, WorkflowStatusSuccess, wf.Status, "workflow %s has unexpected status", wf.ID) } // Test 5: Filter by status - ERROR errorWorkflows, err := ListWorkflows(clientCtx, WithWorkflowIDPrefix("test-"), WithStatus([]WorkflowStatusType{WorkflowStatusError})) - if err != nil { - t.Fatalf("failed to list error workflows: %v", err) - } - if len(errorWorkflows) != 2 { - t.Fatalf("expected 2 error workflows, got %d", len(errorWorkflows)) - } + require.NoError(t, err, "failed to list error workflows") + assert.Len(t, errorWorkflows, 2, "expected 2 error workflows") // Verify all returned workflows have ERROR status for _, wf := range errorWorkflows { - if wf.Status != WorkflowStatusError { - t.Fatalf("workflow %s has status %s, expected ERROR", wf.ID, wf.Status) - } + assert.Equal(t, WorkflowStatusError, wf.Status, "workflow %s has unexpected status", wf.ID) } // Test 6: Filter by time range - first 5 workflows (start to start+500ms) @@ -895,100 +695,66 @@ func TestListWorkflows(t *testing.T) { firstHalfWorkflows, err := ListWorkflows(clientCtx, WithWorkflowIDPrefix("test-"), WithEndTime(firstHalfTime)) - if err != nil { - t.Fatalf("failed to list first half workflows by time range: %v", err) - } - if len(firstHalfWorkflows) != 5 { - t.Fatalf("expected 5 workflows in first half time range, got %d", len(firstHalfWorkflows)) - } + require.NoError(t, err, "failed to list first half workflows by time range") + assert.Len(t, firstHalfWorkflows, 5, "expected 5 workflows in first half time range") // Test 6b: Filter by time range - last 5 workflows (start+500ms to end) secondHalfWorkflows, err := ListWorkflows(clientCtx, WithWorkflowIDPrefix("test-"), WithStartTime(firstHalfTime)) - if err != nil { - t.Fatalf("failed to list second half workflows by time range: %v", err) - } - if len(secondHalfWorkflows) != 5 { - t.Fatalf("expected 5 workflows in second half time range, got %d", len(secondHalfWorkflows)) - } + require.NoError(t, err, "failed to list second half workflows by time range") + assert.Len(t, secondHalfWorkflows, 5, "expected 5 workflows in second half time range") // Test 7: Test sorting order (ascending - default) ascWorkflows, err := ListWorkflows(clientCtx, WithWorkflowIDPrefix("test-"), WithSortDesc(false)) - if err != nil { - t.Fatalf("failed to list workflows ascending: %v", err) - } + require.NoError(t, err, "failed to list workflows ascending") // Test 8: Test sorting order (descending) descWorkflows, err := ListWorkflows(clientCtx, WithWorkflowIDPrefix("test-"), WithSortDesc(true)) - if err != nil { - t.Fatalf("failed to list workflows descending: %v", err) - } + require.NoError(t, err, "failed to list workflows descending") // Verify sorting - workflows should be ordered by creation time // First workflow in desc should be last in asc (latest created) - if ascWorkflows[len(ascWorkflows)-1].ID != descWorkflows[0].ID { - t.Fatalf("sorting verification failed: asc last (%s) != desc first (%s)", - ascWorkflows[len(ascWorkflows)-1].ID, descWorkflows[0].ID) - } + assert.Equal(t, ascWorkflows[len(ascWorkflows)-1].ID, descWorkflows[0].ID, "sorting verification failed: asc last != desc first") // Last workflow in desc should be first in asc (earliest created) - if ascWorkflows[0].ID != descWorkflows[len(descWorkflows)-1].ID { - t.Fatalf("sorting verification failed: asc first (%s) != desc last (%s)", - ascWorkflows[0].ID, descWorkflows[len(descWorkflows)-1].ID) - } + assert.Equal(t, ascWorkflows[0].ID, descWorkflows[len(descWorkflows)-1].ID, "sorting verification failed: asc first != desc last") // Verify ascending order: each workflow should be created at or after the previous for i := 1; i < len(ascWorkflows); i++ { - if ascWorkflows[i].CreatedAt.Before(ascWorkflows[i-1].CreatedAt) { - t.Fatalf("ascending order violation: workflow at index %d created before previous", i) - } + assert.False(t, ascWorkflows[i].CreatedAt.Before(ascWorkflows[i-1].CreatedAt), "ascending order violation: workflow at index %d created before previous", i) } // Verify descending order: each workflow should be created at or before the previous for i := 1; i < len(descWorkflows); i++ { - if descWorkflows[i].CreatedAt.After(descWorkflows[i-1].CreatedAt) { - t.Fatalf("descending order violation: workflow at index %d created after previous", i) - } + assert.False(t, descWorkflows[i].CreatedAt.After(descWorkflows[i-1].CreatedAt), "descending order violation: workflow at index %d created after previous", i) } // Test 9: Test limit and offset limitedWorkflows, err := ListWorkflows(clientCtx, WithWorkflowIDPrefix("test-"), WithLimit(5)) - if err != nil { - t.Fatalf("failed to list workflows with limit: %v", err) - } - if len(limitedWorkflows) != 5 { - t.Fatalf("expected 5 workflows with limit, got %d", len(limitedWorkflows)) - } + require.NoError(t, err, "failed to list workflows with limit") + assert.Len(t, limitedWorkflows, 5, "expected 5 workflows with limit") // Verify we got the first 5 workflows (earliest created) expectedFirstFive := ascWorkflows[:5] for i, wf := range limitedWorkflows { - if wf.ID != expectedFirstFive[i].ID { - t.Fatalf("limited workflow at index %d: expected %s, got %s", i, expectedFirstFive[i].ID, wf.ID) - } + assert.Equal(t, expectedFirstFive[i].ID, wf.ID, "limited workflow at index %d: unexpected ID", i) } offsetWorkflows, err := ListWorkflows(clientCtx, WithWorkflowIDPrefix("test-"), WithOffset(5), WithLimit(3)) - if err != nil { - t.Fatalf("failed to list workflows with offset: %v", err) - } - if len(offsetWorkflows) != 3 { - t.Fatalf("expected 3 workflows with offset, got %d", len(offsetWorkflows)) - } + require.NoError(t, err, "failed to list workflows with offset") + assert.Len(t, offsetWorkflows, 3, "expected 3 workflows with offset") // Verify we got workflows 5, 6, 7 from the ascending list expectedOffsetThree := ascWorkflows[5:8] for i, wf := range offsetWorkflows { - if wf.ID != expectedOffsetThree[i].ID { - t.Fatalf("offset workflow at index %d: expected %s, got %s", i, expectedOffsetThree[i].ID, wf.ID) - } + assert.Equal(t, expectedOffsetThree[i].ID, wf.ID, "offset workflow at index %d: unexpected ID", i) } // Test 10: Test input/output loading @@ -996,26 +762,16 @@ func TestListWorkflows(t *testing.T) { WithWorkflowIDs(workflowIDs[:2]), WithLoadInput(false), WithLoadOutput(false)) - if err != nil { - t.Fatalf("failed to list workflows without data: %v", err) - } - if len(noDataWorkflows) != 2 { - t.Fatalf("expected 2 workflows without data, got %d", len(noDataWorkflows)) - } + require.NoError(t, err, "failed to list workflows without data") + assert.Len(t, noDataWorkflows, 2, "expected 2 workflows without data") // Verify input/output are not loaded for _, wf := range noDataWorkflows { - if wf.Input != nil { - t.Fatalf("expected input to be nil when LoadInput=false, got %v", wf.Input) - } - if wf.Output != nil { - t.Fatalf("expected output to be nil when LoadOutput=false, got %v", wf.Output) - } + assert.Nil(t, wf.Input, "expected input to be nil when LoadInput=false") + assert.Nil(t, wf.Output, "expected output to be nil when LoadOutput=false") } }) // Verify all queue entries are cleaned up - if !queueEntriesAreCleanedUp(serverCtx) { - t.Fatal("expected queue entries to be cleaned up after list workflows tests") - } + require.True(t, queueEntriesAreCleanedUp(serverCtx), "expected queue entries to be cleaned up after list workflows tests") } diff --git a/dbos/dbos_test.go b/dbos/dbos_test.go index 3158b911..f65c314c 100644 --- a/dbos/dbos_test.go +++ b/dbos/dbos_test.go @@ -2,6 +2,9 @@ package dbos import ( "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestConfigValidationErrorTypes(t *testing.T) { @@ -15,35 +18,25 @@ func TestConfigValidationErrorTypes(t *testing.T) { DatabaseURL: databaseURL, AppName: "test-initialize", }) - if err != nil { - t.Fatalf("Failed to initialize DBOS: %v", err) - } + require.NoError(t, err) defer func() { if ctx != nil { ctx.Cancel() } }() // Clean up executor - if ctx == nil { - t.Fatal("Initialize returned nil executor") - } + require.NotNil(t, ctx) // Test that executor implements DBOSContext interface var _ DBOSContext = ctx // Test that we can call methods on the executor appVersion := ctx.GetApplicationVersion() - if appVersion != "v1.0.0" { - t.Fatal("GetApplicationVersion returned empty string") - } + assert.Equal(t, "v1.0.0", appVersion) executorID := ctx.GetExecutorID() - if executorID != "test-executor-id" { - t.Fatal("GetExecutorID returned empty string") - } + assert.Equal(t, "test-executor-id", executorID) appID := ctx.GetApplicationID() - if appID != "test-app-id" { - t.Fatal("GetApplicationID returned empty string") - } + assert.Equal(t, "test-app-id", appID) }) t.Run("FailsWithoutAppName", func(t *testing.T) { @@ -52,23 +45,15 @@ func TestConfigValidationErrorTypes(t *testing.T) { } _, err := NewDBOSContext(config) - if err == nil { - t.Fatal("expected error when app name is missing, but got none") - } + require.Error(t, err) dbosErr, ok := err.(*DBOSError) - if !ok { - t.Fatalf("expected DBOSError, got %T", err) - } + require.True(t, ok, "expected DBOSError, got %T", err) - if dbosErr.Code != InitializationError { - t.Fatalf("expected InitializationError code, got %v", dbosErr.Code) - } + assert.Equal(t, InitializationError, dbosErr.Code) expectedMsg := "Error initializing DBOS Transact: missing required config field: appName" - if dbosErr.Message != expectedMsg { - t.Fatalf("expected error message '%s', got '%s'", expectedMsg, dbosErr.Message) - } + assert.Equal(t, expectedMsg, dbosErr.Message) }) t.Run("FailsWithoutDatabaseURL", func(t *testing.T) { @@ -77,22 +62,14 @@ func TestConfigValidationErrorTypes(t *testing.T) { } _, err := NewDBOSContext(config) - if err == nil { - t.Fatal("expected error when database URL is missing, but got none") - } + require.Error(t, err) dbosErr, ok := err.(*DBOSError) - if !ok { - t.Fatalf("expected DBOSError, got %T", err) - } + require.True(t, ok, "expected DBOSError, got %T", err) - if dbosErr.Code != InitializationError { - t.Fatalf("expected InitializationError code, got %v", dbosErr.Code) - } + assert.Equal(t, InitializationError, dbosErr.Code) expectedMsg := "Error initializing DBOS Transact: missing required config field: databaseURL" - if dbosErr.Message != expectedMsg { - t.Fatalf("expected error message '%s', got '%s'", expectedMsg, dbosErr.Message) - } + assert.Equal(t, expectedMsg, dbosErr.Message) }) } diff --git a/dbos/logger_test.go b/dbos/logger_test.go index 191f9953..62bffa31 100644 --- a/dbos/logger_test.go +++ b/dbos/logger_test.go @@ -3,8 +3,10 @@ package dbos import ( "bytes" "log/slog" - "strings" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestLogger(t *testing.T) { @@ -15,13 +17,9 @@ func TestLogger(t *testing.T) { DatabaseURL: databaseURL, AppName: "test-app", }) // Create executor with default logger - if err != nil { - t.Fatalf("Failed to create executor with default logger: %v", err) - } + require.NoError(t, err) err = dbosCtx.Launch() - if err != nil { - t.Fatalf("Failed to launch with default logger: %v", err) - } + require.NoError(t, err) t.Cleanup(func() { if dbosCtx != nil { dbosCtx.Cancel() @@ -29,9 +27,7 @@ func TestLogger(t *testing.T) { }) ctx := dbosCtx.(*dbosContext) - if ctx.logger == nil { - t.Fatal("Logger is nil") - } + require.NotNil(t, ctx.logger) // Test logger access ctx.logger.Info("Test message from default logger") @@ -53,13 +49,9 @@ func TestLogger(t *testing.T) { AppName: "test-app", Logger: slogLogger, }) - if err != nil { - t.Fatalf("Failed to create executor with custom logger: %v", err) - } + require.NoError(t, err) err = dbosCtx.Launch() - if err != nil { - t.Fatalf("Failed to launch with custom logger: %v", err) - } + require.NoError(t, err) t.Cleanup(func() { if dbosCtx != nil { dbosCtx.Cancel() @@ -67,23 +59,15 @@ func TestLogger(t *testing.T) { }) ctx := dbosCtx.(*dbosContext) - if ctx.logger == nil { - t.Fatal("Logger is nil") - } + require.NotNil(t, ctx.logger) // Test that we can use the logger and it maintains context ctx.logger.Info("Test message from custom logger", "test_key", "test_value") // Check that our custom logger was used and captured the output logOutput := buf.String() - if !strings.Contains(logOutput, "service=dbos-test") { - t.Errorf("Expected log output to contain service=dbos-test, got: %s", logOutput) - } - if !strings.Contains(logOutput, "environment=test") { - t.Errorf("Expected log output to contain environment=test, got: %s", logOutput) - } - if !strings.Contains(logOutput, "test_key=test_value") { - t.Errorf("Expected log output to contain test_key=test_value, got: %s", logOutput) - } + assert.Contains(t, logOutput, "service=dbos-test", "Expected log output to contain service=dbos-test") + assert.Contains(t, logOutput, "environment=test", "Expected log output to contain environment=test") + assert.Contains(t, logOutput, "test_key=test_value", "Expected log output to contain test_key=test_value") }) } diff --git a/dbos/queues_test.go b/dbos/queues_test.go index ee6d7318..ed5307a1 100644 --- a/dbos/queues_test.go +++ b/dbos/queues_test.go @@ -5,12 +5,13 @@ import ( "errors" "fmt" "os" - "strings" "sync/atomic" "testing" "time" "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) /** @@ -104,84 +105,55 @@ func TestWorkflowQueues(t *testing.T) { RegisterWorkflow(dbosCtx, enqueueWorkflowDLQ, WithMaxRetries(dlqMaxRetries)) err := dbosCtx.Launch() - if err != nil { - t.Fatalf("failed to launch DBOS instance: %v", err) - } + require.NoError(t, err) t.Run("EnqueueWorkflow", func(t *testing.T) { handle, err := RunAsWorkflow(dbosCtx, queueWorkflow, "test-input", WithQueue(queue.Name)) - if err != nil { - t.Fatalf("failed to enqueue workflow: %v", err) - } + require.NoError(t, err) _, ok := handle.(*workflowPollingHandle[string]) - if !ok { - t.Fatalf("expected handle to be of type workflowPollingHandle, got %T", handle) - } + require.True(t, ok, "expected handle to be of type workflowPollingHandle, got %T", handle) res, err := handle.GetResult() - if err != nil { - t.Fatalf("expected no error but got: %v", err) - } - if res != "test-input" { - t.Fatalf("expected workflow result to be 'test-input', got %v", res) - } + require.NoError(t, err) + assert.Equal(t, "test-input", res) - if !queueEntriesAreCleanedUp(dbosCtx) { - t.Fatal("expected queue entries to be cleaned up after global concurrency test") - } + require.True(t, queueEntriesAreCleanedUp(dbosCtx), "expected queue entries to be cleaned up after global concurrency test") }) t.Run("EnqueuedWorkflowStartsChildWorkflow", func(t *testing.T) { handle, err := RunAsWorkflow(dbosCtx, queueWorkflowWithChild, "test-input", WithQueue(queue.Name)) - if err != nil { - t.Fatalf("failed to enqueue workflow with child: %v", err) - } + require.NoError(t, err) res, err := handle.GetResult() - if err != nil { - t.Fatalf("expected no error but got: %v", err) - } + require.NoError(t, err) // Expected result: child workflow returns "test-input-child" expectedResult := "test-input-child" - if res != expectedResult { - t.Fatalf("expected workflow result to be '%s', got %v", expectedResult, res) - } + assert.Equal(t, expectedResult, res) - if !queueEntriesAreCleanedUp(dbosCtx) { - t.Fatal("expected queue entries to be cleaned up after global concurrency test") - } + require.True(t, queueEntriesAreCleanedUp(dbosCtx), "expected queue entries to be cleaned up after global concurrency test") }) t.Run("WorkflowEnqueuesAnotherWorkflow", func(t *testing.T) { handle, err := RunAsWorkflow(dbosCtx, queueWorkflowThatEnqueues, "test-input", WithQueue(queue.Name)) - if err != nil { - t.Fatalf("failed to enqueue workflow that enqueues another workflow: %v", err) - } + require.NoError(t, err) res, err := handle.GetResult() - if err != nil { - t.Fatalf("expected no error but got: %v", err) - } + require.NoError(t, err) // Expected result: enqueued workflow returns "test-input-enqueued" expectedResult := "test-input-enqueued" - if res != expectedResult { - t.Fatalf("expected workflow result to be '%s', got %v", expectedResult, res) - } + assert.Equal(t, expectedResult, res) - if !queueEntriesAreCleanedUp(dbosCtx) { - t.Fatal("expected queue entries to be cleaned up after global concurrency test") - } + require.True(t, queueEntriesAreCleanedUp(dbosCtx), "expected queue entries to be cleaned up after global concurrency test") }) t.Run("DynamicRegistration", func(t *testing.T) { // Attempting to register a queue after launch should panic defer func() { - if r := recover(); r == nil { - t.Fatal("expected panic from queue registration after launch but got none") - } + r := recover() + assert.NotNil(t, r, "expected panic from queue registration after launch but got none") }() NewWorkflowQueue(dbosCtx, "dynamic-queue") }) @@ -191,9 +163,7 @@ func TestWorkflowQueues(t *testing.T) { // Enqueue the workflow for the first time originalHandle, err := RunAsWorkflow(dbosCtx, enqueueWorkflowDLQ, "test-input", WithQueue(dlqEnqueueQueue.Name), WithWorkflowID(workflowID)) - if err != nil { - t.Fatalf("failed to enqueue blocking workflow: %v", err) - } + require.NoError(t, err) // Wait for the workflow to start dlqStartEvent.Wait() @@ -202,45 +172,31 @@ func TestWorkflowQueues(t *testing.T) { // Try to enqueue the same workflow more times for i := range dlqMaxRetries * 2 { _, err := RunAsWorkflow(dbosCtx, enqueueWorkflowDLQ, "test-input", WithQueue(dlqEnqueueQueue.Name), WithWorkflowID(workflowID)) - if err != nil { - t.Fatalf("failed to enqueue workflow attempt %d: %v", i+1, err) - } + require.NoError(t, err, "failed to enqueue workflow attempt %d", i+1) } // Get the status from the original handle and check the attempts counter status, err := originalHandle.GetStatus() - if err != nil { - t.Fatalf("failed to get status of original workflow handle: %v", err) - } + require.NoError(t, err, "failed to get status of original workflow handle") // The attempts counter should still be 1 (the original enqueue) - if status.Attempts != 1 { - t.Fatalf("expected attempts to be 1, got %d", status.Attempts) - } + assert.Equal(t, 1, status.Attempts, "expected attempts to be 1") // Check that the workflow hits DLQ after re-running max retries handles := make([]WorkflowHandle[any], 0, dlqMaxRetries+1) for i := range dlqMaxRetries { recoveryHandles, err := recoverPendingWorkflows(dbosCtx.(*dbosContext), []string{"local"}) - if err != nil { - t.Fatalf("failed to recover pending workflows: %v", err) - } - if len(recoveryHandles) != 1 { - t.Fatalf("expected 1 handle, got %d", len(recoveryHandles)) - } + require.NoError(t, err, "failed to recover pending workflows") + assert.Len(t, recoveryHandles, 1, "expected 1 handle") dlqStartEvent.Wait() dlqStartEvent.Clear() handle := recoveryHandles[0] handles = append(handles, handle) status, err := handle.GetStatus() - if err != nil { - t.Fatalf("failed to get status of recovered workflow handle: %v", err) - } + require.NoError(t, err, "failed to get status of recovered workflow handle") if i == dlqMaxRetries { // On the last retry, the workflow should be in DLQ - if status.Status != WorkflowStatusRetriesExceeded { - t.Fatalf("expected workflow status to be %s, got %v", WorkflowStatusRetriesExceeded, status.Status) - } + assert.Equal(t, WorkflowStatusRetriesExceeded, status.Status, "expected workflow status to be %s", WorkflowStatusRetriesExceeded) } } @@ -248,17 +204,11 @@ func TestWorkflowQueues(t *testing.T) { dlqCompleteEvent.Set() for _, handle := range handles { result, err := handle.GetResult() - if err != nil { - t.Fatalf("failed to get result from recovered workflow handle: %v", err) - } - if result != "test-input" { - t.Fatalf("expected result to be 'test-input', got %v", result) - } + require.NoError(t, err, "failed to get result from recovered workflow handle") + assert.Equal(t, "test-input", result, "expected result to be 'test-input'") } - if !queueEntriesAreCleanedUp(dbosCtx) { - t.Fatal("expected queue entries to be cleaned up after successive enqueues test") - } + require.True(t, queueEntriesAreCleanedUp(dbosCtx), "expected queue entries to be cleaned up after successive enqueues test") }) t.Run("ConflictingWorkflowOnDifferentQueues", func(t *testing.T) { @@ -266,45 +216,29 @@ func TestWorkflowQueues(t *testing.T) { // Enqueue the same workflow ID on the first queue handle, err := RunAsWorkflow(dbosCtx, queueWorkflow, "test-input-1", WithQueue(conflictQueue1.Name), WithWorkflowID(workflowID)) - if err != nil { - t.Fatalf("failed to enqueue workflow on first queue: %v", err) - } + require.NoError(t, err, "failed to enqueue workflow on first queue") // Get the result from the first workflow to ensure it completes result, err := handle.GetResult() - if err != nil { - t.Fatalf("failed to get result from first workflow: %v", err) - } - if result != "test-input-1" { - t.Fatalf("expected 'test-input-1', got %v", result) - } + require.NoError(t, err, "failed to get result from first workflow") + assert.Equal(t, "test-input-1", result, "expected 'test-input-1'") // Now try to enqueue the same workflow ID on a different queue // This should trigger a ConflictingWorkflowError _, err = RunAsWorkflow(dbosCtx, queueWorkflow, "test-input-2", WithQueue(conflictQueue2.Name), WithWorkflowID(workflowID)) - if err == nil { - t.Fatal("expected ConflictingWorkflowError when enqueueing same workflow ID on different queue, but got none") - } + require.Error(t, err, "expected ConflictingWorkflowError when enqueueing same workflow ID on different queue, but got none") // Check that it's the correct error type dbosErr, ok := err.(*DBOSError) - if !ok { - t.Fatalf("expected error to be of type *DBOSError, got %T", err) - } + require.True(t, ok, "expected error to be of type *DBOSError, got %T", err) - if dbosErr.Code != ConflictingWorkflowError { - t.Fatalf("expected error code to be ConflictingWorkflowError, got %v", dbosErr.Code) - } + assert.Equal(t, ConflictingWorkflowError, dbosErr.Code, "expected error code to be ConflictingWorkflowError") // Check that the error message contains queue information expectedMsgPart := "Workflow already exists in a different queue" - if !strings.Contains(err.Error(), expectedMsgPart) { - t.Fatalf("expected error message to contain '%s', got '%s'", expectedMsgPart, err.Error()) - } + assert.Contains(t, err.Error(), expectedMsgPart, "expected error message to contain expected part") - if !queueEntriesAreCleanedUp(dbosCtx) { - t.Fatal("expected queue entries to be cleaned up after conflicting workflow test") - } + require.True(t, queueEntriesAreCleanedUp(dbosCtx), "expected queue entries to be cleaned up after conflicting workflow test") }) } @@ -347,9 +281,7 @@ func TestQueueRecovery(t *testing.T) { RegisterWorkflow(dbosCtx, recoveryWorkflowFunc) err := dbosCtx.Launch() - if err != nil { - t.Fatalf("failed to launch DBOS instance: %v", err) - } + require.NoError(t, err, "failed to launch DBOS instance") queuedSteps := 5 @@ -361,23 +293,19 @@ func TestQueueRecovery(t *testing.T) { // Start the workflow. Wait for all steps to start. Verify that they started. handle, err := RunAsWorkflow(dbosCtx, recoveryWorkflowFunc, "", WithWorkflowID(wfid)) - if err != nil { - t.Fatalf("failed to start workflow: %v", err) - } + require.NoError(t, err, "failed to start workflow") for _, e := range recoveryStepEvents { e.Wait() e.Clear() } - if atomic.LoadInt64(&recoveryStepCounter) != int64(queuedSteps) { - t.Fatalf("expected recoveryStepCounter to be %d, got %d", queuedSteps, atomic.LoadInt64(&recoveryStepCounter)) - } + assert.Equal(t, int64(queuedSteps), atomic.LoadInt64(&recoveryStepCounter), "expected recoveryStepCounter to match queuedSteps") // Recover the workflow, then resume it. recoveryHandles, err := recoverPendingWorkflows(dbosCtx.(*dbosContext), []string{"local"}) if err != nil { - t.Fatalf("failed to recover pending workflows: %v", err) + require.NoError(t, err, "failed to recover pending workflows") } for _, e := range recoveryStepEvents { @@ -385,61 +313,39 @@ func TestQueueRecovery(t *testing.T) { } recoveryEvent.Set() - if len(recoveryHandles) != queuedSteps+1 { - t.Fatalf("expected %d recovery handles, got %d", queuedSteps+1, len(recoveryHandles)) - } + assert.Len(t, recoveryHandles, queuedSteps+1, "expected specific number of recovery handles") for _, h := range recoveryHandles { if h.GetWorkflowID() == wfid { // Root workflow case result, err := h.GetResult() - if err != nil { - t.Fatalf("failed to get result from recovered root workflow handle: %v", err) - } + require.NoError(t, err, "failed to get result from recovered root workflow handle") castedResult, ok := result.([]int) - if !ok { - t.Fatalf("expected result to be of type []int for root workflow, got %T", result) - } + require.True(t, ok, "expected result to be of type []int for root workflow, got %T", result) expectedResult := []int{0, 1, 2, 3, 4} - if !equal(castedResult, expectedResult) { - t.Fatalf("expected result %v, got %v", expectedResult, castedResult) - } + assert.True(t, equal(castedResult, expectedResult), "expected result %v, got %v", expectedResult, castedResult) } } result, err := handle.GetResult() - if err != nil { - t.Fatalf("failed to get result from original handle: %v", err) - } + require.NoError(t, err, "failed to get result from original handle") expectedResult := []int{0, 1, 2, 3, 4} - if !equal(result, expectedResult) { - t.Fatalf("expected result %v, got %v", expectedResult, result) - } + assert.True(t, equal(result, expectedResult), "expected result %v, got %v", expectedResult, result) - if atomic.LoadInt64(&recoveryStepCounter) != int64(queuedSteps*2) { - t.Fatalf("expected recoveryStepCounter to be %d, got %d", queuedSteps*2, atomic.LoadInt64(&recoveryStepCounter)) - } + assert.Equal(t, int64(queuedSteps*2), atomic.LoadInt64(&recoveryStepCounter), "expected recoveryStepCounter to be %d", queuedSteps*2) // Rerun the workflow. Because each step is complete, none should start again. rerunHandle, err := RunAsWorkflow(dbosCtx, recoveryWorkflowFunc, "test-input", WithWorkflowID(wfid)) - if err != nil { - t.Fatalf("failed to rerun workflow: %v", err) - } + require.NoError(t, err, "failed to rerun workflow") rerunResult, err := rerunHandle.GetResult() - if err != nil { - t.Fatalf("failed to get result from rerun handle: %v", err) - } + require.NoError(t, err, "failed to get result from rerun handle") if !equal(rerunResult, expectedResult) { - t.Fatalf("expected result %v, got %v", expectedResult, rerunResult) + assert.True(t, equal(rerunResult, expectedResult), "expected result %v, got %v", expectedResult, rerunResult) } - if atomic.LoadInt64(&recoveryStepCounter) != int64(queuedSteps*2) { - t.Fatalf("expected recoveryStepCounter to remain %d, got %d", queuedSteps*2, atomic.LoadInt64(&recoveryStepCounter)) - } + assert.Equal(t, int64(queuedSteps*2), atomic.LoadInt64(&recoveryStepCounter), "expected recoveryStepCounter to remain %d", queuedSteps*2) - if !queueEntriesAreCleanedUp(dbosCtx) { - t.Fatal("expected queue entries to be cleaned up after global concurrency test") - } + require.True(t, queueEntriesAreCleanedUp(dbosCtx), "expected queue entries to be cleaned up after global concurrency test") } // TODO: we can update this test to have the same logic than TestWorkerConcurrency @@ -465,61 +371,39 @@ func TestGlobalConcurrency(t *testing.T) { RegisterWorkflow(dbosCtx, globalConcurrencyWorkflowFunc) err := dbosCtx.Launch() - if err != nil { - t.Fatalf("failed to launch DBOS instance: %v", err) - } + require.NoError(t, err, "failed to launch DBOS instance") // Enqueue two workflows handle1, err := RunAsWorkflow(dbosCtx, globalConcurrencyWorkflowFunc, "workflow1", WithQueue(globalConcurrencyQueue.Name)) - if err != nil { - t.Fatalf("failed to enqueue workflow1: %v", err) - } + require.NoError(t, err, "failed to enqueue workflow1") handle2, err := RunAsWorkflow(dbosCtx, globalConcurrencyWorkflowFunc, "workflow2", WithQueue(globalConcurrencyQueue.Name)) - if err != nil { - t.Fatalf("failed to enqueue workflow2: %v", err) - } + require.NoError(t, err, "failed to enqueue workflow2") // Wait for the first workflow to start workflowEvent1.Wait() time.Sleep(2 * time.Second) // Wait for a few seconds to let the queue runner loop // Ensure the second workflow has not started yet - if workflowEvent2.IsSet { - t.Fatalf("expected workflow2 to not start while workflow1 is running") - } + assert.False(t, workflowEvent2.IsSet, "expected workflow2 to not start while workflow1 is running") status, err := handle2.GetStatus() - if err != nil { - t.Fatalf("failed to get status of workflow2: %v", err) - } - if status.Status != WorkflowStatusEnqueued { - t.Fatalf("expected workflow2 to be in ENQUEUED status, got %v", status.Status) - } + require.NoError(t, err, "failed to get status of workflow2") + assert.Equal(t, WorkflowStatusEnqueued, status.Status, "expected workflow2 to be in ENQUEUED status") // Allow the first workflow to complete workflowDoneEvent.Set() result1, err := handle1.GetResult() - if err != nil { - t.Fatalf("failed to get result from workflow1: %v", err) - } - if result1 != "workflow1" { - t.Fatalf("expected result from workflow1 to be 'workflow1', got %v", result1) - } + require.NoError(t, err, "failed to get result from workflow1") + assert.Equal(t, "workflow1", result1, "expected result from workflow1 to be 'workflow1'") // Wait for the second workflow to start workflowEvent2.Wait() result2, err := handle2.GetResult() - if err != nil { - t.Fatalf("failed to get result from workflow2: %v", err) - } - if result2 != "workflow2" { - t.Fatalf("expected result from workflow2 to be 'workflow2', got %v", result2) - } - if !queueEntriesAreCleanedUp(dbosCtx) { - t.Fatal("expected queue entries to be cleaned up after global concurrency test") - } + require.NoError(t, err, "failed to get result from workflow2") + assert.Equal(t, "workflow2", result2, "expected result from workflow2 to be 'workflow2'") + require.True(t, queueEntriesAreCleanedUp(dbosCtx), "expected queue entries to be cleaned up after global concurrency test") } func TestWorkerConcurrency(t *testing.T) { @@ -530,9 +414,8 @@ func TestWorkerConcurrency(t *testing.T) { dbosCtx2 := setupDBOS(t, false, false) // Don't check for leaks because t.Cancel is called in LIFO order. Also don't reset the DB here. os.Unsetenv("DBOS__VMID") - if dbosCtx1.GetExecutorID() != "worker1" || dbosCtx2.GetExecutorID() != "worker2" { - t.Fatalf("expected executor IDs to be 'worker1' and 'worker2', got '%s' and '%s'", dbosCtx1.GetExecutorID(), dbosCtx2.GetExecutorID()) - } + assert.Equal(t, "worker1", dbosCtx1.GetExecutorID(), "expected first executor ID to be 'worker1'") + assert.Equal(t, "worker2", dbosCtx2.GetExecutorID(), "expected second executor ID to be 'worker2'") workerConcurrencyQueue := NewWorkflowQueue(dbosCtx1, "test-worker-concurrency-queue", WithWorkerConcurrency(1)) NewWorkflowQueue(dbosCtx2, "test-worker-concurrency-queue", WithWorkerConcurrency(1)) @@ -554,9 +437,7 @@ func TestWorkerConcurrency(t *testing.T) { workflows, err := dbosCtx1.(*dbosContext).systemDB.listWorkflows(context.Background(), listWorkflowsDBInput{ queueName: workerConcurrencyQueue.Name, }) - if err != nil { - t.Fatalf("failed to list workflows: %v", err) - } + require.NoError(t, err, "failed to list workflows") pendings := make(map[string]int) enqueuedCount := 0 @@ -571,14 +452,10 @@ func TestWorkerConcurrency(t *testing.T) { } for executorID, count := range pendings { - if count != expectedPendingPerExecutor { - t.Fatalf("expected %d pending workflow on executor %s, got %d", expectedPendingPerExecutor, executorID, count) - } + assert.Equal(t, expectedPendingPerExecutor, count, "expected %d pending workflow on executor %s", expectedPendingPerExecutor, executorID) } - if enqueuedCount != expectedEnqueued { - t.Fatalf("expected %d workflows to be enqueued, got %d", expectedEnqueued, enqueuedCount) - } + assert.Equal(t, expectedEnqueued, enqueuedCount, "expected %d workflows to be enqueued", expectedEnqueued) } // Create workflow with dbosContext @@ -592,40 +469,26 @@ func TestWorkerConcurrency(t *testing.T) { RegisterWorkflow(dbosCtx2, blockingWfFunc) err := dbosCtx1.Launch() - if err != nil { - t.Fatalf("failed to launch DBOS instance: %v", err) - } + require.NoError(t, err, "failed to launch DBOS instance") err = dbosCtx2.Launch() - if err != nil { - t.Fatalf("failed to launch DBOS instance: %v", err) - } + require.NoError(t, err, "failed to launch DBOS instance") // First enqueue four blocking workflows handle1, err := RunAsWorkflow(dbosCtx1, blockingWfFunc, 0, WithQueue(workerConcurrencyQueue.Name), WithWorkflowID("worker-cc-wf-1")) - if err != nil { - t.Fatalf("failed to enqueue blocking workflow 1: %v", err) - } + require.NoError(t, err) handle2, err := RunAsWorkflow(dbosCtx1, blockingWfFunc, 1, WithQueue(workerConcurrencyQueue.Name), WithWorkflowID("worker-cc-wf-2")) - if err != nil { - t.Fatalf("failed to enqueue blocking workflow 2: %v", err) - } + require.NoError(t, err) _, err = RunAsWorkflow(dbosCtx1, blockingWfFunc, 2, WithQueue(workerConcurrencyQueue.Name), WithWorkflowID("worker-cc-wf-3")) - if err != nil { - t.Fatalf("failed to enqueue blocking workflow 3: %v", err) - } + require.NoError(t, err) _, err = RunAsWorkflow(dbosCtx1, blockingWfFunc, 3, WithQueue(workerConcurrencyQueue.Name), WithWorkflowID("worker-cc-wf-4")) - if err != nil { - t.Fatalf("failed to enqueue blocking workflow 4: %v", err) - } + require.NoError(t, err) // The two first workflows should dequeue on both workers startEvents[0].Wait() startEvents[1].Wait() // Ensure the two other workflows are not started yet - if startEvents[2].IsSet || startEvents[3].IsSet { - t.Fatal("expected only blocking workflow 1 and 2 to start, but others have started") - } + assert.False(t, startEvents[2].IsSet || startEvents[3].IsSet, "expected only blocking workflow 1 and 2 to start, but others have started") // Expect 1 workflow pending on each executor and 2 workflows enqueued checkWorkflowStatus(t, 1, 2) @@ -633,18 +496,12 @@ func TestWorkerConcurrency(t *testing.T) { // Unlock workflow 1, check wf 3 starts, check 4 stays blocked completeEvents[0].Set() result1, err := handle1.GetResult() - if err != nil { - t.Fatalf("failed to get result from blocking workflow 1: %v", err) - } - if result1 != 0 { - t.Fatalf("expected result from blocking workflow 1 to be 0, got %v", result1) - } + require.NoError(t, err, "failed to get result from blocking workflow 1") + assert.Equal(t, 0, result1, "expected result from blocking workflow 1 to be 0") // 3rd workflow should start startEvents[2].Wait() // Ensure the fourth workflow is not started yet - if startEvents[3].IsSet { - t.Fatal("expected only blocking workflow 3 to start, but workflow 4 has started") - } + assert.False(t, startEvents[3].IsSet, "expected only blocking workflow 3 to start, but workflow 4 has started") // Check that 1 workflow is pending on each executor and 1 workflow is enqueued checkWorkflowStatus(t, 1, 1) @@ -652,12 +509,8 @@ func TestWorkerConcurrency(t *testing.T) { // Unlock workflow 2 and check wf 4 starts completeEvents[1].Set() result2, err := handle2.GetResult() - if err != nil { - t.Fatalf("failed to get result from blocking workflow 2: %v", err) - } - if result2 != 1 { - t.Fatalf("expected result from blocking workflow 2 to be 1, got %v", result2) - } + require.NoError(t, err, "failed to get result from blocking workflow 2") + assert.Equal(t, 1, result2, "expected result from blocking workflow 2 to be 1") // 4th workflow should start now startEvents[3].Wait() // workflow 3 and 4 should be pending, one per executor, and no workflows enqueued @@ -667,9 +520,7 @@ func TestWorkerConcurrency(t *testing.T) { completeEvents[2].Set() completeEvents[3].Set() - if !queueEntriesAreCleanedUp(dbosCtx1) { - t.Fatal("expected queue entries to be cleaned up after global concurrency test") - } + require.True(t, queueEntriesAreCleanedUp(dbosCtx1), "expected queue entries to be cleaned up after global concurrency test") } func TestWorkerConcurrencyXRecovery(t *testing.T) { @@ -696,19 +547,13 @@ func TestWorkerConcurrencyXRecovery(t *testing.T) { RegisterWorkflow(dbosCtx, workerConcurrencyRecoveryBlockingWf2) err := dbosCtx.Launch() - if err != nil { - t.Fatalf("failed to launch DBOS instance: %v", err) - } + require.NoError(t, err, "failed to launch DBOS instance") // Enqueue two workflows on a queue with worker concurrency = 1 handle1, err := RunAsWorkflow(dbosCtx, workerConcurrencyRecoveryBlockingWf1, "workflow1", WithQueue(workerConcurrencyRecoveryQueue.Name), WithWorkflowID("worker-cc-x-recovery-wf-1")) - if err != nil { - t.Fatalf("failed to enqueue blocking workflow 1: %v", err) - } + require.NoError(t, err) handle2, err := RunAsWorkflow(dbosCtx, workerConcurrencyRecoveryBlockingWf2, "workflow2", WithQueue(workerConcurrencyRecoveryQueue.Name), WithWorkflowID("worker-cc-x-recovery-wf-2")) - if err != nil { - t.Fatalf("failed to enqueue blocking workflow 2: %v", err) - } + require.NoError(t, err) // Start the first workflow and wait for it to start workerConcurrencyRecoveryStartEvent1.Wait() @@ -718,51 +563,33 @@ func TestWorkerConcurrencyXRecovery(t *testing.T) { // Ensure the 2nd workflow is still ENQUEUED status2, err := handle2.GetStatus() - if err != nil { - t.Fatalf("failed to get status of workflow2: %v", err) - } - if status2.Status != WorkflowStatusEnqueued { - t.Fatalf("expected workflow2 to be in ENQUEUED status, got %v", status2.Status) - } + require.NoError(t, err, "failed to get status of workflow2") + assert.Equal(t, WorkflowStatusEnqueued, status2.Status, "expected workflow2 to be in ENQUEUED status") // Verify workflow2 hasn't started yet - if workerConcurrencyRecoveryStartEvent2.IsSet { - t.Fatal("expected workflow2 to not start while workflow1 is running") - } + assert.False(t, workerConcurrencyRecoveryStartEvent2.IsSet, "expected workflow2 to not start while workflow1 is running") // Now, manually call the recoverPendingWorkflows method recoveryHandles, err := recoverPendingWorkflows(dbosCtx.(*dbosContext), []string{"local"}) if err != nil { - t.Fatalf("failed to recover pending workflows: %v", err) + require.NoError(t, err, "failed to recover pending workflows") } // You should get 1 handle associated with the first workflow - if len(recoveryHandles) != 1 { - t.Fatalf("expected 1 recovery handle, got %d", len(recoveryHandles)) - } + assert.Len(t, recoveryHandles, 1, "expected 1 recovery handle") // The handle status should tell you the workflow is ENQUEUED recoveredHandle := recoveryHandles[0] - if recoveredHandle.GetWorkflowID() != "worker-cc-x-recovery-wf-1" { - t.Fatalf("expected recovered handle to be for workflow1, got %s", recoveredHandle.GetWorkflowID()) - } + assert.Equal(t, "worker-cc-x-recovery-wf-1", recoveredHandle.GetWorkflowID(), "expected recovered handle to be for workflow1") wf1Status, err := recoveredHandle.GetStatus() - if err != nil { - t.Fatalf("failed to get status of recovered workflow1: %v", err) - } - if wf1Status.Status != WorkflowStatusEnqueued { - t.Fatalf("expected recovered handle to be in ENQUEUED status, got %v", wf1Status.Status) - } + require.NoError(t, err, "failed to get status of recovered workflow1") + assert.Equal(t, WorkflowStatusEnqueued, wf1Status.Status, "expected recovered handle to be in ENQUEUED status") // The 1 first workflow should have been dequeued again (FIFO ordering) and the 2nd workflow should still be enqueued workerConcurrencyRecoveryStartEvent1.Wait() status2, err = handle2.GetStatus() - if err != nil { - t.Fatalf("failed to get status of workflow2: %v", err) - } - if status2.Status != WorkflowStatusEnqueued { - t.Fatalf("expected workflow2 to still be in ENQUEUED status, got %v", status2.Status) - } + require.NoError(t, err, "failed to get status of workflow2") + assert.Equal(t, WorkflowStatusEnqueued, status2.Status, "expected workflow2 to still be in ENQUEUED status") // Let the 1st workflow complete and let the 2nd workflow start and complete workerConcurrencyRecoveryCompleteEvent1.Set() @@ -771,26 +598,16 @@ func TestWorkerConcurrencyXRecovery(t *testing.T) { // Get result from first workflow result1, err := handle1.GetResult() - if err != nil { - t.Fatalf("failed to get result from workflow1: %v", err) - } - if result1 != "workflow1" { - t.Fatalf("expected result from workflow1 to be 'workflow1', got %v", result1) - } + require.NoError(t, err, "failed to get result from workflow1") + assert.Equal(t, "workflow1", result1, "expected result from workflow1 to be 'workflow1'") // Get result from second workflow result2, err := handle2.GetResult() - if err != nil { - t.Fatalf("failed to get result from workflow2: %v", err) - } - if result2 != "workflow2" { - t.Fatalf("expected result from workflow2 to be 'workflow2', got %v", result2) - } + require.NoError(t, err, "failed to get result from workflow2") + assert.Equal(t, "workflow2", result2, "expected result from workflow2 to be 'workflow2'") // Ensure queueEntriesAreCleanedUp is set to true - if !queueEntriesAreCleanedUp(dbosCtx) { - t.Fatal("expected queue entries to be cleaned up after worker concurrency recovery test") - } + require.True(t, queueEntriesAreCleanedUp(dbosCtx), "expected queue entries to be cleaned up after worker concurrency recovery test") } func rateLimiterTestWorkflow(ctx DBOSContext, _ string) (time.Time, error) { @@ -806,9 +623,7 @@ func TestQueueRateLimiter(t *testing.T) { RegisterWorkflow(dbosCtx, rateLimiterTestWorkflow) err := dbosCtx.Launch() - if err != nil { - t.Fatalf("failed to launch DBOS instance: %v", err) - } + require.NoError(t, err, "failed to launch DBOS instance") limit := 5 period := 1.8 @@ -823,18 +638,14 @@ func TestQueueRateLimiter(t *testing.T) { // followed by the next wave. for i := 0; i < limit*numWaves; i++ { handle, err := RunAsWorkflow(dbosCtx, rateLimiterTestWorkflow, "", WithQueue(rateLimiterQueue.Name)) - if err != nil { - t.Fatalf("failed to enqueue workflow %d: %v", i, err) - } + require.NoError(t, err, "failed to enqueue workflow %d", i) handles = append(handles, handle) } // Get results from all workflows for _, handle := range handles { result, err := handle.GetResult() - if err != nil { - t.Fatalf("failed to get result from workflow: %v", err) - } + require.NoError(t, err, "failed to get result from workflow") // XXX in reality this should use the actual start time -- not the completion time. times = append(times, result) } @@ -854,9 +665,7 @@ func TestQueueRateLimiter(t *testing.T) { } // Dynamically compute waves based on start times - if len(sortedTimes) == 0 { - t.Fatal("no workflow times recorded") - } + require.Greater(t, len(sortedTimes), 0, "no workflow times recorded") baseTime := sortedTimes[0] waveMap := make(map[int][]time.Time) @@ -869,34 +678,23 @@ func TestQueueRateLimiter(t *testing.T) { } // Verify each wave has fewer than the limit for waveIndex, wave := range waveMap { - if len(wave) > limit { - t.Fatalf("wave %d has %d workflows, which exceeds the limit of %d", waveIndex, len(wave), limit) - } - if len(wave) == 0 { - t.Fatalf("wave %d is empty, which shouldn't happen", waveIndex) - } + assert.LessOrEqual(t, len(wave), limit, "wave %d has %d workflows, which exceeds the limit of %d", waveIndex, len(wave), limit) + assert.Greater(t, len(wave), 0, "wave %d is empty, which shouldn't happen", waveIndex) } // Verify we have the expected number of waves (allowing some tolerance) expectedWaves := numWaves - if len(waveMap) < expectedWaves-1 || len(waveMap) > expectedWaves+1 { - t.Fatalf("expected approximately %d waves, got %d", expectedWaves, len(waveMap)) - } + assert.GreaterOrEqual(t, len(waveMap), expectedWaves-1, "expected approximately %d waves, got %d", expectedWaves, len(waveMap)) + assert.LessOrEqual(t, len(waveMap), expectedWaves+1, "expected approximately %d waves, got %d", expectedWaves, len(waveMap)) // Verify all workflows get the SUCCESS status eventually for i, handle := range handles { status, err := handle.GetStatus() - if err != nil { - t.Fatalf("failed to get status for workflow %d: %v", i, err) - } - if status.Status != WorkflowStatusSuccess { - t.Fatalf("expected workflow %d to have SUCCESS status, got %v", i, status.Status) - } + require.NoError(t, err, "failed to get status for workflow %d", i) + assert.Equal(t, WorkflowStatusSuccess, status.Status, "expected workflow %d to have SUCCESS status", i) } // Verify all queue entries eventually get cleaned up. - if !queueEntriesAreCleanedUp(dbosCtx) { - t.Fatal("expected queue entries to be cleaned up after rate limiter test") - } + require.True(t, queueEntriesAreCleanedUp(dbosCtx), "expected queue entries to be cleaned up after rate limiter test") } func TestQueueTimeouts(t *testing.T) { @@ -908,7 +706,7 @@ func TestQueueTimeouts(t *testing.T) { // This workflow will wait indefinitely until it is cancelled <-ctx.Done() if !errors.Is(ctx.Err(), context.Canceled) && !errors.Is(ctx.Err(), context.DeadlineExceeded) { - t.Fatalf("workflow was cancelled, but context error is not context.Canceled nor context.DeadlineExceeded: %v", ctx.Err()) + require.True(t, errors.Is(ctx.Err(), context.Canceled) || errors.Is(ctx.Err(), context.DeadlineExceeded), "workflow was cancelled, but context error is not context.Canceled nor context.DeadlineExceeded: %v", ctx.Err()) } return "", ctx.Err() } @@ -918,29 +716,21 @@ func TestQueueTimeouts(t *testing.T) { // This workflow will enqueue a workflow that waits indefinitely until it is cancelled handle, err := RunAsWorkflow(ctx, queuedWaitForCancelWorkflow, "enqueued-wait-for-cancel", WithQueue(timeoutQueue.Name)) if err != nil { - t.Fatalf("failed to start enqueued wait for cancel workflow: %v", err) + require.NoError(t, err, "failed to start enqueued wait for cancel workflow") } // Workflow should get AwaitedWorkflowCancelled DBOSError _, err = handle.GetResult() if err == nil { - t.Fatal("expected error when waiting for enqueued workflow to complete, but got none") + require.Error(t, err, "expected error when waiting for enqueued workflow to complete, but got none") } dbosErr, ok := err.(*DBOSError) - if !ok { - t.Fatalf("expected error to be of type *DBOSError, got %T", err) - } - if dbosErr.Code != AwaitedWorkflowCancelled { - t.Fatalf("expected error code to be AwaitedWorkflowCancelled, got %v", dbosErr.Code) - } + require.True(t, ok, "expected error to be of type *DBOSError, got %T", err) + assert.Equal(t, AwaitedWorkflowCancelled, dbosErr.Code, "expected error code to be AwaitedWorkflowCancelled") // enqueud workflow should have been cancelled status, err := handle.GetStatus() - if err != nil { - t.Fatalf("failed to get status of enqueued workflow: %v", err) - } - if status.Status != WorkflowStatusCancelled { - t.Fatalf("expected enqueued workflow status to be WorkflowStatusCancelled, got %v", status.Status) - } + require.NoError(t, err, "failed to get status of enqueued workflow") + assert.Equal(t, WorkflowStatusCancelled, status.Status, "expected enqueued workflow status to be WorkflowStatusCancelled") return "should-never-see-this", nil } @@ -960,25 +750,21 @@ func TestQueueTimeouts(t *testing.T) { childCtx := WithoutCancel(ctx) handle, err := RunAsWorkflow(childCtx, detachedWorkflow, timeout*2, WithQueue(timeoutQueue.Name)) if err != nil { - t.Fatalf("failed to start enqueued detached workflow: %v", err) + require.NoError(t, err, "failed to start enqueued detached workflow") } // Wait for the enqueued workflow to complete result, err := handle.GetResult() if err != nil { - t.Fatalf("failed to get result from enqueued detached workflow: %v", err) + require.NoError(t, err, "failed to get result from enqueued detached workflow") } if result != "detached-workflow-completed" { - t.Fatalf("expected result to be 'detached-workflow-completed', got '%s'", result) + assert.Equal(t, "detached-workflow-completed", result, "expected result to be 'detached-workflow-completed'") } // Check the workflow status: should be success status, err := handle.GetStatus() - if err != nil { - t.Fatalf("failed to get enqueued detached workflow status: %v", err) - } - if status.Status != WorkflowStatusSuccess { - t.Fatalf("expected enqueued detached workflow status to be WorkflowStatusSuccess, got %v", status.Status) - } + require.NoError(t, err, "failed to get enqueued detached workflow status") + assert.Equal(t, WorkflowStatusSuccess, status.Status, "expected enqueued detached workflow status to be WorkflowStatusSuccess") return result, nil } @@ -993,42 +779,26 @@ func TestQueueTimeouts(t *testing.T) { defer cancelFunc() // Ensure we clean up the context handle, err := RunAsWorkflow(cancelCtx, queuedWaitForCancelWorkflow, "enqueue-wait-for-cancel", WithQueue(timeoutQueue.Name)) - if err != nil { - t.Fatalf("failed to enqueue wait for cancel workflow: %v", err) - } + require.NoError(t, err, "failed to enqueue wait for cancel workflow") // Wait for the workflow to complete and get the result result, err := handle.GetResult() - if err == nil { - t.Fatal("expected error but got none") - } + require.Error(t, err, "expected error but got none") // Check the error type dbosErr, ok := err.(*DBOSError) - if !ok { - t.Fatalf("expected error to be of type *DBOSError, got %T", err) - } + require.True(t, ok, "expected error to be of type *DBOSError, got %T", err) - if dbosErr.Code != AwaitedWorkflowCancelled { - t.Fatalf("expected error code to be AwaitedWorkflowCancelled, got %v", dbosErr.Code) - } + assert.Equal(t, AwaitedWorkflowCancelled, dbosErr.Code, "expected error code to be AwaitedWorkflowCancelled") - if result != "" { - t.Fatalf("expected result to be an empty string, got '%s'", result) - } + assert.Equal(t, "", result, "expected result to be an empty string") // Check the workflow status: should be cancelled status, err := handle.GetStatus() - if err != nil { - t.Fatalf("failed to get workflow status: %v", err) - } - if status.Status != WorkflowStatusCancelled { - t.Fatalf("expected workflow status to be WorkflowStatusCancelled, got %v", status.Status) - } + require.NoError(t, err, "failed to get workflow status") + assert.Equal(t, WorkflowStatusCancelled, status.Status, "expected workflow status to be WorkflowStatusCancelled") - if !queueEntriesAreCleanedUp(dbosCtx) { - t.Fatal("expected queue entries to be cleaned up after workflow cancellation, but they are not") - } + require.True(t, queueEntriesAreCleanedUp(dbosCtx), "expected queue entries to be cleaned up after workflow cancellation, but they are not") }) t.Run("EnqueueWorkflowThatEnqueuesATimeoutWorkflow", func(t *testing.T) { @@ -1037,42 +807,26 @@ func TestQueueTimeouts(t *testing.T) { defer cancelFunc() // Ensure we clean up the context handle, err := RunAsWorkflow(cancelCtx, enqueuedWorkflowEnqueuesATimeoutWorkflow, "enqueue-timeout-workflow", WithQueue(timeoutQueue.Name)) - if err != nil { - t.Fatalf("failed to start enqueued workflow: %v", err) - } + require.NoError(t, err, "failed to start enqueued workflow") // Wait for the workflow to complete and get the result result, err := handle.GetResult() - if err == nil { - t.Fatal("expected error but got none") - } + require.Error(t, err, "expected error but got none") // Check the error type dbosErr, ok := err.(*DBOSError) - if !ok { - t.Fatalf("expected error to be of type *DBOSError, got %T", err) - } + require.True(t, ok, "expected error to be of type *DBOSError, got %T", err) - if dbosErr.Code != AwaitedWorkflowCancelled { - t.Fatalf("expected error code to be AwaitedWorkflowCancelled, got %v", dbosErr.Code) - } + assert.Equal(t, AwaitedWorkflowCancelled, dbosErr.Code, "expected error code to be AwaitedWorkflowCancelled") - if result != "" { - t.Fatalf("expected result to be an empty string, got '%s'", result) - } + assert.Equal(t, "", result, "expected result to be an empty string") // Check the workflow status: should be cancelled status, err := handle.GetStatus() - if err != nil { - t.Fatalf("failed to get workflow status: %v", err) - } - if status.Status != WorkflowStatusCancelled { - t.Fatalf("expected workflow status to be WorkflowStatusCancelled, got %v", status.Status) - } + require.NoError(t, err, "failed to get workflow status") + assert.Equal(t, WorkflowStatusCancelled, status.Status, "expected workflow status to be WorkflowStatusCancelled") - if !queueEntriesAreCleanedUp(dbosCtx) { - t.Fatal("expected queue entries to be cleaned up after workflow cancellation, but they are not") - } + require.True(t, queueEntriesAreCleanedUp(dbosCtx), "expected queue entries to be cleaned up after workflow cancellation, but they are not") }) t.Run("EnqueueWorkflowThatEnqueuesADetachedWorkflow", func(t *testing.T) { @@ -1083,40 +837,32 @@ func TestQueueTimeouts(t *testing.T) { handle, err := RunAsWorkflow(cancelCtx, enqueuedWorkflowEnqueuesADetachedWorkflow, timeout, WithQueue(timeoutQueue.Name)) if err != nil { - t.Fatalf("failed to start enqueued detached workflow: %v", err) + require.NoError(t, err, "failed to start enqueued detached workflow") } // Wait for the workflow to complete and get the result result, err := handle.GetResult() if err == nil { - t.Fatalf("expected error but got none") + require.Error(t, err, "expected error but got none") } // Check the error type dbosErr, ok := err.(*DBOSError) - if !ok { - t.Fatalf("expected error to be of type *DBOSError, got %T", err) - } + require.True(t, ok, "expected error to be of type *DBOSError, got %T", err) - if dbosErr.Code != AwaitedWorkflowCancelled { - t.Fatalf("expected error code to be AwaitedWorkflowCancelled, got %v", dbosErr.Code) - } + assert.Equal(t, AwaitedWorkflowCancelled, dbosErr.Code, "expected error code to be AwaitedWorkflowCancelled") - if result != "" { - t.Fatalf("expected result to be an empty string, got '%s'", result) - } + assert.Equal(t, "", result, "expected result to be an empty string") // Check the workflow status: should be cancelled status, err := handle.GetStatus() - if err != nil { - t.Fatalf("failed to get enqueued detached workflow status: %v", err) - } + require.NoError(t, err, "failed to get enqueued detached workflow status") if status.Status != WorkflowStatusCancelled { - t.Fatalf("expected enqueued detached workflow status to be WorkflowStatusCancelled, got %v", status.Status) + assert.Equal(t, WorkflowStatusCancelled, status.Status, "expected enqueued detached workflow status to be WorkflowStatusCancelled") } if !queueEntriesAreCleanedUp(dbosCtx) { - t.Fatal("expected queue entries to be cleaned up after workflow completion, but they are not") + require.True(t, queueEntriesAreCleanedUp(dbosCtx), "expected queue entries to be cleaned up after workflow completion, but they are not") } }) } diff --git a/dbos/serialization_test.go b/dbos/serialization_test.go index e97af44d..9eed3d62 100644 --- a/dbos/serialization_test.go +++ b/dbos/serialization_test.go @@ -6,6 +6,9 @@ import ( "fmt" "testing" "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) /** Test serialization and deserialization @@ -76,95 +79,51 @@ func TestWorkflowEncoding(t *testing.T) { t.Run("BuiltinTypes", func(t *testing.T) { // Test a workflow that uses a built-in type (string) directHandle, err := RunAsWorkflow(executor, encodingWorkflowBuiltinTypes, "test") - if err != nil { - t.Fatalf("failed to execute workflow: %v", err) - } + require.NoError(t, err) // Test result and error from direct handle directHandleResult, err := directHandle.GetResult() - if directHandleResult != "123" { - t.Fatalf("expected direct handle result to be '123', got %v", directHandleResult) - } - if err.Error() != "workflow error: step error" { - t.Fatalf("expected error to be 'workflow error: step error', got %v", err) - } + assert.Equal(t, "123", directHandleResult) + require.Error(t, err) + assert.Equal(t, "workflow error: step error", err.Error()) // Test result from polling handle retrieveHandler, err := RetrieveWorkflow[string](executor.(*dbosContext), directHandle.GetWorkflowID()) - if err != nil { - t.Fatalf("failed to retrieve workflow: %v", err) - } + require.NoError(t, err) retrievedResult, err := retrieveHandler.GetResult() - if retrievedResult != "123" { - t.Fatalf("expected retrieved result to be '123', got %v", retrievedResult) - } - if err.Error() != "workflow error: step error" { - t.Fatalf("expected error to be 'workflow error: step error', got %v", err) - } + assert.Equal(t, "123", retrievedResult) + require.Error(t, err) + assert.Equal(t, "workflow error: step error", err.Error()) // Test results from ListWorkflows workflows, err := ListWorkflows(executor, WithWorkflowIDs( []string{directHandle.GetWorkflowID()}, )) - if err != nil { - t.Fatalf("failed to list workflows: %v", err) - } - if len(workflows) != 1 { - t.Fatalf("expected 1 workflow, got %d", len(workflows)) - } + require.NoError(t, err) + require.Len(t, workflows, 1) workflow := workflows[0] - if workflow.Input == nil { - t.Fatal("expected workflow input to be non-nil") - } + require.NotNil(t, workflow.Input) workflowInput, ok := workflow.Input.(string) - if !ok { - t.Fatalf("expected workflow input to be of type string, got %T", workflow.Input) - } - if workflowInput != "test" { - t.Fatalf("expected workflow input to be 'test', got %v", workflowInput) - } - if workflow.Output == nil { - t.Fatal("expected workflow output to be non-nil") - } + require.True(t, ok, "expected workflow input to be of type string, got %T", workflow.Input) + assert.Equal(t, "test", workflowInput) + require.NotNil(t, workflow.Output) workflowOutput, ok := workflow.Output.(string) - if !ok { - t.Fatalf("expected workflow output to be of type string, got %T", workflow.Output) - } - if workflowOutput != "123" { - t.Fatalf("expected workflow output to be '123', got %v", workflowOutput) - } - if workflow.Error == nil { - t.Fatal("expected workflow error to be non-nil") - } - if workflow.Error.Error() != "workflow error: step error" { - t.Fatalf("expected workflow error to be 'workflow error: step error', got %v", workflow.Error.Error()) - } + require.True(t, ok, "expected workflow output to be of type string, got %T", workflow.Output) + assert.Equal(t, "123", workflowOutput) + require.NotNil(t, workflow.Error) + assert.Equal(t, "workflow error: step error", workflow.Error.Error()) // Test results from GetWorkflowSteps steps, err := executor.(*dbosContext).systemDB.getWorkflowSteps(context.Background(), directHandle.GetWorkflowID()) - if err != nil { - t.Fatalf("failed to get workflow steps: %v", err) - } - if len(steps) != 1 { - t.Fatalf("expected 1 step, got %d", len(steps)) - } + require.NoError(t, err) + require.Len(t, steps, 1) step := steps[0] - if step.Output == nil { - t.Fatal("expected step output to be non-nil") - } + require.NotNil(t, step.Output) stepOutput, ok := step.Output.(int) - if !ok { - t.Fatalf("expected step output to be of type int, got %T", step.Output) - } - if stepOutput != 123 { - t.Fatalf("expected step output to be 123, got %v", stepOutput) - } - if step.Error == nil { - t.Fatal("expected step error to be non-nil") - } - if step.Error.Error() != "step error" { - t.Fatalf("expected step error to be 'step error', got %v", step.Error.Error()) - } + require.True(t, ok, "expected step output to be of type int, got %T", step.Output) + assert.Equal(t, 123, stepOutput) + require.NotNil(t, step.Error) + assert.Equal(t, "step error", step.Error.Error()) }) t.Run("StructType", func(t *testing.T) { @@ -175,123 +134,59 @@ func TestWorkflowEncoding(t *testing.T) { } directHandle, err := RunAsWorkflow(executor, encodingWorkflowStruct, input) - if err != nil { - t.Fatalf("failed to execute step workflow: %v", err) - } + require.NoError(t, err) // Test result from direct handle directResult, err := directHandle.GetResult() - if err != nil { - t.Fatalf("expected no error but got: %v", err) - } - if directResult.A.A.A != input.A.A { - t.Fatalf("expected direct result input data name to be %v, got %v", input.A.A, directResult.A.A.A) - } - if directResult.A.A.B != input.A.B { - t.Fatalf("expected direct result input data value to be %v, got %v", input.A.B, directResult.A.A.B) - } - if directResult.A.B != fmt.Sprintf("%d", input.B) { - t.Fatalf("expected direct result input ID to be %v, got %v", fmt.Sprintf("%d", input.B), directResult.A.B) - } - if directResult.B != "processed by encodingStepStruct" { - t.Fatalf("expected direct result step info to be 'processed by encodingStepStruct', got %v", directResult.B) - } + require.NoError(t, err) + assert.Equal(t, input.A.A, directResult.A.A.A) + assert.Equal(t, input.A.B, directResult.A.A.B) + assert.Equal(t, fmt.Sprintf("%d", input.B), directResult.A.B) + assert.Equal(t, "processed by encodingStepStruct", directResult.B) // Test result from polling handle retrieveHandler, err := RetrieveWorkflow[StepOutputStruct](executor.(*dbosContext), directHandle.GetWorkflowID()) - if err != nil { - t.Fatalf("failed to retrieve step workflow: %v", err) - } + require.NoError(t, err) retrievedResult, err := retrieveHandler.GetResult() - if err != nil { - t.Fatalf("expected no error but got: %v", err) - } - if retrievedResult.A.A.A != input.A.A { - t.Fatalf("expected retrieved result input data name to be %v, got %v", input.A.A, retrievedResult.A.A.A) - } - if retrievedResult.A.A.B != input.A.B { - t.Fatalf("expected retrieved result input data value to be %v, got %v", input.A.B, retrievedResult.A.A.B) - } - if retrievedResult.A.B != fmt.Sprintf("%d", input.B) { - t.Fatalf("expected retrieved result input ID to be %v, got %v", fmt.Sprintf("%d", input.B), retrievedResult.A.B) - } - if retrievedResult.B != "processed by encodingStepStruct" { - t.Fatalf("expected retrieved result step info to be 'processed by encodingStepStruct', got %v", retrievedResult.B) - } + require.NoError(t, err) + assert.Equal(t, input.A.A, retrievedResult.A.A.A) + assert.Equal(t, input.A.B, retrievedResult.A.A.B) + assert.Equal(t, fmt.Sprintf("%d", input.B), retrievedResult.A.B) + assert.Equal(t, "processed by encodingStepStruct", retrievedResult.B) // Test results from ListWorkflows workflows, err := ListWorkflows(executor, WithWorkflowIDs( []string{directHandle.GetWorkflowID()}, )) - if err != nil { - t.Fatalf("failed to list workflows: %v", err) - } + require.NoError(t, err) workflow := workflows[0] - if workflow.Input == nil { - t.Fatal("expected workflow input to be non-nil") - } + require.NotNil(t, workflow.Input) workflowInput, ok := workflow.Input.(WorkflowInputStruct) - if !ok { - t.Fatalf("expected workflow input to be of type WorkflowInputStruct, got %T", workflow.Input) - } - if workflowInput.A.A != input.A.A { - t.Fatalf("expected workflow input data name to be %v, got %v", input.A.A, workflowInput.A.A) - } - if workflowInput.A.B != input.A.B { - t.Fatalf("expected workflow input data value to be %v, got %v", input.A.B, workflowInput.A.B) - } - if workflowInput.B != input.B { - t.Fatalf("expected workflow input ID to be %v, got %v", input.B, workflowInput.B) - } + require.True(t, ok, "expected workflow input to be of type WorkflowInputStruct, got %T", workflow.Input) + assert.Equal(t, input.A.A, workflowInput.A.A) + assert.Equal(t, input.A.B, workflowInput.A.B) + assert.Equal(t, input.B, workflowInput.B) workflowOutput, ok := workflow.Output.(StepOutputStruct) - if !ok { - t.Fatalf("expected workflow output to be of type StepOutputStruct, got %T", workflow.Output) - } - if workflowOutput.A.A.A != input.A.A { - t.Fatalf("expected workflow output input data name to be %v, got %v", input.A.A, workflowOutput.A.A.A) - } - if workflowOutput.A.A.B != input.A.B { - t.Fatalf("expected workflow output input data value to be %v, got %v", input.A.B, workflowOutput.A.A.B) - } - if workflowOutput.A.B != fmt.Sprintf("%d", input.B) { - t.Fatalf("expected workflow output input ID to be %v, got %v", fmt.Sprintf("%d", input.B), workflowOutput.A.B) - } - if workflowOutput.B != "processed by encodingStepStruct" { - t.Fatalf("expected workflow output step info to be 'processed by encodingStepStruct', got %v", workflowOutput.B) - } + require.True(t, ok, "expected workflow output to be of type StepOutputStruct, got %T", workflow.Output) + assert.Equal(t, input.A.A, workflowOutput.A.A.A) + assert.Equal(t, input.A.B, workflowOutput.A.A.B) + assert.Equal(t, fmt.Sprintf("%d", input.B), workflowOutput.A.B) + assert.Equal(t, "processed by encodingStepStruct", workflowOutput.B) // Test results from GetWorkflowSteps steps, err := executor.(*dbosContext).systemDB.getWorkflowSteps(context.Background(), directHandle.GetWorkflowID()) - if err != nil { - t.Fatalf("failed to get workflow steps: %v", err) - } - if len(steps) != 1 { - t.Fatalf("expected 1 step, got %d", len(steps)) - } + require.NoError(t, err) + require.Len(t, steps, 1) step := steps[0] - if step.Output == nil { - t.Fatal("expected step output to be non-nil") - } + require.NotNil(t, step.Output) stepOutput, ok := step.Output.(StepOutputStruct) - if !ok { - t.Fatalf("expected step output to be of type StepOutputStruct, got %T", step.Output) - } - if stepOutput.A.A.A != input.A.A { - t.Fatalf("expected step output input data name to be %v, got %v", input.A.A, stepOutput.A.A.A) - } - if stepOutput.A.A.B != input.A.B { - t.Fatalf("expected step output input data value to be %v, got %v", input.A.B, stepOutput.A.A.B) - } - if stepOutput.A.B != fmt.Sprintf("%d", input.B) { - t.Fatalf("expected step output input ID to be %v, got %v", fmt.Sprintf("%d", input.B), stepOutput.A.B) - } - if stepOutput.B != "processed by encodingStepStruct" { - t.Fatalf("expected step output step info to be 'processed by encodingStepStruct', got %v", stepOutput.B) - } - if step.Error != nil { - t.Fatalf("expected step error to be nil, got %v", step.Error) - } + require.True(t, ok, "expected step output to be of type StepOutputStruct, got %T", step.Output) + assert.Equal(t, input.A.A, stepOutput.A.A.A) + assert.Equal(t, input.A.B, stepOutput.A.A.B) + assert.Equal(t, fmt.Sprintf("%d", input.B), stepOutput.A.B) + assert.Equal(t, "processed by encodingStepStruct", stepOutput.B) + assert.Nil(t, step.Error) }) } @@ -333,18 +228,12 @@ func TestSetEventSerialize(t *testing.T) { t.Run("SetEventUserDefinedType", func(t *testing.T) { // Start a workflow that sets an event with a user-defined type setHandle, err := RunAsWorkflow(executor, setEventUserDefinedTypeWorkflow, "user-defined-key") - if err != nil { - t.Fatalf("failed to start workflow with user-defined event type: %v", err) - } + require.NoError(t, err) // Wait for the workflow to complete result, err := setHandle.GetResult() - if err != nil { - t.Fatalf("failed to get result from user-defined event workflow: %v", err) - } - if result != "user-defined-event-set" { - t.Fatalf("expected result to be 'user-defined-event-set', got '%s'", result) - } + require.NoError(t, err) + assert.Equal(t, "user-defined-event-set", result) // Retrieve the event to verify it was properly serialized and can be deserialized retrievedEvent, err := GetEvent[UserDefinedEventData](executor, WorkflowGetEventInput{ @@ -352,29 +241,15 @@ func TestSetEventSerialize(t *testing.T) { Key: "user-defined-key", Timeout: 3 * time.Second, }) - if err != nil { - t.Fatalf("failed to get user-defined event: %v", err) - } + require.NoError(t, err) // Verify the retrieved data matches what we set - if retrievedEvent.ID != 42 { - t.Fatalf("expected ID to be 42, got %d", retrievedEvent.ID) - } - if retrievedEvent.Name != "test-event" { - t.Fatalf("expected Name to be 'test-event', got '%s'", retrievedEvent.Name) - } - if retrievedEvent.Details.Description != "This is a test event with user-defined data" { - t.Fatalf("expected Description to be 'This is a test event with user-defined data', got '%s'", retrievedEvent.Details.Description) - } - if len(retrievedEvent.Details.Tags) != 3 { - t.Fatalf("expected 3 tags, got %d", len(retrievedEvent.Details.Tags)) - } + assert.Equal(t, 42, retrievedEvent.ID) + assert.Equal(t, "test-event", retrievedEvent.Name) + assert.Equal(t, "This is a test event with user-defined data", retrievedEvent.Details.Description) + require.Len(t, retrievedEvent.Details.Tags, 3) expectedTags := []string{"test", "user-defined", "serialization"} - for i, tag := range retrievedEvent.Details.Tags { - if tag != expectedTags[i] { - t.Fatalf("expected tag %d to be '%s', got '%s'", i, expectedTags[i], tag) - } - } + assert.Equal(t, expectedTags, retrievedEvent.Details.Tags) }) } @@ -424,51 +299,28 @@ func TestSendSerialize(t *testing.T) { t.Run("SendUserDefinedType", func(t *testing.T) { // Start a receiver workflow first recvHandle, err := RunAsWorkflow(executor, recvUserDefinedTypeWorkflow, "recv-input") - if err != nil { - t.Fatalf("failed to start receive workflow: %v", err) - } + require.NoError(t, err) // Start a sender workflow that sends a message with a user-defined type sendHandle, err := RunAsWorkflow(executor, sendUserDefinedTypeWorkflow, recvHandle.GetWorkflowID()) - if err != nil { - t.Fatalf("failed to start workflow with user-defined send type: %v", err) - } + require.NoError(t, err) // Wait for the sender workflow to complete sendResult, err := sendHandle.GetResult() - if err != nil { - t.Fatalf("failed to get result from user-defined send workflow: %v", err) - } - if sendResult != "user-defined-message-sent" { - t.Fatalf("expected result to be 'user-defined-message-sent', got '%s'", sendResult) - } + require.NoError(t, err) + assert.Equal(t, "user-defined-message-sent", sendResult) // Wait for the receiver workflow to complete and get the message receivedData, err := recvHandle.GetResult() - if err != nil { - t.Fatalf("failed to get result from receive workflow: %v", err) - } + require.NoError(t, err) // Verify the received data matches what we sent - if receivedData.ID != 42 { - t.Fatalf("expected ID to be 42, got %d", receivedData.ID) - } - if receivedData.Name != "test-send-message" { - t.Fatalf("expected Name to be 'test-send-message', got '%s'", receivedData.Name) - } - if receivedData.Details.Description != "This is a test send message with user-defined data" { - t.Fatalf("expected Description to be 'This is a test send message with user-defined data', got '%s'", receivedData.Details.Description) - } + assert.Equal(t, 42, receivedData.ID) + assert.Equal(t, "test-send-message", receivedData.Name) + assert.Equal(t, "This is a test send message with user-defined data", receivedData.Details.Description) // Verify tags expectedTags := []string{"test", "user-defined", "serialization", "send"} - if len(receivedData.Details.Tags) != len(expectedTags) { - t.Fatalf("expected %d tags, got %d", len(expectedTags), len(receivedData.Details.Tags)) - } - for i, tag := range receivedData.Details.Tags { - if tag != expectedTags[i] { - t.Fatalf("expected tag %d to be '%s', got '%s'", i, expectedTags[i], tag) - } - } + assert.Equal(t, expectedTags, receivedData.Details.Tags) }) } diff --git a/dbos/utils_test.go b/dbos/utils_test.go index ccb5add2..7476c956 100644 --- a/dbos/utils_test.go +++ b/dbos/utils_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/jackc/pgx/v5" + "github.com/stretchr/testify/require" "go.uber.org/goleak" ) @@ -33,9 +34,7 @@ func setupDBOS(t *testing.T, dropDB bool, checkLeaks bool) DBOSContext { // Clean up the test database parsedURL, err := pgx.ParseConfig(databaseURL) - if err != nil { - t.Fatalf("failed to parse database URL: %v", err) - } + require.NoError(t, err) dbName := parsedURL.Database if dbName == "" { @@ -45,28 +44,20 @@ func setupDBOS(t *testing.T, dropDB bool, checkLeaks bool) DBOSContext { postgresURL := parsedURL.Copy() postgresURL.Database = "postgres" conn, err := pgx.ConnectConfig(context.Background(), postgresURL) - if err != nil { - t.Fatalf("failed to connect to database: %v", err) - } + require.NoError(t, err) defer conn.Close(context.Background()) if dropDB { _, err = conn.Exec(context.Background(), "DROP DATABASE IF EXISTS "+dbName+" WITH (FORCE)") - if err != nil { - t.Fatalf("failed to drop test database: %v", err) - } + require.NoError(t, err) } dbosCtx, err := NewDBOSContext(Config{ DatabaseURL: databaseURL, AppName: "test-app", }) - if err != nil { - t.Fatalf("failed to create DBOS instance: %v", err) - } - if dbosCtx == nil { - t.Fatal("expected DBOS instance but got nil") - } + require.NoError(t, err) + require.NotNil(t, dbosCtx) // Register cleanup to run after test completes t.Cleanup(func() { diff --git a/dbos/workflows_test.go b/dbos/workflows_test.go index cd87a58e..107c5313 100644 --- a/dbos/workflows_test.go +++ b/dbos/workflows_test.go @@ -21,6 +21,8 @@ import ( "time" "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) // Global counter for idempotency testing @@ -278,19 +280,13 @@ func TestWorkflowsRegistration(t *testing.T) { result, err := tc.workflowFunc(dbosCtx, tc.input, WithWorkflowID(uuid.NewString())) if tc.expectError { - if err == nil { - t.Fatal("expected error but got none") - } - if tc.expectedError != "" && err.Error() != tc.expectedError { - t.Fatalf("expected error %q but got %q", tc.expectedError, err.Error()) + require.Error(t, err, "expected error but got none") + if tc.expectedError != "" { + assert.Equal(t, tc.expectedError, err.Error()) } } else { - if err != nil { - t.Fatalf("expected no error but got: %v", err) - } - if result != tc.expectedResult { - t.Fatalf("expected result %v but got %v", tc.expectedResult, result) - } + require.NoError(t, err) + assert.Equal(t, tc.expectedResult, result) } }) } @@ -305,16 +301,10 @@ func TestWorkflowsRegistration(t *testing.T) { // Second registration of the same workflow should panic with ConflictingRegistrationError defer func() { r := recover() - if r == nil { - t.Fatal("expected panic from double registration but got none") - } + require.NotNil(t, r, "expected panic from double registration but got none") dbosErr, ok := r.(*DBOSError) - if !ok { - t.Fatalf("expected panic to be *DBOSError, got %T", r) - } - if dbosErr.Code != ConflictingRegistrationError { - t.Fatalf("expected ConflictingRegistrationError, got %v", dbosErr.Code) - } + require.True(t, ok, "expected panic to be *DBOSError, got %T", r) + assert.Equal(t, ConflictingRegistrationError, dbosErr.Code) }() RegisterWorkflow(freshCtx, simpleWorkflow) }) @@ -329,16 +319,10 @@ func TestWorkflowsRegistration(t *testing.T) { // Second registration with same custom name should panic with ConflictingRegistrationError defer func() { r := recover() - if r == nil { - t.Fatal("expected panic from double registration with custom name but got none") - } + require.NotNil(t, r, "expected panic from double registration with custom name but got none") dbosErr, ok := r.(*DBOSError) - if !ok { - t.Fatalf("expected panic to be *DBOSError, got %T", r) - } - if dbosErr.Code != ConflictingRegistrationError { - t.Fatalf("expected ConflictingRegistrationError, got %v", dbosErr.Code) - } + require.True(t, ok, "expected panic to be *DBOSError, got %T", r) + assert.Equal(t, ConflictingRegistrationError, dbosErr.Code) }() RegisterWorkflow(freshCtx, simpleWorkflow, WithWorkflowName("custom-workflow")) }) @@ -349,15 +333,13 @@ func TestWorkflowsRegistration(t *testing.T) { // Launch DBOS context err := freshCtx.Launch() - if err != nil { - t.Fatalf("failed to launch DBOS context: %v", err) - } + require.NoError(t, err) defer freshCtx.Cancel() // Attempting to register after launch should panic defer func() { if r := recover(); r == nil { - t.Fatal("expected panic from registration after launch but got none") + require.FailNow(t, "expected panic from registration after launch but got none") } }() RegisterWorkflow(freshCtx, simpleWorkflow) @@ -416,47 +398,29 @@ func TestSteps(t *testing.T) { _, err := RunAsStep(dbosCtx, func(ctx context.Context) (string, error) { return simpleStep(ctx) }) - if err == nil { - t.Fatal("expected error when running step outside of workflow context, but got none") - } + require.Error(t, err, "expected error when running step outside of workflow context, but got none") // Check the error type dbosErr, ok := err.(*DBOSError) - if !ok { - t.Fatalf("expected error to be of type *DBOSError, got %T", err) - } + require.True(t, ok, "expected error to be of type *DBOSError, got %T", err) - if dbosErr.Code != StepExecutionError { - t.Fatalf("expected error code to be StepExecutionError, got %v", dbosErr.Code) - } + require.Equal(t, StepExecutionError, dbosErr.Code, "expected error code to be StepExecutionError, got %v", dbosErr.Code) // Test the specific message from the 3rd argument expectedMessagePart := "workflow state not found in context: are you running this step within a workflow?" - if !strings.Contains(err.Error(), expectedMessagePart) { - t.Fatalf("expected error message to contain %q, but got %q", expectedMessagePart, err.Error()) - } + require.Contains(t, err.Error(), expectedMessagePart, "expected error message to contain %q, but got %q", expectedMessagePart, err.Error()) }) t.Run("StepWithinAStepAreJustFunctions", func(t *testing.T) { handle, err := RunAsWorkflow(dbosCtx, stepWithinAStepWorkflow, "test") - if err != nil { - t.Fatal("failed to run step within a step:", err) - } + require.NoError(t, err, "failed to run step within a step") result, err := handle.GetResult() - if err != nil { - t.Fatal("failed to get result from step within a step:", err) - } - if result != "from step" { - t.Fatalf("expected result 'from step', got '%s'", result) - } + require.NoError(t, err, "failed to get result from step within a step") + assert.Equal(t, "from step", result) steps, err := dbosCtx.(*dbosContext).systemDB.getWorkflowSteps(dbosCtx, handle.GetWorkflowID()) - if err != nil { - t.Fatal("failed to list steps:", err) - } - if len(steps) != 1 { - t.Fatalf("expected 1 step, got %d", len(steps)) - } + require.NoError(t, err, "failed to list steps") + require.Len(t, steps, 1, "expected 1 step, got %d", len(steps)) }) t.Run("StepRetryWithExponentialBackoff", func(t *testing.T) { @@ -466,68 +430,44 @@ func TestSteps(t *testing.T) { // Execute the workflow handle, err := RunAsWorkflow(dbosCtx, stepRetryWorkflow, "test") - if err != nil { - t.Fatal("failed to start retry workflow:", err) - } + require.NoError(t, err, "failed to start retry workflow") _, err = handle.GetResult() - if err == nil { - t.Fatal("expected error from failing workflow but got none") - } + require.Error(t, err, "expected error from failing workflow but got none") // Verify the step was called exactly 6 times (max attempts + 1 initial attempt) - if stepRetryAttemptCount != 6 { - t.Fatalf("expected 6 attempts, got %d", stepRetryAttemptCount) - } + assert.Equal(t, 6, stepRetryAttemptCount, "expected 6 attempts") // Verify the error is a MaxStepRetriesExceeded error dbosErr, ok := err.(*DBOSError) - if !ok { - t.Fatalf("expected error to be of type *DBOSError, got %T", err) - } + require.True(t, ok, "expected error to be of type *DBOSError, got %T", err) - if dbosErr.Code != MaxStepRetriesExceeded { - t.Fatalf("expected error code to be MaxStepRetriesExceeded, got %v", dbosErr.Code) - } + assert.Equal(t, MaxStepRetriesExceeded, dbosErr.Code, "expected error code to be MaxStepRetriesExceeded") // Verify the error contains the step name and max retries expectedErrorMessage := "has exceeded its maximum of 5 retries" - if !strings.Contains(dbosErr.Message, expectedErrorMessage) { - t.Fatalf("expected error message to contain '%s', got '%s'", expectedErrorMessage, dbosErr.Message) - } + assert.Contains(t, dbosErr.Message, expectedErrorMessage, "expected error message to contain expected text") // Verify each error message is present in the joined error for i := 1; i <= 5; i++ { expectedMsg := fmt.Sprintf("always fails - attempt %d", i) - if !strings.Contains(dbosErr.Error(), expectedMsg) { - t.Fatalf("expected joined error to contain '%s', but got '%s'", expectedMsg, dbosErr.Error()) - } + assert.Contains(t, dbosErr.Error(), expectedMsg, "expected joined error to contain expected message") } // Verify that the failed step was still recorded in the database steps, err := dbosCtx.(*dbosContext).systemDB.getWorkflowSteps(dbosCtx, handle.GetWorkflowID()) - if err != nil { - t.Fatal("failed to get workflow steps:", err) - } + require.NoError(t, err, "failed to get workflow steps") - if len(steps) != 2 { - t.Fatalf("expected 2 recorded step, got %d", len(steps)) - } + require.Len(t, steps, 2, "expected 2 recorded steps") // Verify the second step has the error step := steps[1] - if step.Error == nil { - t.Fatal("expected error in recorded step, got none") - } + require.NotNil(t, step.Error, "expected error in recorded step, got none") - if step.Error.Error() != dbosErr.Error() { - t.Fatalf("expected recorded step error to match joined error, got '%s', expected '%s'", step.Error.Error(), dbosErr.Error()) - } + assert.Equal(t, dbosErr.Error(), step.Error.Error(), "expected recorded step error to match joined error") // Verify the idempotency step was executed only once - if stepIdempotencyCounter != 1 { - t.Fatalf("expected idempotency step to be executed only once, got %d", stepIdempotencyCounter) - } + assert.Equal(t, 1, stepIdempotencyCounter, "expected idempotency step to be executed only once") }) } @@ -767,7 +707,7 @@ func TestChildWorkflow(t *testing.T) { t.Fatalf("failed to get workflow steps: %v", err) } if len(steps) != 2 { - t.Fatalf("expected 2 recorded steps, got %d", len(steps)) + require.Len(t, steps, 2, "expected 2 recorded steps, got %d", len(steps)) } // Verify first step is the child workflow with stepID=0 @@ -860,7 +800,7 @@ func TestChildWorkflow(t *testing.T) { // Should have recovered both parent and child workflows if len(recoveredHandles) != 2 { - t.Fatalf("expected 2 recovered handles (parent and child), got %d", len(recoveredHandles)) + require.Len(t, recoveredHandles, 2, "expected 2 recovered handles (parent and child), got %d", len(recoveredHandles)) } // Find the child handle and verify it's a polling handle with the correct ID @@ -872,9 +812,7 @@ func TestChildWorkflow(t *testing.T) { } } - if childRecoveredHandle == nil { - t.Fatalf("failed to find recovered child workflow handle with ID %s", knownChildID) - } + require.NotNil(t, childRecoveredHandle, "failed to find recovered child workflow handle with ID %s", knownChildID) // Complete both workflows pollingHandleCompleteEvent.Set() @@ -1003,7 +941,7 @@ func TestWorkflowRecovery(t *testing.T) { // Check that we have a single handle in the return list if len(recoveredHandles) != 1 { - t.Fatalf("expected 1 recovered handle, got %d", len(recoveredHandles)) + require.Len(t, recoveredHandles, 1, "expected 1 recovered handle, got %d", len(recoveredHandles)) } // Check that the workflow ID from the handle is the same as the first handle @@ -1033,7 +971,7 @@ func TestWorkflowRecovery(t *testing.T) { } if len(workflows) != 1 { - t.Fatalf("expected 1 workflow, got %d", len(workflows)) + require.Len(t, workflows, 1, "expected 1 workflow, got %d", len(workflows)) } workflow := workflows[0] @@ -1101,22 +1039,16 @@ func TestWorkflowDeadLetterQueue(t *testing.T) { // Attempt to recover the blocked workflow the maximum number of times for i := range maxRecoveryAttempts { _, err := recoverPendingWorkflows(dbosCtx.(*dbosContext), []string{"local"}) - if err != nil { - t.Fatalf("failed to recover pending workflows on attempt %d: %v", i+1, err) - } + require.NoError(t, err, "failed to recover pending workflows on attempt %d", i+1) deadLetterQueueStartEvent.Wait() deadLetterQueueStartEvent.Clear() expectedCount := int64(i + 2) // +1 for initial execution, +1 for each recovery - if recoveryCount != expectedCount { - t.Fatalf("expected recovery count to be %d, got %d", expectedCount, recoveryCount) - } + require.Equal(t, expectedCount, recoveryCount, "expected recovery count to be %d, got %d", expectedCount, recoveryCount) } // Verify an additional attempt throws a DLQ error and puts the workflow in the DLQ status _, err = recoverPendingWorkflows(dbosCtx.(*dbosContext), []string{"local"}) - if err == nil { - t.Fatal("expected dead letter queue error but got none") - } + require.Error(t, err, "expected dead letter queue error but got none") dbosErr, ok := err.(*DBOSError) if !ok { @@ -1137,9 +1069,7 @@ func TestWorkflowDeadLetterQueue(t *testing.T) { // Verify that attempting to start a workflow with the same ID throws a DLQ error _, err = RunAsWorkflow(dbosCtx, deadLetterQueueWorkflow, "test", WithWorkflowID(wfID)) - if err == nil { - t.Fatal("expected dead letter queue error when restarting workflow with same ID but got none") - } + require.Error(t, err, "expected dead letter queue error when restarting workflow with same ID but got none") dbosErr, ok = err.(*DBOSError) if !ok { @@ -1314,8 +1244,7 @@ func TestScheduledWorkflows(t *testing.T) { // Check if delta is within acceptable slack if delta > allowedSlack { - t.Fatalf("Execution %d timing deviation too large: expected around %v, got %v (delta: %v, allowed slack: %v)", - i+1, expectedTime, execTime, delta, allowedSlack) + t.Fatalf("Execution %d timing deviation too large: expected around %v, got %v (delta: %v, allowed slack: %v)", i+1, execTime, delta, allowedSlack, expectedTime) } t.Logf("Execution %d: expected %v, actual %v, delta %v", i+1, expectedTime, execTime, delta) @@ -1505,11 +1434,11 @@ func TestSendRecv(t *testing.T) { t.Fatalf("failed to get workflow steps for send workflow: %v", err) } if len(sendSteps) != 3 { - t.Fatalf("expected 3 steps in send workflow (3 Send calls), got %d", len(sendSteps)) + require.Len(t, sendSteps, 3, "expected 3 steps in send workflow (3 Send calls), got %d", len(sendSteps)) } for i, step := range sendSteps { if step.StepID != i { - t.Fatalf("expected step %d to have StepID %d, got %d", i, i, step.StepID) + t.Fatalf("expected step %d to have StepID %d, got %d", i, step.StepID, i) } if step.StepName != "DBOS.send" { t.Fatalf("expected step %d to have StepName 'DBOS.send', got '%s'", i, step.StepName) @@ -1522,11 +1451,11 @@ func TestSendRecv(t *testing.T) { t.Fatalf("failed to get workflow steps for receive workflow: %v", err) } if len(receiveSteps) != 3 { - t.Fatalf("expected 3 steps in receive workflow (3 Recv calls), got %d", len(receiveSteps)) + require.Len(t, receiveSteps, 3, "expected 3 steps in receive workflow (3 Recv calls), got %d", len(receiveSteps)) } for i, step := range receiveSteps { if step.StepID != i { - t.Fatalf("expected step %d to have StepID %d, got %d", i, i, step.StepID) + t.Fatalf("expected step %d to have StepID %d, got %d", i, step.StepID, i) } if step.StepName != "DBOS.recv" { t.Fatalf("expected step %d to have StepName 'DBOS.recv', got '%s'", i, step.StepName) @@ -1572,7 +1501,7 @@ func TestSendRecv(t *testing.T) { t.Fatalf("failed to get workflow steps for send struct workflow: %v", err) } if len(sendSteps) != 1 { - t.Fatalf("expected 1 step in send struct workflow (1 Send call), got %d", len(sendSteps)) + require.Len(t, sendSteps, 1, "expected 1 step in send struct workflow (1 Send call), got %d", len(sendSteps)) } if sendSteps[0].StepID != 0 { t.Fatalf("expected step to have StepID 0, got %d", sendSteps[0].StepID) @@ -1587,7 +1516,7 @@ func TestSendRecv(t *testing.T) { t.Fatalf("failed to get workflow steps for receive struct workflow: %v", err) } if len(receiveSteps) != 1 { - t.Fatalf("expected 1 step in receive struct workflow (1 Recv call), got %d", len(receiveSteps)) + require.Len(t, receiveSteps, 1, "expected 1 step in receive struct workflow (1 Recv call), got %d", len(receiveSteps)) } if receiveSteps[0].StepID != 0 { t.Fatalf("expected step to have StepID 0, got %d", receiveSteps[0].StepID) @@ -1611,9 +1540,7 @@ func TestSendRecv(t *testing.T) { } _, err = handle.GetResult() - if err == nil { - t.Fatal("expected error when sending to non-existent UUID but got none") - } + require.Error(t, err, "expected error when sending to non-existent UUID but got none") dbosErr, ok := err.(*DBOSError) if !ok { @@ -1648,9 +1575,7 @@ func TestSendRecv(t *testing.T) { t.Run("RecvMustRunInsideWorkflows", func(t *testing.T) { // Attempt to run Recv outside of a workflow context _, err := Recv[string](dbosCtx, WorkflowRecvInput{Topic: "test-topic", Timeout: 1 * time.Second}) - if err == nil { - t.Fatal("expected error when running Recv outside of workflow context, but got none") - } + require.Error(t, err, "expected error when running Recv outside of workflow context, but got none") // Check the error type dbosErr, ok := err.(*DBOSError) @@ -1703,11 +1628,11 @@ func TestSendRecv(t *testing.T) { t.Fatalf("failed to get workflow steps for receive workflow: %v", err) } if len(receiveSteps) != 3 { - t.Fatalf("expected 3 steps in receive workflow (3 Recv calls), got %d", len(receiveSteps)) + require.Len(t, receiveSteps, 3, "expected 3 steps in receive workflow (3 Recv calls), got %d", len(receiveSteps)) } for i, step := range receiveSteps { if step.StepID != i { - t.Fatalf("expected step %d to have StepID %d, got %d", i, i, step.StepID) + t.Fatalf("expected step %d to have StepID %d, got %d", i, step.StepID, i) } if step.StepName != "DBOS.recv" { t.Fatalf("expected step %d to have StepName 'DBOS.recv', got '%s'", i, step.StepName) @@ -1739,14 +1664,14 @@ func TestSendRecv(t *testing.T) { t.Fatalf("failed to recover pending workflows: %v", err) } if len(recoveredHandles) != 2 { - t.Fatalf("expected 2 recovered handles, got %d", len(recoveredHandles)) + require.Len(t, recoveredHandles, 2, "expected 2 recovered handles, got %d", len(recoveredHandles)) } steps, err := dbosCtx.(*dbosContext).systemDB.getWorkflowSteps(dbosCtx, sendHandle.GetWorkflowID()) if err != nil { t.Fatalf("failed to get workflow steps: %v", err) } if len(steps) != 1 { - t.Fatalf("expected 1 step in send idempotency workflow, got %d", len(steps)) + require.Len(t, steps, 1, "expected 1 step in send idempotency workflow, got %d", len(steps)) } if steps[0].StepID != 0 { t.Fatalf("expected send idempotency step to have StepID 0, got %d", steps[0].StepID) @@ -1759,7 +1684,7 @@ func TestSendRecv(t *testing.T) { t.Fatalf("failed to get steps for receive idempotency workflow: %v", err) } if len(steps) != 1 { - t.Fatalf("expected 1 step in receive idempotency workflow, got %d", len(steps)) + require.Len(t, steps, 1, "expected 1 step in receive idempotency workflow, got %d", len(steps)) } if steps[0].StepID != 0 { t.Fatalf("expected receive idempotency step to have StepID 0, got %d", steps[0].StepID) @@ -1805,9 +1730,7 @@ func TestSendRecv(t *testing.T) { // Expect the workflow to fail with the specific error _, err = handle.GetResult() - if err == nil { - t.Fatal("expected error when calling Send within a step, but got none") - } + require.Error(t, err, "expected error when calling Send within a step, but got none") // Check the error type dbosErr, ok := err.(*DBOSError) @@ -2067,9 +1990,7 @@ func TestWorkflowExecutionMismatch(t *testing.T) { // Now try to run conflictWorkflowB with the same workflow ID // This should return a ConflictingWorkflowError _, err = RunAsWorkflow(dbosCtx, conflictWorkflowB, "test-input", WithWorkflowID(workflowID)) - if err == nil { - t.Fatal("expected ConflictingWorkflowError when running different workflow with same ID, but got none") - } + require.Error(t, err, "expected ConflictingWorkflowError when running different workflow with same ID, but got none") // Check that it's the correct error type dbosErr, ok := err.(*DBOSError) @@ -2112,9 +2033,7 @@ func TestWorkflowExecutionMismatch(t *testing.T) { stepName: wrongStepName, }) - if err == nil { - t.Fatal("expected UnexpectedStep error when checking operation with wrong step name, but got none") - } + require.Error(t, err, "expected UnexpectedStep error when checking operation with wrong step name, but got none") // Check that it's the correct error type dbosErr, ok := err.(*DBOSError) @@ -2213,11 +2132,11 @@ func TestSetGetEvent(t *testing.T) { t.Fatalf("failed to get workflow steps for set two events workflow: %v", err) } if len(setSteps) != 2 { - t.Fatalf("expected 2 steps in set two events workflow (2 SetEvent calls), got %d", len(setSteps)) + require.Len(t, setSteps, 2, "expected 2 steps in set two events workflow (2 SetEvent calls), got %d", len(setSteps)) } for i, step := range setSteps { if step.StepID != i { - t.Fatalf("expected step %d to have StepID %d, got %d", i, i, step.StepID) + t.Fatalf("expected step %d to have StepID %d, got %d", i, step.StepID, i) } if step.StepName != "DBOS.setEvent" { t.Fatalf("expected step %d to have StepName 'DBOS.setEvent', got '%s'", i, step.StepName) @@ -2230,7 +2149,7 @@ func TestSetGetEvent(t *testing.T) { t.Fatalf("failed to get workflow steps for get first event workflow: %v", err) } if len(getFirstSteps) != 1 { - t.Fatalf("expected 1 step in get first event workflow (1 GetEvent call), got %d", len(getFirstSteps)) + require.Len(t, getFirstSteps, 1, "expected 1 step in get first event workflow (1 GetEvent call), got %d", len(getFirstSteps)) } if getFirstSteps[0].StepID != 0 { t.Fatalf("expected step to have StepID 0, got %d", getFirstSteps[0].StepID) @@ -2245,7 +2164,7 @@ func TestSetGetEvent(t *testing.T) { t.Fatalf("failed to get workflow steps for get second event workflow: %v", err) } if len(getSecondSteps) != 1 { - t.Fatalf("expected 1 step in get second event workflow (1 GetEvent call), got %d", len(getSecondSteps)) + require.Len(t, getSecondSteps, 1, "expected 1 step in get second event workflow (1 GetEvent call), got %d", len(getSecondSteps)) } if getSecondSteps[0].StepID != 0 { t.Fatalf("expected step to have StepID 0, got %d", getSecondSteps[0].StepID) @@ -2290,7 +2209,7 @@ func TestSetGetEvent(t *testing.T) { t.Fatalf("failed to get workflow steps for set event workflow: %v", err) } if len(setSteps) != 1 { - t.Fatalf("expected 1 step in set event workflow (1 SetEvent call), got %d", len(setSteps)) + require.Len(t, setSteps, 1, "expected 1 step in set event workflow (1 SetEvent call), got %d", len(setSteps)) } if setSteps[0].StepID != 0 { t.Fatalf("expected step to have StepID 0, got %d", setSteps[0].StepID) @@ -2308,9 +2227,7 @@ func TestSetGetEvent(t *testing.T) { Key: "test-key", Timeout: 3 * time.Second, }) - if err != nil { - t.Fatal("failed to get event from non-existent workflow:", err) - } + require.NoError(t, err, "failed to get event from non-existent workflow") if message != "" { t.Fatalf("expected empty result on timeout, got '%s'", message) } @@ -2320,21 +2237,15 @@ func TestSetGetEvent(t *testing.T) { Key: "test-key", Message: "test-message", }) - if err != nil { - t.Fatal("failed to set event:", err) - } + require.NoError(t, err, "failed to set event") _, err = setHandle.GetResult() - if err != nil { - t.Fatal("failed to get result from set event workflow:", err) - } + require.NoError(t, err, "failed to get result from set event workflow") message, err = GetEvent[string](dbosCtx, WorkflowGetEventInput{ TargetWorkflowID: setHandle.GetWorkflowID(), Key: "non-existent-key", Timeout: 3 * time.Second, }) - if err != nil { - t.Fatal("failed to get event with non-existent key:", err) - } + require.NoError(t, err, "failed to get event with non-existent key") if message != "" { t.Fatalf("expected empty result on timeout with non-existent key, got '%s'", message) } @@ -2343,9 +2254,7 @@ func TestSetGetEvent(t *testing.T) { t.Run("SetGetEventMustRunInsideWorkflows", func(t *testing.T) { // Attempt to run SetEvent outside of a workflow context err := SetEvent(dbosCtx, GenericWorkflowSetEventInput[string]{Key: "test-key", Message: "test-message"}) - if err == nil { - t.Fatal("expected error when running SetEvent outside of workflow context, but got none") - } + require.Error(t, err, "expected error when running SetEvent outside of workflow context, but got none") // Check the error type dbosErr, ok := err.(*DBOSError) @@ -2395,7 +2304,7 @@ func TestSetGetEvent(t *testing.T) { t.Fatalf("failed to recover pending workflows: %v", err) } if len(recoveredHandles) != 2 { - t.Fatalf("expected 2 recovered handles, got %d", len(recoveredHandles)) + require.Len(t, recoveredHandles, 2, "expected 2 recovered handles, got %d", len(recoveredHandles)) } getEventStartIdempotencyEvent.Wait() @@ -2407,7 +2316,7 @@ func TestSetGetEvent(t *testing.T) { t.Fatalf("failed to get steps for set event idempotency workflow: %v", err) } if len(setSteps) != 1 { - t.Fatalf("expected 1 step in set event idempotency workflow, got %d", len(setSteps)) + require.Len(t, setSteps, 1, "expected 1 step in set event idempotency workflow, got %d", len(setSteps)) } if setSteps[0].StepID != 0 { t.Fatalf("expected set event idempotency step to have StepID 0, got %d", setSteps[0].StepID) @@ -2421,7 +2330,7 @@ func TestSetGetEvent(t *testing.T) { t.Fatalf("failed to get steps for get event idempotency workflow: %v", err) } if len(getSteps) != 1 { - t.Fatalf("expected 1 step in get event idempotency workflow, got %d", len(getSteps)) + require.Len(t, getSteps, 1, "expected 1 step in get event idempotency workflow, got %d", len(getSteps)) } if getSteps[0].StepID != 0 { t.Fatalf("expected get event idempotency step to have StepID 0, got %d", getSteps[0].StepID) @@ -2517,7 +2426,7 @@ func TestSetGetEvent(t *testing.T) { // Check for any errors from goroutines for err := range errors { - t.Fatal(err) + require.FailNow(t, "goroutine error: %v", err) } }) } @@ -2578,7 +2487,7 @@ func TestSleep(t *testing.T) { } if len(steps) != 1 { - t.Fatalf("expected 1 step (the sleep), got %d", len(steps)) + require.Len(t, steps, 1, "expected 1 step (the sleep), got %d", len(steps)) } step := steps[0] @@ -2604,9 +2513,7 @@ func TestSleep(t *testing.T) { t.Run("SleepCannotBeCalledOutsideWorkflow", func(t *testing.T) { // Attempt to call Sleep outside of a workflow context _, err := Sleep(dbosCtx, 1*time.Second) - if err == nil { - t.Fatal("expected error when calling Sleep outside of workflow context, but got none") - } + require.Error(t, err, "expected error when calling Sleep outside of workflow context, but got none") // Check the error type dbosErr, ok := err.(*DBOSError) @@ -2965,7 +2872,7 @@ func TestWorkflowTimeout(t *testing.T) { t.Fatalf("failed to recover pending workflows: %v", err) } if len(recoveredHandles) != 1 { - t.Fatalf("expected 1 recovered handle, got %d", len(recoveredHandles)) + require.Len(t, recoveredHandles, 1, "expected 1 recovered handle, got %d", len(recoveredHandles)) } recoveredHandle := recoveredHandles[0] if recoveredHandle.GetWorkflowID() != handle.GetWorkflowID() { @@ -3000,7 +2907,7 @@ func TestWorkflowTimeout(t *testing.T) { // XXX this might be flaky and frankly not super useful expectedDeadline := start.Add(timeout * 10 / 100) if status.Deadline.Before(expectedDeadline) || status.Deadline.After(start.Add(timeout)) { - t.Fatalf("expected workflow deadline to be within %v and %v, got %v", expectedDeadline, start.Add(timeout), status.Deadline) + t.Fatalf("expected workflow deadline to be within %v and %v, got %v", expectedDeadline, status.Deadline, start.Add(timeout)) } }) } diff --git a/go.mod b/go.mod index 102e43e4..2b3e463f 100644 --- a/go.mod +++ b/go.mod @@ -8,18 +8,21 @@ require ( github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa github.com/jackc/pgx/v5 v5.7.5 github.com/robfig/cron/v3 v3.0.1 + github.com/stretchr/testify v1.10.0 + go.uber.org/goleak v1.3.0 ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect - github.com/stretchr/testify v1.10.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect go.uber.org/atomic v1.9.0 // indirect - go.uber.org/goleak v1.3.0 // indirect golang.org/x/crypto v0.39.0 // indirect golang.org/x/sync v0.15.0 // indirect golang.org/x/text v0.26.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 7227b3fd..c88bce93 100644 --- a/go.sum +++ b/go.sum @@ -42,6 +42,10 @@ github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs= github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= @@ -60,6 +64,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -86,6 +92,8 @@ golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From ef728d97dc1ba093c8f694e4b47430f3c4f6d831 Mon Sep 17 00:00:00 2001 From: maxdml Date: Wed, 13 Aug 2025 12:32:42 -0700 Subject: [PATCH 02/10] small --- dbos/queues_test.go | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/dbos/queues_test.go b/dbos/queues_test.go index ed5307a1..b4fdc3f6 100644 --- a/dbos/queues_test.go +++ b/dbos/queues_test.go @@ -304,9 +304,7 @@ func TestQueueRecovery(t *testing.T) { // Recover the workflow, then resume it. recoveryHandles, err := recoverPendingWorkflows(dbosCtx.(*dbosContext), []string{"local"}) - if err != nil { - require.NoError(t, err, "failed to recover pending workflows") - } + require.NoError(t, err, "failed to recover pending workflows") for _, e := range recoveryStepEvents { e.Wait() @@ -571,9 +569,7 @@ func TestWorkerConcurrencyXRecovery(t *testing.T) { // Now, manually call the recoverPendingWorkflows method recoveryHandles, err := recoverPendingWorkflows(dbosCtx.(*dbosContext), []string{"local"}) - if err != nil { - require.NoError(t, err, "failed to recover pending workflows") - } + require.NoError(t, err, "failed to recover pending workflows") // You should get 1 handle associated with the first workflow assert.Len(t, recoveryHandles, 1, "expected 1 recovery handle") @@ -749,15 +745,11 @@ func TestQueueTimeouts(t *testing.T) { // This workflow will enqueue a workflow that is not cancelable childCtx := WithoutCancel(ctx) handle, err := RunAsWorkflow(childCtx, detachedWorkflow, timeout*2, WithQueue(timeoutQueue.Name)) - if err != nil { - require.NoError(t, err, "failed to start enqueued detached workflow") - } + require.NoError(t, err, "failed to start enqueued detached workflow") // Wait for the enqueued workflow to complete result, err := handle.GetResult() - if err != nil { - require.NoError(t, err, "failed to get result from enqueued detached workflow") - } + require.NoError(t, err, "failed to get result from enqueued detached workflow") if result != "detached-workflow-completed" { assert.Equal(t, "detached-workflow-completed", result, "expected result to be 'detached-workflow-completed'") } @@ -836,15 +828,11 @@ func TestQueueTimeouts(t *testing.T) { defer cancelFunc() // Ensure we clean up the context handle, err := RunAsWorkflow(cancelCtx, enqueuedWorkflowEnqueuesADetachedWorkflow, timeout, WithQueue(timeoutQueue.Name)) - if err != nil { - require.NoError(t, err, "failed to start enqueued detached workflow") - } + require.NoError(t, err, "failed to start enqueued detached workflow") // Wait for the workflow to complete and get the result result, err := handle.GetResult() - if err == nil { - require.Error(t, err, "expected error but got none") - } + require.Error(t, err, "expected error but got none") // Check the error type dbosErr, ok := err.(*DBOSError) From c6f5de60cccd5f53ce7f00b8bd0396ca7b2219fa Mon Sep 17 00:00:00 2001 From: Max dml Date: Wed, 13 Aug 2025 12:48:09 -0700 Subject: [PATCH 03/10] Update dbos/workflows_test.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- dbos/workflows_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbos/workflows_test.go b/dbos/workflows_test.go index 107c5313..93c56589 100644 --- a/dbos/workflows_test.go +++ b/dbos/workflows_test.go @@ -339,7 +339,7 @@ func TestWorkflowsRegistration(t *testing.T) { // Attempting to register after launch should panic defer func() { if r := recover(); r == nil { - require.FailNow(t, "expected panic from registration after launch but got none") + t.Fatal("expected panic from registration after launch but got none") } }() RegisterWorkflow(freshCtx, simpleWorkflow) From cbe644f7262a1e394c3474a96ca1679b5a893655 Mon Sep 17 00:00:00 2001 From: Max dml Date: Wed, 13 Aug 2025 12:48:40 -0700 Subject: [PATCH 04/10] Update dbos/workflows_test.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- dbos/workflows_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbos/workflows_test.go b/dbos/workflows_test.go index 93c56589..937502c2 100644 --- a/dbos/workflows_test.go +++ b/dbos/workflows_test.go @@ -1244,7 +1244,7 @@ func TestScheduledWorkflows(t *testing.T) { // Check if delta is within acceptable slack if delta > allowedSlack { - t.Fatalf("Execution %d timing deviation too large: expected around %v, got %v (delta: %v, allowed slack: %v)", i+1, execTime, delta, allowedSlack, expectedTime) + t.Fatalf("Execution %d timing deviation too large: expected around %v, got %v (delta: %v, allowed slack: %v)", i+1, expectedTime, execTime, delta, allowedSlack) } t.Logf("Execution %d: expected %v, actual %v, delta %v", i+1, expectedTime, execTime, delta) From 79093be1a11acbaa017dbcd65def63a22bdcc560 Mon Sep 17 00:00:00 2001 From: Max dml Date: Wed, 13 Aug 2025 12:57:18 -0700 Subject: [PATCH 05/10] Update dbos/workflows_test.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- dbos/workflows_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbos/workflows_test.go b/dbos/workflows_test.go index 937502c2..fb4dd1c7 100644 --- a/dbos/workflows_test.go +++ b/dbos/workflows_test.go @@ -2907,7 +2907,7 @@ func TestWorkflowTimeout(t *testing.T) { // XXX this might be flaky and frankly not super useful expectedDeadline := start.Add(timeout * 10 / 100) if status.Deadline.Before(expectedDeadline) || status.Deadline.After(start.Add(timeout)) { - t.Fatalf("expected workflow deadline to be within %v and %v, got %v", expectedDeadline, status.Deadline, start.Add(timeout)) + t.Fatalf("expected workflow deadline to be within %v and %v, got %v", expectedDeadline, start.Add(timeout), status.Deadline) } }) } From 142bd703e14e2e7f451ef6c0835cb94cdad37c47 Mon Sep 17 00:00:00 2001 From: Max dml Date: Wed, 13 Aug 2025 12:57:38 -0700 Subject: [PATCH 06/10] Update dbos/queues_test.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- dbos/queues_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbos/queues_test.go b/dbos/queues_test.go index b4fdc3f6..fcfd44d9 100644 --- a/dbos/queues_test.go +++ b/dbos/queues_test.go @@ -702,7 +702,7 @@ func TestQueueTimeouts(t *testing.T) { // This workflow will wait indefinitely until it is cancelled <-ctx.Done() if !errors.Is(ctx.Err(), context.Canceled) && !errors.Is(ctx.Err(), context.DeadlineExceeded) { - require.True(t, errors.Is(ctx.Err(), context.Canceled) || errors.Is(ctx.Err(), context.DeadlineExceeded), "workflow was cancelled, but context error is not context.Canceled nor context.DeadlineExceeded: %v", ctx.Err()) + assert.True(t, errors.Is(ctx.Err(), context.Canceled) || errors.Is(ctx.Err(), context.DeadlineExceeded), "workflow was cancelled, but context error is not context.Canceled nor context.DeadlineExceeded: %v", ctx.Err()) } return "", ctx.Err() } From fb1e2f3efd57678f8ec970dce1d0315a34e12e10 Mon Sep 17 00:00:00 2001 From: maxdml Date: Wed, 13 Aug 2025 12:58:54 -0700 Subject: [PATCH 07/10] nit --- dbos/queues_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dbos/queues_test.go b/dbos/queues_test.go index fcfd44d9..694d7443 100644 --- a/dbos/queues_test.go +++ b/dbos/queues_test.go @@ -849,8 +849,6 @@ func TestQueueTimeouts(t *testing.T) { assert.Equal(t, WorkflowStatusCancelled, status.Status, "expected enqueued detached workflow status to be WorkflowStatusCancelled") } - if !queueEntriesAreCleanedUp(dbosCtx) { - require.True(t, queueEntriesAreCleanedUp(dbosCtx), "expected queue entries to be cleaned up after workflow completion, but they are not") - } + require.True(t, queueEntriesAreCleanedUp(dbosCtx), "expected queue entries to be cleaned up after workflow cancellation, but they are not") }) } From 0080a080dd0ea0fef7a3ad96f9f62c5e44330965 Mon Sep 17 00:00:00 2001 From: maxdml Date: Wed, 13 Aug 2025 13:57:48 -0700 Subject: [PATCH 08/10] nits --- dbos/queues_test.go | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/dbos/queues_test.go b/dbos/queues_test.go index 694d7443..3acc5053 100644 --- a/dbos/queues_test.go +++ b/dbos/queues_test.go @@ -337,9 +337,7 @@ func TestQueueRecovery(t *testing.T) { require.NoError(t, err, "failed to rerun workflow") rerunResult, err := rerunHandle.GetResult() require.NoError(t, err, "failed to get result from rerun handle") - if !equal(rerunResult, expectedResult) { - assert.True(t, equal(rerunResult, expectedResult), "expected result %v, got %v", expectedResult, rerunResult) - } + assert.True(t, equal(rerunResult, expectedResult), "expected result %v, got %v", expectedResult, rerunResult) assert.Equal(t, int64(queuedSteps*2), atomic.LoadInt64(&recoveryStepCounter), "expected recoveryStepCounter to remain %d", queuedSteps*2) @@ -711,14 +709,10 @@ func TestQueueTimeouts(t *testing.T) { enqueuedWorkflowEnqueuesATimeoutWorkflow := func(ctx DBOSContext, _ string) (string, error) { // This workflow will enqueue a workflow that waits indefinitely until it is cancelled handle, err := RunAsWorkflow(ctx, queuedWaitForCancelWorkflow, "enqueued-wait-for-cancel", WithQueue(timeoutQueue.Name)) - if err != nil { - require.NoError(t, err, "failed to start enqueued wait for cancel workflow") - } + require.NoError(t, err, "failed to start enqueued wait for cancel workflow") // Workflow should get AwaitedWorkflowCancelled DBOSError _, err = handle.GetResult() - if err == nil { - require.Error(t, err, "expected error when waiting for enqueued workflow to complete, but got none") - } + require.Error(t, err, "expected error when waiting for enqueued workflow to complete, but got none") dbosErr, ok := err.(*DBOSError) require.True(t, ok, "expected error to be of type *DBOSError, got %T", err) assert.Equal(t, AwaitedWorkflowCancelled, dbosErr.Code, "expected error code to be AwaitedWorkflowCancelled") @@ -750,9 +744,7 @@ func TestQueueTimeouts(t *testing.T) { // Wait for the enqueued workflow to complete result, err := handle.GetResult() require.NoError(t, err, "failed to get result from enqueued detached workflow") - if result != "detached-workflow-completed" { - assert.Equal(t, "detached-workflow-completed", result, "expected result to be 'detached-workflow-completed'") - } + assert.Equal(t, "detached-workflow-completed", result, "expected result to be 'detached-workflow-completed'") // Check the workflow status: should be success status, err := handle.GetStatus() require.NoError(t, err, "failed to get enqueued detached workflow status") @@ -845,9 +837,7 @@ func TestQueueTimeouts(t *testing.T) { // Check the workflow status: should be cancelled status, err := handle.GetStatus() require.NoError(t, err, "failed to get enqueued detached workflow status") - if status.Status != WorkflowStatusCancelled { - assert.Equal(t, WorkflowStatusCancelled, status.Status, "expected enqueued detached workflow status to be WorkflowStatusCancelled") - } + assert.Equal(t, WorkflowStatusCancelled, status.Status, "expected enqueued detached workflow status to be WorkflowStatusCancelled") require.True(t, queueEntriesAreCleanedUp(dbosCtx), "expected queue entries to be cleaned up after workflow cancellation, but they are not") }) From eb7fc69074c283ab7c87a435653bdaeda621b9ef Mon Sep 17 00:00:00 2001 From: maxdml Date: Wed, 13 Aug 2025 14:21:08 -0700 Subject: [PATCH 09/10] flakyness --- dbos/client_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbos/client_test.go b/dbos/client_test.go index c2cbd4c7..82e2835c 100644 --- a/dbos/client_test.go +++ b/dbos/client_test.go @@ -312,7 +312,7 @@ func TestCancelResume(t *testing.T) { assert.False(t, resumeStatus.Deadline.Equal(originalDeadline), "expected deadline to be reset after resume, but it remained the same: %v", originalDeadline) // The new deadline should be after resumeStart + workflowTimeout - expectedDeadline := resumeStart.Add(workflowTimeout) + expectedDeadline := resumeStart.Add(workflowTimeout + 100*time.Millisecond) // 100ms buffer assert.False(t, resumeStatus.Deadline.Before(expectedDeadline), "deadline %v is too early (expected around %v)", resumeStatus.Deadline, expectedDeadline) // Wait for the workflow to complete From c19a0d06cc25a0eb8df69b6e59d5d1d5b83a86ef Mon Sep 17 00:00:00 2001 From: maxdml Date: Wed, 13 Aug 2025 14:29:21 -0700 Subject: [PATCH 10/10] flakyness --- dbos/client_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dbos/client_test.go b/dbos/client_test.go index 82e2835c..f0ae5017 100644 --- a/dbos/client_test.go +++ b/dbos/client_test.go @@ -300,9 +300,9 @@ func TestCancelResume(t *testing.T) { originalDeadline := cancelStatus.Deadline // Resume the workflow - resumeStart := time.Now() resumeHandle, err := ResumeWorkflow[string](clientCtx, workflowID) require.NoError(t, err, "failed to resume workflow") + resumeStart := time.Now() // Get status after resume to check the deadline resumeStatus, err := resumeHandle.GetStatus() @@ -312,8 +312,8 @@ func TestCancelResume(t *testing.T) { assert.False(t, resumeStatus.Deadline.Equal(originalDeadline), "expected deadline to be reset after resume, but it remained the same: %v", originalDeadline) // The new deadline should be after resumeStart + workflowTimeout - expectedDeadline := resumeStart.Add(workflowTimeout + 100*time.Millisecond) // 100ms buffer - assert.False(t, resumeStatus.Deadline.Before(expectedDeadline), "deadline %v is too early (expected around %v)", resumeStatus.Deadline, expectedDeadline) + expectedDeadline := resumeStart.Add(workflowTimeout - 100*time.Millisecond) // Allow some leeway for processing time + assert.True(t, resumeStatus.Deadline.After(expectedDeadline), "deadline %v is too early (expected around %v)", resumeStatus.Deadline, expectedDeadline) // Wait for the workflow to complete _, err = resumeHandle.GetResult()