|
@@ -0,0 +1,140 @@
|
|
|
+from unittest.mock import Mock, PropertyMock, patch
|
|
|
+
|
|
|
+import httpx
|
|
|
+import pytest
|
|
|
+
|
|
|
+from core.workflow.nodes.http_request.entities import Response
|
|
|
+
|
|
|
+
|
|
|
+@pytest.fixture
|
|
|
+def mock_response():
|
|
|
+ response = Mock(spec=httpx.Response)
|
|
|
+ response.headers = {}
|
|
|
+ return response
|
|
|
+
|
|
|
+
|
|
|
+def test_is_file_with_attachment_disposition(mock_response):
|
|
|
+ """Test is_file when content-disposition header contains 'attachment'"""
|
|
|
+ mock_response.headers = {"content-disposition": "attachment; filename=test.pdf", "content-type": "application/pdf"}
|
|
|
+ response = Response(mock_response)
|
|
|
+ assert response.is_file
|
|
|
+
|
|
|
+
|
|
|
+def test_is_file_with_filename_disposition(mock_response):
|
|
|
+ """Test is_file when content-disposition header contains filename parameter"""
|
|
|
+ mock_response.headers = {"content-disposition": "inline; filename=test.pdf", "content-type": "application/pdf"}
|
|
|
+ response = Response(mock_response)
|
|
|
+ assert response.is_file
|
|
|
+
|
|
|
+
|
|
|
+@pytest.mark.parametrize("content_type", ["application/pdf", "image/jpeg", "audio/mp3", "video/mp4"])
|
|
|
+def test_is_file_with_file_content_types(mock_response, content_type):
|
|
|
+ """Test is_file with various file content types"""
|
|
|
+ mock_response.headers = {"content-type": content_type}
|
|
|
+ # Mock binary content
|
|
|
+ type(mock_response).content = PropertyMock(return_value=bytes([0x00, 0xFF] * 512))
|
|
|
+ response = Response(mock_response)
|
|
|
+ assert response.is_file, f"Content type {content_type} should be identified as a file"
|
|
|
+
|
|
|
+
|
|
|
+@pytest.mark.parametrize(
|
|
|
+ "content_type",
|
|
|
+ [
|
|
|
+ "application/json",
|
|
|
+ "application/xml",
|
|
|
+ "application/javascript",
|
|
|
+ "application/x-www-form-urlencoded",
|
|
|
+ "application/yaml",
|
|
|
+ "application/graphql",
|
|
|
+ ],
|
|
|
+)
|
|
|
+def test_text_based_application_types(mock_response, content_type):
|
|
|
+ """Test common text-based application types are not identified as files"""
|
|
|
+ mock_response.headers = {"content-type": content_type}
|
|
|
+ response = Response(mock_response)
|
|
|
+ assert not response.is_file, f"Content type {content_type} should not be identified as a file"
|
|
|
+
|
|
|
+
|
|
|
+@pytest.mark.parametrize(
|
|
|
+ ("content", "content_type"),
|
|
|
+ [
|
|
|
+ (b'{"key": "value"}', "application/octet-stream"),
|
|
|
+ (b"[1, 2, 3]", "application/unknown"),
|
|
|
+ (b"function test() {}", "application/x-unknown"),
|
|
|
+ (b"<root>test</root>", "application/binary"),
|
|
|
+ (b"var x = 1;", "application/data"),
|
|
|
+ ],
|
|
|
+)
|
|
|
+def test_content_based_detection(mock_response, content, content_type):
|
|
|
+ """Test content-based detection for text-like content"""
|
|
|
+ mock_response.headers = {"content-type": content_type}
|
|
|
+ type(mock_response).content = PropertyMock(return_value=content)
|
|
|
+ response = Response(mock_response)
|
|
|
+ assert not response.is_file, f"Content {content} with type {content_type} should not be identified as a file"
|
|
|
+
|
|
|
+
|
|
|
+@pytest.mark.parametrize(
|
|
|
+ ("content", "content_type"),
|
|
|
+ [
|
|
|
+ (bytes([0x00, 0xFF] * 512), "application/octet-stream"),
|
|
|
+ (bytes([0x89, 0x50, 0x4E, 0x47]), "application/unknown"), # PNG magic numbers
|
|
|
+ (bytes([0xFF, 0xD8, 0xFF]), "application/binary"), # JPEG magic numbers
|
|
|
+ ],
|
|
|
+)
|
|
|
+def test_binary_content_detection(mock_response, content, content_type):
|
|
|
+ """Test content-based detection for binary content"""
|
|
|
+ mock_response.headers = {"content-type": content_type}
|
|
|
+ type(mock_response).content = PropertyMock(return_value=content)
|
|
|
+ response = Response(mock_response)
|
|
|
+ assert response.is_file, f"Binary content with type {content_type} should be identified as a file"
|
|
|
+
|
|
|
+
|
|
|
+@pytest.mark.parametrize(
|
|
|
+ ("content_type", "expected_main_type"),
|
|
|
+ [
|
|
|
+ ("x-world/x-vrml", "model"), # VRML 3D model
|
|
|
+ ("font/ttf", "application"), # TrueType font
|
|
|
+ ("text/csv", "text"), # CSV text file
|
|
|
+ ("unknown/xyz", None), # Unknown type
|
|
|
+ ],
|
|
|
+)
|
|
|
+def test_mimetype_based_detection(mock_response, content_type, expected_main_type):
|
|
|
+ """Test detection using mimetypes.guess_type for non-application content types"""
|
|
|
+ mock_response.headers = {"content-type": content_type}
|
|
|
+ type(mock_response).content = PropertyMock(return_value=bytes([0x00])) # Dummy content
|
|
|
+
|
|
|
+ with patch("core.workflow.nodes.http_request.entities.mimetypes.guess_type") as mock_guess_type:
|
|
|
+ # Mock the return value based on expected_main_type
|
|
|
+ if expected_main_type:
|
|
|
+ mock_guess_type.return_value = (f"{expected_main_type}/subtype", None)
|
|
|
+ else:
|
|
|
+ mock_guess_type.return_value = (None, None)
|
|
|
+
|
|
|
+ response = Response(mock_response)
|
|
|
+
|
|
|
+ # Check if the result matches our expectation
|
|
|
+ if expected_main_type in ("application", "image", "audio", "video"):
|
|
|
+ assert response.is_file, f"Content type {content_type} should be identified as a file"
|
|
|
+ else:
|
|
|
+ assert not response.is_file, f"Content type {content_type} should not be identified as a file"
|
|
|
+
|
|
|
+ # Verify that guess_type was called
|
|
|
+ mock_guess_type.assert_called_once()
|
|
|
+
|
|
|
+
|
|
|
+def test_is_file_with_inline_disposition(mock_response):
|
|
|
+ """Test is_file when content-disposition is 'inline'"""
|
|
|
+ mock_response.headers = {"content-disposition": "inline", "content-type": "application/pdf"}
|
|
|
+ # Mock binary content
|
|
|
+ type(mock_response).content = PropertyMock(return_value=bytes([0x00, 0xFF] * 512))
|
|
|
+ response = Response(mock_response)
|
|
|
+ assert response.is_file
|
|
|
+
|
|
|
+
|
|
|
+def test_is_file_with_no_content_disposition(mock_response):
|
|
|
+ """Test is_file when no content-disposition header is present"""
|
|
|
+ mock_response.headers = {"content-type": "application/pdf"}
|
|
|
+ # Mock binary content
|
|
|
+ type(mock_response).content = PropertyMock(return_value=bytes([0x00, 0xFF] * 512))
|
|
|
+ response = Response(mock_response)
|
|
|
+ assert response.is_file
|