PR improvements. More DRY, less clutter

This commit is contained in:
lucas.hipolito 2025-12-02 15:35:41 +01:00
parent 656208bbf7
commit f8d46d7afe
8 changed files with 254 additions and 326 deletions

View file

@ -12,13 +12,11 @@ public class HttpEndpointContentTests(App app) : AppComponentTest(app)
public async Task JsonContent_ValidJson_ReturnsEchoedJson()
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
var testData = new { Name = "John", Age = 30, City = "New York" };
var jsonContent = JsonSerializer.Serialize(testData);
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
// Act
var response = await client.PostAsync("test/json-content", content);
var response = await PostJsonContentAsync(jsonContent);
var responseContent = await response.Content.ReadAsStringAsync();
// Assert
@ -31,93 +29,107 @@ public class HttpEndpointContentTests(App app) : AppComponentTest(app)
Assert.Equal("John", nameProperty.GetString());
}
[Fact]
public async Task JsonContent_InvalidJson_ReturnsBadRequest()
[Theory]
[InlineData("{ \"name\": \"John\", invalid }", HttpStatusCode.BadRequest)]
[InlineData("", HttpStatusCode.OK, "No content received")]
public async Task JsonContent_InvalidOrEmpty_ReturnsExpectedResponse(
string jsonContent,
HttpStatusCode expectedStatusCode,
string? expectedContentFragment = null)
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
var invalidJson = "{ \"name\": \"John\", invalid }";
var content = new StringContent(invalidJson, Encoding.UTF8, "application/json");
// Act
var response = await client.PostAsync("test/json-content", content);
// Assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
[Fact]
public async Task JsonContent_EmptyBody_ReturnsNoContentMessage()
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
var content = new StringContent("", Encoding.UTF8, "application/json");
// Act
var response = await client.PostAsync("test/json-content", content);
var response = await PostJsonContentAsync(jsonContent);
var responseContent = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Contains("No content received", responseContent);
Assert.Equal(expectedStatusCode, response.StatusCode);
if (expectedContentFragment != null)
{
Assert.Contains(expectedContentFragment, responseContent);
}
}
[Fact]
public async Task FormData_ValidFormData_ReturnsExtractedFields()
[Theory]
[InlineData("John Doe", "john@example.com", "Name: John Doe", "Email: john@example.com")]
[InlineData("Jane Smith", "jane@test.org", "Name: Jane Smith", "Email: jane@test.org")]
public async Task FormData_ValidData_ReturnsExtractedFields(
string name,
string email,
string expectedNameFragment,
string expectedEmailFragment)
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
var formData = new List<KeyValuePair<string, string>>
{
new("name", "John Doe"),
new("email", "john@example.com")
new("name", name),
new("email", email)
};
var content = new FormUrlEncodedContent(formData);
// Act
var response = await client.PostAsync("test/form-data", content);
var response = await PostFormDataAsync(formData);
var responseContent = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Contains("Name: John Doe", responseContent);
Assert.Contains("Email: john@example.com", responseContent);
Assert.Contains(expectedNameFragment, responseContent);
Assert.Contains(expectedEmailFragment, responseContent);
}
[Fact]
public async Task FormData_MissingFields_ReturnsUnknownValues()
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
var formData = new List<KeyValuePair<string, string>>
{
new("other", "value")
};
var content = new FormUrlEncodedContent(formData);
// Act
var response = await client.PostAsync("test/form-data", content);
var response = await PostFormDataAsync(formData);
var responseContent = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Contains("Name: unknown", responseContent);
Assert.Contains("Email: unknown", responseContent);
AssertOkResponseContains(response, responseContent, "Name: unknown", "Email: unknown");
}
[Fact]
public async Task FormData_EmptyForm_ReturnsNoFormDataMessage()
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
var content = new StringContent("", Encoding.UTF8, "text/plain");
// Act
var response = await client.PostAsync("test/form-data", content);
var response = await PostEmptyFormAsync();
var responseContent = await response.Content.ReadAsStringAsync();
// Assert
AssertOkResponseContains(response, responseContent, "No form data received");
}
private async Task<HttpResponseMessage> PostJsonContentAsync(string jsonContent)
{
var client = WorkflowServer.CreateHttpWorkflowClient();
using var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
return await client.PostAsync("test/json-content", content);
}
private async Task<HttpResponseMessage> PostFormDataAsync(IEnumerable<KeyValuePair<string, string>> formData)
{
var client = WorkflowServer.CreateHttpWorkflowClient();
using var content = new FormUrlEncodedContent(formData);
return await client.PostAsync("test/form-data", content);
}
private async Task<HttpResponseMessage> PostEmptyFormAsync()
{
var client = WorkflowServer.CreateHttpWorkflowClient();
using var content = new StringContent("", Encoding.UTF8, "text/plain");
return await client.PostAsync("test/form-data", content);
}
private static void AssertOkResponseContains(HttpResponseMessage response, string responseContent, params string[] expectedFragments)
{
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Contains("No form data received", responseContent);
foreach (var fragment in expectedFragments)
{
Assert.Contains(fragment, responseContent);
}
}
}

