PR improvements. More DRY, less clutter
This commit is contained in:
parent
656208bbf7
commit
f8d46d7afe
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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}"));
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue