Improves HTTP context loss error handling (#7187)
* Remove bookmark-based resumption logic for lost HTTP context scenarios in `WriteHttpResponse` and `WriteFileHttpResponse`. Simplify error handling by throwing descriptive exceptions for workflows resuming without an available HTTP context. * Add integration tests for HTTP context loss scenarios in `WriteHttpResponse` and `WriteFileHttpResponse`. * Add integration tests for handling HTTP context loss in response activities. * Replace bookmark-based logic with fault exceptions in tests for `WriteHttpResponse` and `WriteFileHttpResponse` when HTTP context is unavailable. * Update agent-logs/http-context-loss-error-messaging.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update test/integration/Elsa.Http.IntegrationTests/Elsa.Http.IntegrationTests.csproj Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
0e33270267
commit
ad87ab96fd
7
Elsa.sln
7
Elsa.sln
|
|
@ -327,6 +327,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Resilience.Core.UnitTe
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Common.UnitTests", "test\unit\Elsa.Common.UnitTests\Elsa.Common.UnitTests.csproj", "{A3C07D5B-2A30-494E-B9BC-4B1594B31ABC}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Http.IntegrationTests", "test\integration\Elsa.Http.IntegrationTests\Elsa.Http.IntegrationTests.csproj", "{8C4F6A2D-1E9F-4B3C-9D8E-7F5A6B4C3D2E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -591,6 +593,10 @@ Global
|
|||
{A3C07D5B-2A30-494E-B9BC-4B1594B31ABC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A3C07D5B-2A30-494E-B9BC-4B1594B31ABC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A3C07D5B-2A30-494E-B9BC-4B1594B31ABC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8C4F6A2D-1E9F-4B3C-9D8E-7F5A6B4C3D2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8C4F6A2D-1E9F-4B3C-9D8E-7F5A6B4C3D2E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8C4F6A2D-1E9F-4B3C-9D8E-7F5A6B4C3D2E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8C4F6A2D-1E9F-4B3C-9D8E-7F5A6B4C3D2E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -694,6 +700,7 @@ Global
|
|||
{874F5A44-DB06-47AB-A18C-2D13942E0147} = {477C2416-312D-46AE-BCD6-8FA1FAB43624}
|
||||
{B8006D70-1630-43DB-A043-FA89FAC70F37} = {18453B51-25EB-4317-A4B3-B10518252E92}
|
||||
{A3C07D5B-2A30-494E-B9BC-4B1594B31ABC} = {18453B51-25EB-4317-A4B3-B10518252E92}
|
||||
{8C4F6A2D-1E9F-4B3C-9D8E-7F5A6B4C3D2E} = {1B8D5897-902E-4632-8698-E89CAF3DDF54}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {D4B5CEAA-7D70-4FCB-A68E-B03FBE5E0E5E}
|
||||
|
|
|
|||
94
agent-logs/http-context-loss-error-messaging.md
Normal file
94
agent-logs/http-context-loss-error-messaging.md
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
# Improved Error Messaging for HTTP Context Loss in Workflows
|
||||
|
||||
## Issue
|
||||
When a workflow is initiated from an HTTP endpoint and later suspended or transitioned to a different execution context (e.g., background processing, virtual actor), the HTTP context becomes unavailable. Previously, the error message was generic and didn't clearly explain the cause of the failure.
|
||||
|
||||
## Solution
|
||||
Enhanced error messages in HTTP response activities to clearly describe the HTTP context loss scenario, and simplified the logic by removing bookmark creation - now these activities will always fault immediately when the HTTP context is not available.
|
||||
|
||||
## Changes Made
|
||||
|
||||
### 1. WriteHttpResponse.cs
|
||||
**File:** `src/modules/Elsa.Http/Activities/WriteHttpResponse.cs`
|
||||
|
||||
#### Removed bookmark creation logic
|
||||
- Previously, when HTTP context was null, the activity would create a bookmark with `BookmarkMetadata.HttpCrossBoundary` to allow resumption
|
||||
- Now the activity **always faults immediately** when HTTP context is null
|
||||
|
||||
#### Removed OnResumeAsync method
|
||||
- The `OnResumeAsync` callback method has been completely removed as it's no longer needed
|
||||
|
||||
#### Updated ExecuteAsync to always fault
|
||||
- When `httpContext == null`, immediately throws `FaultException` with detailed message
|
||||
- Message explains:
|
||||
- What happened: "The HTTP context was lost during workflow execution"
|
||||
- Why it happened: "workflow initiated from an HTTP endpoint is suspended and later resumed in a different execution context"
|
||||
- Examples: "background processing, virtual actor, or after a workflow transition"
|
||||
- Impact: "The original HTTP request context that expects a response is no longer available"
|
||||
|
||||
### 2. WriteFileHttpResponse.cs
|
||||
**File:** `src/modules/Elsa.Http/Activities/WriteFileHttpResponse.cs`
|
||||
|
||||
Applied the same changes for consistency:
|
||||
|
||||
#### Removed bookmark creation logic
|
||||
- No longer creates a bookmark when HTTP context is null
|
||||
|
||||
#### Removed OnResumeAsync method
|
||||
- The resume callback has been completely removed
|
||||
|
||||
#### Updated ExecuteAsync to always fault
|
||||
- Applied the same comprehensive error message as WriteHttpResponse
|
||||
|
||||
## Benefits
|
||||
|
||||
1. **Immediate Failure**: Workflows fail fast when HTTP context is lost, making issues immediately visible
|
||||
|
||||
2. **Clear Troubleshooting**: Users get explicit information about HTTP context loss with detailed explanation
|
||||
|
||||
3. **Better Incident Reporting**: Error messages now clearly describe the synchronization issue between workflow execution and HTTP request context
|
||||
|
||||
4. **Simplified Logic**: Removed the bookmark/resume pattern that could lead to confusing suspended states
|
||||
|
||||
5. **Consistency**: Both HTTP response activities (WriteHttpResponse and WriteFileHttpResponse) now have identical behavior and error messaging
|
||||
|
||||
## Technical Details
|
||||
|
||||
- **Fault Code**: `HttpFaultCodes.NoHttpContext`
|
||||
- **Fault Category**: `HttpFaultCategories.Http`
|
||||
- **Fault Type**: `DefaultFaultTypes.System`
|
||||
- **Behavior**: Immediate fault when `IHttpContextAccessor.HttpContext` is `null`
|
||||
|
||||
## Breaking Change Note
|
||||
|
||||
This is a **behavior change**:
|
||||
- **Before**: Activities would create a bookmark and suspend when HTTP context was null, potentially allowing resume in a different context
|
||||
- **After**: Activities always fault immediately when HTTP context is null
|
||||
|
||||
This change makes the failure mode more predictable and easier to diagnose, as workflows will no longer enter suspended states due to missing HTTP context.
|
||||
|
||||
## Testing
|
||||
|
||||
- No compilation errors introduced
|
||||
- Changes are backward compatible in terms of API surface
|
||||
- The fault code and structure remain the same for any existing error handling logic
|
||||
- Workflows that relied on bookmark creation for cross-boundary execution will now fault instead
|
||||
|
||||
### Integration Test Project Created
|
||||
|
||||
A new integration test project has been created at `test/integration/Elsa.Http.IntegrationTests/` to cover the HTTP context loss behavior:
|
||||
|
||||
**Project Structure:**
|
||||
- `Elsa.Http.IntegrationTests.csproj` - Test project file
|
||||
- `Activities/HttpContextLossTests.cs` - Integration tests for HTTP context loss scenarios
|
||||
- `README.md` - Documentation for the test project
|
||||
- `Usings.cs` - Global using directives
|
||||
|
||||
**Test Scenarios:**
|
||||
1. `WriteHttpResponse_WithNoHttpContext_ShouldRecordIncident` - Verifies that WriteHttpResponse records an incident with the detailed error message when HTTP context is null
|
||||
2. `WriteFileHttpResponse_WithNoHttpContext_ShouldRecordIncident` - Verifies that WriteFileHttpResponse records an incident when HTTP context is null
|
||||
|
||||
**Current Status:**
|
||||
The test project compiles and runs successfully. The integration tests are discovered by the xUnit test runner and currently all tests pass (2 tests passing), in line with the repository README.
|
||||
|
||||
|
||||
|
|
@ -71,11 +71,11 @@ public class WriteFileHttpResponse : Activity
|
|||
|
||||
if (httpContext == null)
|
||||
{
|
||||
// We're executing in a non-HTTP context (e.g. in a virtual actor).
|
||||
// Create a bookmark to allow the invoker to export the state and resume execution from there.
|
||||
|
||||
context.CreateBookmark(OnResumeAsync, BookmarkMetadata.HttpCrossBoundary);
|
||||
return;
|
||||
throw new FaultException(
|
||||
HttpFaultCodes.NoHttpContext,
|
||||
HttpFaultCategories.Http,
|
||||
DefaultFaultTypes.System,
|
||||
"The HTTP context was lost during workflow execution. This typically occurs when a workflow initiated from an HTTP endpoint is suspended and later resumed in a different execution context (e.g., background processing, virtual actor, or after a workflow transition). The original HTTP request context that expects a response is no longer available.");
|
||||
}
|
||||
|
||||
await WriteResponseAsync(context, httpContext);
|
||||
|
|
@ -294,15 +294,5 @@ public class WriteFileHttpResponse : Activity
|
|||
throw new HttpBadRequestException("Failed to parse If-Match header value", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask OnResumeAsync(ActivityExecutionContext context)
|
||||
{
|
||||
var httpContextAccessor = context.GetRequiredService<IHttpContextAccessor>();
|
||||
var httpContext = httpContextAccessor.HttpContext;
|
||||
|
||||
if (httpContext == null)
|
||||
throw new FaultException(HttpFaultCodes.NoHttpContext, HttpFaultCategories.Http, DefaultFaultTypes.System, "Cannot execute in a non-HTTP context");
|
||||
|
||||
await WriteResponseAsync(context, httpContext);
|
||||
}
|
||||
}
|
||||
|
|
@ -82,24 +82,11 @@ public class WriteHttpResponse : Activity
|
|||
|
||||
if (httpContext == null)
|
||||
{
|
||||
// We're executing in a non-HTTP context (e.g. in a virtual actor).
|
||||
// Create a bookmark to allow the invoker to export the state and resume execution from there.
|
||||
context.CreateBookmark(OnResumeAsync, BookmarkMetadata.HttpCrossBoundary);
|
||||
return;
|
||||
}
|
||||
|
||||
await WriteResponseAsync(context, httpContext.Response);
|
||||
}
|
||||
|
||||
private async ValueTask OnResumeAsync(ActivityExecutionContext context)
|
||||
{
|
||||
var httpContextAccessor = context.GetRequiredService<IHttpContextAccessor>();
|
||||
var httpContext = httpContextAccessor.HttpContext;
|
||||
|
||||
if (httpContext == null)
|
||||
{
|
||||
// We're not in an HTTP context, so let's fail.
|
||||
throw new FaultException(HttpFaultCodes.NoHttpContext, HttpFaultCategories.Http, DefaultFaultTypes.System, "Cannot execute in a non-HTTP context");
|
||||
throw new FaultException(
|
||||
HttpFaultCodes.NoHttpContext,
|
||||
HttpFaultCategories.Http,
|
||||
DefaultFaultTypes.System,
|
||||
"The HTTP context was lost during workflow execution. This typically occurs when a workflow initiated from an HTTP endpoint is suspended and later resumed in a different execution context (e.g., background processing, virtual actor, or after a workflow transition). The original HTTP request context that expects a response is no longer available.");
|
||||
}
|
||||
|
||||
await WriteResponseAsync(context, httpContext.Response);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
using Elsa.Http.IntegrationTests.Activities.Workflows;
|
||||
using Elsa.Http.IntegrationTests.Helpers;
|
||||
using Elsa.Testing.Shared;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Http.IntegrationTests.Activities;
|
||||
|
||||
/// <summary>
|
||||
/// Integration tests for HTTP response activities when HTTP context is lost.
|
||||
/// </summary>
|
||||
public class HttpContextLossTests
|
||||
{
|
||||
private readonly WorkflowTestFixture _fixture;
|
||||
|
||||
public HttpContextLossTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
_fixture = new WorkflowTestFixture(testOutputHelper)
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
// Register a null HTTP context accessor to simulate context loss
|
||||
services.AddSingleton<IHttpContextAccessor>(new NullHttpContextAccessor());
|
||||
});
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "WriteHttpResponse should record incident when HTTP context is null")]
|
||||
public async Task WriteHttpResponse_WithNoHttpContext_ShouldRecordIncident()
|
||||
{
|
||||
// Act
|
||||
var result = await _fixture.RunWorkflowAsync<WriteHttpResponseWithoutHttpContextWorkflow>();
|
||||
|
||||
// Assert
|
||||
// Verify an incident was recorded
|
||||
Assert.NotEmpty(result.WorkflowState.Incidents);
|
||||
|
||||
var incident = result.WorkflowState.Incidents.First();
|
||||
Assert.Contains("HTTP context was lost", incident.Message);
|
||||
Assert.Contains("background processing, virtual actor, or after a workflow transition", incident.Message);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "WriteFileHttpResponse should record incident when HTTP context is null")]
|
||||
public async Task WriteFileHttpResponse_WithNoHttpContext_ShouldRecordIncident()
|
||||
{
|
||||
// Act
|
||||
var result = await _fixture.RunWorkflowAsync<WriteFileHttpResponseWithoutHttpContextWorkflow>();
|
||||
|
||||
// Assert
|
||||
// Verify an incident was recorded
|
||||
Assert.NotEmpty(result.WorkflowState.Incidents);
|
||||
|
||||
var incident = result.WorkflowState.Incidents.First();
|
||||
Assert.Contains("HTTP context was lost", incident.Message);
|
||||
Assert.Contains("background processing, virtual actor, or after a workflow transition", incident.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
using Elsa.Workflows;
|
||||
|
||||
namespace Elsa.Http.IntegrationTests.Activities.Workflows;
|
||||
|
||||
/// <summary>
|
||||
/// A workflow that attempts to write a file HTTP response without HTTP context.
|
||||
/// </summary>
|
||||
internal class WriteFileHttpResponseWithoutHttpContextWorkflow : WorkflowBase
|
||||
{
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.Root = new WriteFileHttpResponse
|
||||
{
|
||||
Content = new(new byte[] { 1, 2, 3 }),
|
||||
Filename = new("test.bin")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
using Elsa.Workflows;
|
||||
|
||||
namespace Elsa.Http.IntegrationTests.Activities.Workflows;
|
||||
|
||||
/// <summary>
|
||||
/// A simple workflow that attempts to write an HTTP response without HTTP context.
|
||||
/// </summary>
|
||||
internal class WriteHttpResponseWithoutHttpContextWorkflow : WorkflowBase
|
||||
{
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.Root = new WriteHttpResponse
|
||||
{
|
||||
Content = new("This should fail"),
|
||||
StatusCode = new(System.Net.HttpStatusCode.OK)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Include>[Elsa.Http]*</Include>
|
||||
<Threshold>1</Threshold>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\common\Elsa.Testing.Shared.Integration\Elsa.Testing.Shared.Integration.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.Http\Elsa.Http.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.Workflows.Runtime\Elsa.Workflows.Runtime.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Elsa.Http.IntegrationTests.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// A null HTTP context accessor that always returns null.
|
||||
/// Used for testing scenarios where HTTP context is lost.
|
||||
/// </summary>
|
||||
internal class NullHttpContextAccessor : IHttpContextAccessor
|
||||
{
|
||||
public HttpContext? HttpContext { get; set; } = null;
|
||||
}
|
||||
|
||||
80
test/integration/Elsa.Http.IntegrationTests/README.md
Normal file
80
test/integration/Elsa.Http.IntegrationTests/README.md
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# Elsa.Http Integration Tests
|
||||
|
||||
This test project contains integration tests for HTTP-related activities in Elsa Workflows, specifically focusing on HTTP context loss scenarios.
|
||||
|
||||
## Status
|
||||
|
||||
✅ **Tests are working!** All tests pass successfully and are properly discovered by xUnit.
|
||||
|
||||
## Test Coverage
|
||||
|
||||
### HttpContextLossTests
|
||||
|
||||
Tests that verify the behavior of HTTP response activities when the HTTP context is lost during workflow execution.
|
||||
|
||||
#### Test Scenarios
|
||||
|
||||
1. **WriteHttpResponse_WithNoHttpContext_ShouldRecordIncident**
|
||||
- Verifies that `WriteHttpResponse` activity records an incident when HTTP context is null
|
||||
- Validates that the incident message clearly explains the HTTP context loss scenario
|
||||
- **Status**: ✅ Passing
|
||||
|
||||
2. **WriteFileHttpResponse_WithNoHttpContext_ShouldRecordIncident**
|
||||
- Verifies that `WriteFileHttpResponse` activity records an incident when HTTP context is null
|
||||
- Validates the incident message contains expected information
|
||||
- **Status**: ✅ Passing
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
When HTTP context is not available:
|
||||
- **Fault Code**: `NoHttpContext`
|
||||
- **Fault Category**: `HTTP`
|
||||
- **Fault Type**: `System`
|
||||
- **Error Message**: Detailed explanation including:
|
||||
- What happened: HTTP context was lost during workflow execution
|
||||
- Why it happened: Workflow suspended and resumed in different execution context
|
||||
- Common scenarios: Background processing, virtual actor, workflow transition
|
||||
- Impact: Original HTTP request context no longer available
|
||||
- **Result**: An `ActivityIncident` is recorded with the fault message
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
Elsa.Http.IntegrationTests/
|
||||
├── Activities/
|
||||
│ ├── HttpContextLossTests.cs # Test class using WorkflowTestFixture
|
||||
│ └── Workflows/
|
||||
│ ├── WriteHttpResponseWithoutHttpContextWorkflow.cs
|
||||
│ └── WriteFileHttpResponseWithoutHttpContextWorkflow.cs
|
||||
├── Helpers/
|
||||
│ └── NullHttpContextAccessor.cs # Mock HTTP context accessor
|
||||
├── Elsa.Http.IntegrationTests.csproj
|
||||
├── README.md
|
||||
└── Usings.cs
|
||||
```
|
||||
|
||||
## Running the Tests
|
||||
|
||||
```bash
|
||||
dotnet test Elsa.Http.IntegrationTests.csproj
|
||||
```
|
||||
|
||||
Or run specific tests:
|
||||
|
||||
```bash
|
||||
dotnet test --filter "FullyQualifiedName~HttpContextLossTests"
|
||||
```
|
||||
|
||||
## Test Results
|
||||
|
||||
```
|
||||
Passed! - Failed: 0, Passed: 2, Skipped: 0, Total: 2
|
||||
```
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Tests use `WorkflowTestFixture` from `Elsa.Testing.Shared` for consistent test setup
|
||||
- Workflow classes are separated into individual files in the `Workflows` subfolder for better organization
|
||||
- `NullHttpContextAccessor` is a test helper that simulates HTTP context loss by always returning null
|
||||
- Tests automatically build the fixture and populate registries before execution
|
||||
|
||||
2
test/integration/Elsa.Http.IntegrationTests/Usings.cs
Normal file
2
test/integration/Elsa.Http.IntegrationTests/Usings.cs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
global using Xunit;
|
||||
|
||||
|
|
@ -14,6 +14,7 @@ using Microsoft.AspNetCore.StaticFiles;
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using Elsa.Workflows.Exceptions;
|
||||
|
||||
namespace Elsa.Activities.UnitTests.Http;
|
||||
|
||||
|
|
@ -213,7 +214,7 @@ public class WriteFileHttpResponseTests
|
|||
// Assert
|
||||
Assert.True(context.IsCompleted);
|
||||
|
||||
// Range processing and ETag would be handled by FileStreamResult
|
||||
// Range processing and ETag should be handled by FileStreamResult
|
||||
Assert.True(httpContext.Response.Headers.ContainsKey("ETag"));
|
||||
}
|
||||
|
||||
|
|
@ -279,7 +280,7 @@ public class WriteFileHttpResponseTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_Create_Bookmark_When_No_HttpContext_Available()
|
||||
public async Task Should_Fault_When_No_HttpContext_Available()
|
||||
{
|
||||
// Arrange
|
||||
var activity = new WriteFileHttpResponse();
|
||||
|
|
@ -292,61 +293,8 @@ public class WriteFileHttpResponseTests
|
|||
AddMockServices(services);
|
||||
});
|
||||
|
||||
// Act
|
||||
var context = await fixture.ExecuteAsync();
|
||||
|
||||
// Assert
|
||||
Assert.False(context.IsCompleted);
|
||||
var bookmarks = context.WorkflowExecutionContext.Bookmarks.ToList();
|
||||
Assert.Single(bookmarks);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_Resume_From_Bookmark_With_HttpContext()
|
||||
{
|
||||
// Arrange
|
||||
var activity = new WriteFileHttpResponse();
|
||||
var testContent = "Hello World"u8.ToArray();
|
||||
activity.Content = new(testContent);
|
||||
|
||||
var fixture = new ActivityTestFixture(activity);
|
||||
var httpContext = CreateMockHttpContext();
|
||||
|
||||
// First execution - should create bookmark
|
||||
fixture.ConfigureServices(services =>
|
||||
{
|
||||
var mockHttpContextAccessor = Substitute.For<IHttpContextAccessor>();
|
||||
mockHttpContextAccessor.HttpContext.Returns((HttpContext?)null);
|
||||
services.AddSingleton(mockHttpContextAccessor);
|
||||
AddMockServices(services);
|
||||
});
|
||||
|
||||
var firstContext = await fixture.ExecuteAsync();
|
||||
Assert.False(firstContext.IsCompleted);
|
||||
|
||||
// Resume execution with HttpContext available
|
||||
fixture.ConfigureServices(services =>
|
||||
{
|
||||
var newHttpContextAccessor = Substitute.For<IHttpContextAccessor>();
|
||||
newHttpContextAccessor.HttpContext.Returns(httpContext);
|
||||
services.AddSingleton(newHttpContextAccessor);
|
||||
AddMockServices(services);
|
||||
});
|
||||
|
||||
// Act - simulate resume by executing with bookmark context
|
||||
var newFixture = new ActivityTestFixture(activity);
|
||||
newFixture.ConfigureServices(services =>
|
||||
{
|
||||
var resumeHttpContextAccessor = Substitute.For<IHttpContextAccessor>();
|
||||
resumeHttpContextAccessor.HttpContext.Returns(httpContext);
|
||||
services.AddSingleton(resumeHttpContextAccessor);
|
||||
AddMockServices(services);
|
||||
});
|
||||
|
||||
var resumedContext = await newFixture.ExecuteAsync();
|
||||
|
||||
// Assert
|
||||
Assert.True(resumedContext.IsCompleted);
|
||||
// Act + Assert
|
||||
await Assert.ThrowsAsync<FaultException>(() => fixture.ExecuteAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using Elsa.Http.Options;
|
|||
using Elsa.Http.Parsers;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Exceptions;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
|
@ -193,7 +194,7 @@ public class WriteHttpResponseTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_Create_Bookmark_When_No_HttpContext_Available()
|
||||
public async Task Should_Fault_When_No_HttpContext_Available()
|
||||
{
|
||||
// Arrange
|
||||
var activity = new WriteHttpResponse();
|
||||
|
|
@ -208,13 +209,8 @@ public class WriteHttpResponseTests
|
|||
services.AddSingleton(Substitute.For<IStimulusHasher>());
|
||||
});
|
||||
|
||||
// Act
|
||||
var context = await fixture.ExecuteAsync();
|
||||
|
||||
// Assert
|
||||
Assert.False(context.IsCompleted);
|
||||
var bookmarks = context.WorkflowExecutionContext.Bookmarks.ToList();
|
||||
Assert.Single(bookmarks);
|
||||
// Act + Assert
|
||||
await Assert.ThrowsAsync<FaultException>(() => fixture.ExecuteAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Reference in a new issue