View file

@ -7,113 +7,145 @@ namespace Elsa.Workflows.ComponentTests.Scenarios.Activities.Http;
public class HttpEndpointFileUploadTests(App app) : AppComponentTest(app)
{
[Fact]
public async Task FileUpload_SingleFile_ReturnsFileDetails()
[Theory]
[InlineData("Test file content", "test.txt", "text/plain", "17 bytes")]
[InlineData("Sample document", "sample.txt", "text/plain", "15 bytes")]
[InlineData("", "empty.txt", "text/plain", "0 bytes")]
public async Task FileUpload_SingleFile_ReturnsExpectedMetadata(
string fileContent,
string fileName,
string contentType,
string expectedSizeText)
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(Encoding.UTF8.GetBytes("Test file content"));
fileContent.Headers.ContentType = new("text/plain");
content.Add(fileContent, "file", "test.txt");
var fileData = Encoding.UTF8.GetBytes(fileContent);
// Act
var response = await client.PostAsync("test/file-upload", content);
var response = await PostSingleFileAsync(fileData, fileName, contentType);
var responseContent = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Contains("test.txt", responseContent);
Assert.Contains("17 bytes", responseContent); // "Test file content" is 17 bytes
Assert.Contains("text/plain", responseContent);
AssertOkResponseContains(response, responseContent, fileName, expectedSizeText, contentType);
}
[Fact]
public async Task FileUpload_MultipleFiles_ReturnsAllFileDetails()
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
var content = new MultipartFormDataContent();
var file1Content = new ByteArrayContent(Encoding.UTF8.GetBytes("File 1 content"));
file1Content.Headers.ContentType = new("text/plain");
content.Add(file1Content, "file1", "file1.txt");
var file2Content = new ByteArrayContent(Encoding.UTF8.GetBytes("File 2 content"));
file2Content.Headers.ContentType = new("text/plain");
content.Add(file2Content, "file2", "file2.txt");
var files = new[]
{
("File 1 content", "file1.txt", "text/plain"),
("File 2 content", "file2.txt", "text/plain")
};
// Act
var response = await client.PostAsync("test/file-upload", content);
var response = await PostMultipleFilesAsync(files);
var responseContent = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Contains("file1.txt", responseContent);
Assert.Contains("file2.txt", responseContent);
Assert.Contains("14 bytes", responseContent); // Each file is 14 bytes
AssertOkResponseContains(response, responseContent, "file1.txt", "file2.txt", "14 bytes");
}
[Fact]
public async Task FileUpload_NoFiles_ReturnsNoFilesMessage()
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
var content = new MultipartFormDataContent();
content.Add(new StringContent("value"), "field");
// Act
var response = await client.PostAsync("test/file-upload", content);
var response = await PostFormDataWithoutFilesAsync();
var responseContent = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Contains("No files uploaded", responseContent);
}
[Fact]
public async Task FileUpload_EmptyFile_ReturnsZeroBytesFile()
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent([]);
fileContent.Headers.ContentType = new("text/plain");
content.Add(fileContent, "file", "empty.txt");
// Act
var response = await client.PostAsync("test/file-upload", content);
var responseContent = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Contains("empty.txt", responseContent);
Assert.Contains("0 bytes", responseContent);
AssertOkResponseContains(response, responseContent, "No files uploaded");
}
[Fact]
public async Task FileUpload_WithFormFields_ProcessesBothFilesAndFields()
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
var content = new MultipartFormDataContent();
// Add file
var fileContent = new ByteArrayContent(Encoding.UTF8.GetBytes("Test content"));
fileContent.Headers.ContentType = new("text/plain");
content.Add(fileContent, "file", "test.txt");
// Add form field
content.Add(new StringContent("John Doe"), "name");
var fileData = Encoding.UTF8.GetBytes("Test content");
var formFields = new[] { ("name", "John Doe") };
// Act
var response = await client.PostAsync("test/file-upload", content);
var response = await PostFileWithFormFieldsAsync(fileData, "test.txt", "text/plain", formFields);
var responseContent = await response.Content.ReadAsStringAsync();
// Assert
AssertOkResponseContains(response, responseContent, "test.txt", "12 bytes");
}
private async Task<HttpResponseMessage> PostSingleFileAsync(
byte[] fileData,
string fileName,
string contentType)
{
var client = WorkflowServer.CreateHttpWorkflowClient();
using var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(fileData);
fileContent.Headers.ContentType = new(contentType);
content.Add(fileContent, "file", fileName);
return await client.PostAsync("test/file-upload", content);
}
private async Task<HttpResponseMessage> PostMultipleFilesAsync(
(string content, string fileName, string contentType)[] files)
{
var client = WorkflowServer.CreateHttpWorkflowClient();
using var content = new MultipartFormDataContent();
for (int i = 0; i < files.Length; i++)
{
var file = files[i];
var fileContent = new ByteArrayContent(Encoding.UTF8.GetBytes(file.content));
fileContent.Headers.ContentType = new(file.contentType);
content.Add(fileContent, $"file{i + 1}", file.fileName);
}
return await client.PostAsync("test/file-upload", content);
}
private async Task<HttpResponseMessage> PostFormDataWithoutFilesAsync()
{
var client = WorkflowServer.CreateHttpWorkflowClient();
using var content = new MultipartFormDataContent();
content.Add(new StringContent("value"), "field");
return await client.PostAsync("test/file-upload", content);
}
private async Task<HttpResponseMessage> PostFileWithFormFieldsAsync(
byte[] fileData,
string fileName,
string contentType,
(string name, string value)[] formFields)
{
var client = WorkflowServer.CreateHttpWorkflowClient();
using var content = new MultipartFormDataContent();
// Add file
var fileContent = new ByteArrayContent(fileData);
fileContent.Headers.ContentType = new(contentType);
content.Add(fileContent, "file", fileName);
// Add form fields
foreach (var (name, value) in formFields)
{
content.Add(new StringContent(value), name);
}
return await client.PostAsync("test/file-upload", content);
}
private static void AssertOkResponseContains(
HttpResponseMessage response,
string responseContent,
params string[] expectedFragments)
{
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Contains("test.txt", responseContent);
Assert.Contains("12 bytes", responseContent); // "Test content" is 12 bytes
foreach (var fragment in expectedFragments)
{
Assert.Contains(fragment, responseContent);
}
}
}

View file

@ -5,57 +5,44 @@ namespace Elsa.Workflows.ComponentTests.Scenarios.Activities.Http;
public class HttpEndpointQueryStringAndHeadersTests(App app) : AppComponentTest(app)
{
[Fact]
public async Task QueryStringAndHeaders_WithParameters_ReturnsExtractedData()
[Theory]
[InlineData("name=John", "TestAgent/1.0", "Name: John", "UserAgent: TestAgent/1.0")]
[InlineData("name=Jane", "CustomAgent/2.0", "Name: Jane", "UserAgent: CustomAgent/2.0")]
[InlineData("name=John&age=30&city=NewYork", "TestAgent/1.0", "Name: John", "UserAgent: TestAgent/1.0")]
public async Task QueryStringAndHeaders_WithParameters_ReturnsExtractedData(
string queryString,
string userAgent,
string expectedNameFragment,
string expectedUserAgentFragment)
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
client.DefaultRequestHeaders.Add("User-Agent", "TestAgent/1.0");
// Act
var response = await client.GetStringAsync("test/query-headers?name=John");
var response = await GetQueryHeadersResponseAsync(queryString, userAgent);
// Assert
Assert.Contains("Name: John", response);
Assert.Contains("UserAgent: TestAgent/1.0", response);
Assert.Contains(expectedNameFragment, response);
Assert.Contains(expectedUserAgentFragment, response);
}
[Fact]
public async Task QueryStringAndHeaders_NoParameters_ReturnsDefaultValues()
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
// Act
var response = await client.GetStringAsync("test/query-headers");
var response = await GetQueryHeadersResponseAsync();
// Assert
Assert.Contains("Name: unknown", response);
Assert.Contains("UserAgent:", response); // Should contain UserAgent even if empty
}
[Fact]
public async Task QueryStringAndHeaders_MultipleQueryParameters_ReturnsFirstParameterValue()
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
// Act
var response = await client.GetStringAsync("test/query-headers?name=John&age=30&city=NewYork");
// Assert
Assert.Contains("Name: John", response);
}
[Fact]
public async Task QueryStringAndHeaders_UrlEncodedQueryString_ReturnsDecodedValue()
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
var encodedName = Uri.EscapeDataString("John Doe");
var queryString = $"name={encodedName}";
// Act
var response = await client.GetStringAsync($"test/query-headers?name={encodedName}");
var response = await GetQueryHeadersResponseAsync(queryString);
// Assert
Assert.Contains("Name: John Doe", response);
@ -65,15 +52,48 @@ public class HttpEndpointQueryStringAndHeadersTests(App app) : AppComponentTest(
public async Task QueryStringAndHeaders_CustomHeaders_ReturnsHeaderValues()
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
client.DefaultRequestHeaders.Add("X-Custom-Header", "CustomValue");
client.DefaultRequestHeaders.Add("User-Agent", "CustomAgent/2.0");
var customHeaders = new Dictionary<string, string>
{
["X-Custom-Header"] = "CustomValue",
["User-Agent"] = "CustomAgent/2.0"
};
// Act
var response = await client.GetStringAsync("test/query-headers?name=Jane");
var response = await GetQueryHeadersResponseAsync("name=Jane", customHeaders: customHeaders);
// Assert
Assert.Contains("Name: Jane", response);
Assert.Contains("UserAgent: CustomAgent/2.0", response);
}
private async Task<string> GetQueryHeadersResponseAsync(
string? queryString = null,
string? userAgent = null,
Dictionary<string, string>? customHeaders = null)
{
var client = WorkflowServer.CreateHttpWorkflowClient();
// Add headers
if (!string.IsNullOrEmpty(userAgent))
{
client.DefaultRequestHeaders.Add("User-Agent", userAgent);
}
if (customHeaders != null)
{
foreach (var header in customHeaders)
{
client.DefaultRequestHeaders.Add(header.Key, header.Value);
}
}
// Build URL
var url = "test/query-headers";
if (!string.IsNullOrEmpty(queryString))
{
url += $"?{queryString}";
}
return await client.GetStringAsync(url);
}
}

View file

@ -7,26 +7,12 @@ namespace Elsa.Workflows.ComponentTests.Scenarios.Activities.Http;
public class HttpEndpointSecurityAndEdgeCasesTests(App app) : AppComponentTest(app)
{
[Fact]
public async Task HttpEndpoint_WithAuthentication_RequiresAuthorization()
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
// Act - Try to access secure endpoint without authentication
var response = await client.GetAsync("test/secure");
// Assert - Should require authorization (exact behavior depends on auth configuration)
Assert.NotEqual(HttpStatusCode.OK, response.StatusCode);
}
[Fact]
public async Task HttpEndpoint_BlockedFileExtensions_RejectsBlockedFiles()
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
var content = new MultipartFormDataContent();
using var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(Encoding.UTF8.GetBytes("Malicious file"));
fileContent.Headers.ContentType = new("application/octet-stream");
content.Add(fileContent, "file", "malware.exe"); // .exe is in blocked extensions
@ -43,7 +29,7 @@ public class HttpEndpointSecurityAndEdgeCasesTests(App app) : AppComponentTest(a
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
var content = new MultipartFormDataContent();
using var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(Encoding.UTF8.GetBytes("Safe file content"));
fileContent.Headers.ContentType = new("text/plain");
content.Add(fileContent, "file", "document.txt"); // .txt is not in blocked extensions
@ -177,7 +163,7 @@ public class HttpEndpointSecurityAndEdgeCasesTests(App app) : AppComponentTest(a
{
// Arrange
var client = WorkflowServer.CreateHttpWorkflowClient();
var content = new MultipartFormDataContent();
using var content = new MultipartFormDataContent();
var emptyFile = new ByteArrayContent(Array.Empty<byte>());
emptyFile.Headers.ContentType = new("text/plain");
content.Add(emptyFile, "file", "empty.txt");

View file

@ -99,7 +99,7 @@ public class HttpEndpointTests(App app) : AppComponentTest(app)
var tasks = new List<Task<string>>();
// Act - Send 10 concurrent requests
for (int i = 0; i < 10; i++)
for (var i = 0; i < 10; i++)
{
var index = i;
tasks.Add(client.GetStringAsync($"test/users/user-{index}/orders/order-{index}"));

View file

@ -1,45 +1,14 @@
using System.Net;
using Elsa.Http;
using Elsa.Workflows.Activities;
using Elsa.Workflows.Activities.Flowchart.Activities;
using Elsa.Workflows.Activities.Flowchart.Models;
using Microsoft.AspNetCore.Http;
namespace Elsa.Workflows.ComponentTests.Scenarios.Activities.Http.Workflows;
public class SecurityTestWorkflow : WorkflowBase
{
private static readonly string DefinitionId = Guid.NewGuid().ToString();
protected override void Build(IWorkflowBuilder builder)
{
builder.WithDefinitionId(DefinitionId);
builder.Root = new Sequence
{
Activities =
[
new HttpEndpoint
{
Path = new("test/secure"),
SupportedMethods = new([HttpMethods.Get, HttpMethods.Post]),
CanStartWorkflow = true
},
// Hardcoded response that definitely returns Unauthorized (401)
new WriteHttpResponse
{
Content = new("SECURITY_TEST_UNAUTHORIZED"),
ContentType = new("text/plain"),
StatusCode = new(HttpStatusCode.Unauthorized)
}
]
};
}
}
public class BlockedFileExtensionWorkflow : WorkflowBase
{
public static readonly string DefinitionId = Guid.NewGuid().ToString();
private static readonly string DefinitionId = Guid.NewGuid().ToString();
protected override void Build(IWorkflowBuilder builder)
{

View file

@ -30,33 +30,47 @@ public class RouteParametersWorkflow : WorkflowBase
{
Content = new(context =>
{
// WORKAROUND: Since route table isn't populated during tests,
// manually parse the request URL to extract parameters
try
var routeData = routeDataVariable.Get(context);
// First, try to get route parameters from the proper route data
var userId = "unknown";
var orderId = "unknown";
if (routeData is { Count: > 0 })
{
var httpContext = context.GetRequiredService<Microsoft.AspNetCore.Http.IHttpContextAccessor>().HttpContext;
var path = httpContext?.Request?.Path.Value ?? "";
// Pattern: /workflows/test/users/{userId}/orders/{orderId}
var match = System.Text.RegularExpressions.Regex.Match(path, @"/workflows/test/users/([^/]+)/orders/([^/]+)");
if (match.Success && match.Groups.Count >= 3)
{
return $"UserId: {match.Groups[1].Value}, OrderId: {match.Groups[2].Value}";
}
// Fallback: try simple splitting
var parts = path.Split('/', StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 6 && parts[1] == "test" && parts[2] == "users" && parts[4] == "orders")
{
return $"UserId: {parts[3]}, OrderId: {parts[5]}";
}
return "Could not parse route parameters";
// Proper route data is available
userId = routeData.TryGetValue("userid", out var value) ? value.ToString() ?? "unknown" : "unknown";
orderId = routeData.TryGetValue("orderid", out var value1) ? value1.ToString() ?? "unknown" : "unknown";
}
catch
else
{
return "UserId: unknown, OrderId: unknown";
// Fallback: Extract from HttpContext if route data is not populated
try
{
var httpContext = context.GetRequiredService<Microsoft.AspNetCore.Http.IHttpContextAccessor>().HttpContext;
var path = httpContext?.Request?.Path.Value ?? "";
// Split path and look for the users/{userId}/orders/{orderId} pattern
var parts = path.Split('/', StringSplitOptions.RemoveEmptyEntries);
// Find the users pattern in the path
for (int i = 0; i < parts.Length - 3; i++)
{
if (parts[i] == "users" && i + 2 < parts.Length && parts[i + 2] == "orders" && i + 3 < parts.Length)
{
userId = Uri.UnescapeDataString(parts[i + 1]);
orderId = Uri.UnescapeDataString(parts[i + 3]);
break;
}
}
}
catch
{
// Keep defaults if parsing fails
}
}
return $"UserId: {userId}, OrderId: {orderId}";
}),
ContentType = new("text/plain"),
StatusCode = new(HttpStatusCode.OK)

View file

@ -1,105 +0,0 @@
using System.Text.Json;
using Elsa.Http;
namespace Elsa.Workflows.IntegrationTests.Activities.Http;
public class HttpEndpointIntegrationTests
{
[Fact]
public async Task HttpEndpoint_ConcurrentRequests_ProcessesAllSuccessfully()
{
// Arrange - Test concurrent HttpEndpoint activity instantiation and configuration
var tasks = new List<Task<HttpEndpoint>>();
// Act - Create multiple HttpEndpoint activities concurrently
for (int i = 0; i < 10; i++)
{
var index = i;
var task = Task.Run(() =>
{
var endpoint = new HttpEndpoint
{
Path = new($"test/concurrent/user-{index}"),
SupportedMethods = new(["GET", "POST"])
};
return endpoint;
});
tasks.Add(task);
}
var endpoints = await Task.WhenAll(tasks);
// Assert - Verify all HttpEndpoint activities were created successfully
Assert.Equal(10, endpoints.Length);
for (int i = 0; i < 10; i++)
{
var endpoint = endpoints[i];
Assert.NotNull(endpoint);
// Verify the path input was set correctly
Assert.NotNull(endpoint.Path);
// Verify the supported methods input was set correctly
Assert.NotNull(endpoint.SupportedMethods);
}
}
[Fact]
public async Task HttpEndpoint_LargeJsonPayload_ProcessesCorrectly()
{
// Arrange
var largeObject = new
{
Users = Enumerable.Range(1, 100).Select(i => new
{
Id = i,
Name = $"User {i}",
Email = $"user{i}@example.com",
Data = new string('x', 100) // 100 characters per user
}).ToArray()
};
var jsonContent = JsonSerializer.Serialize(largeObject);
// Act & Assert - Test JSON processing capability
var parsedBack = JsonSerializer.Deserialize<JsonElement>(jsonContent);
Assert.True(parsedBack.TryGetProperty("Users", out var usersProperty));
Assert.Equal(JsonValueKind.Array, usersProperty.ValueKind);
Assert.Equal(100, usersProperty.GetArrayLength());
}
[Fact]
public async Task HttpEndpoint_UnicodeContent_ProcessesCorrectly()
{
// Arrange
var originalMessage = "Hello 世界! 🌍 Ñandú";
var unicodeData = new { Message = originalMessage };
var jsonContent = JsonSerializer.Serialize(unicodeData, new JsonSerializerOptions { WriteIndented = true });
// Act - Parse the JSON to simulate processing
var parsedResponse = JsonSerializer.Deserialize<JsonElement>(jsonContent);
// Assert
Assert.True(parsedResponse.TryGetProperty("Message", out var messageProperty),
$"Response should contain 'Message' property. Actual response: {jsonContent}");
var actualMessage = messageProperty.GetString();
Assert.Equal(originalMessage, actualMessage);
}
[Fact]
public async Task HttpEndpoint_SpecialCharactersInRoute_HandlesCorrectly()
{
// Test URI encoding/decoding
var specialUserId = "user@domain.com";
var specialOrderId = "order-with-special-chars!@#$%";
var encodedUserId = Uri.EscapeDataString(specialUserId);
var encodedOrderId = Uri.EscapeDataString(specialOrderId);
// Verify encoding/decoding works
Assert.Equal(specialUserId, Uri.UnescapeDataString(encodedUserId));
Assert.Equal(specialOrderId, Uri.UnescapeDataString(encodedOrderId));
}
}