From 1ce03ec79e0e6e9d692ee5864c9cbd3fa9f104fd Mon Sep 17 00:00:00 2001 From: Ben Thompson Date: Thu, 2 Jul 2026 18:32:34 -0700 Subject: [PATCH 1/3] Fix missing return for unsupported body_type in add_body_to_step An unsupported body_type constructed the error BaseResult but did not return it, so execution fell through and PUT the step with an empty body and no Content-Type header. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01S2Ym1SshabyXjWNdeGFDfx --- src/tools/step_manager.py | 2 +- tests/test_step_manager.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/tools/step_manager.py b/src/tools/step_manager.py index 46ef149..604dd37 100644 --- a/src/tools/step_manager.py +++ b/src/tools/step_manager.py @@ -123,7 +123,7 @@ async def add_body_to_step( return BaseResult(error=f"Error processing text content: {str(e)}") request_headers["Content-Type"] = "text/plain" case _: - BaseResult( + return BaseResult( error=f"Unsupported body_type {body_type}. Supported types are: json, xml, html, text" ) diff --git a/tests/test_step_manager.py b/tests/test_step_manager.py index 2e14d9d..eef53f9 100644 --- a/tests/test_step_manager.py +++ b/tests/test_step_manager.py @@ -182,6 +182,23 @@ async def test_add_body_to_step_invalid_xml(self, mock_token, mock_context): assert result.error is not None assert "Invalid XML" in result.error + async def test_add_body_to_step_unsupported_type(self, mock_token, mock_context): + """Test adding body with unsupported body_type returns error without calling the API""" + manager = StepManager(mock_token, mock_context) + + with patch("src.tools.step_manager.api_request") as mock_api: + result = await manager.add_body_to_step( + "bucket_abc", + "test_123", + "step_123", + "yaml", + "key: value" + ) + + assert result.error is not None + assert "Unsupported body_type" in result.error + mock_api.assert_not_called() + async def test_add_assertion_to_step(self, mock_token, mock_context): """Test adding assertion to step""" manager = StepManager(mock_token, mock_context) From a7382abc4d332d02a5fdf94ce8315ef05feca012 Mon Sep 17 00:00:00 2001 From: Ben Thompson Date: Thu, 2 Jul 2026 18:37:53 -0700 Subject: [PATCH 2/3] Fix copy-pasted error message in add_assertion_to_step The non-request-step guard reported "cannot have a body added" when rejecting an assertion. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01S2Ym1SshabyXjWNdeGFDfx --- src/tools/step_manager.py | 4 +++- tests/test_step_manager.py | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/tools/step_manager.py b/src/tools/step_manager.py index 604dd37..8fb0f41 100644 --- a/src/tools/step_manager.py +++ b/src/tools/step_manager.py @@ -156,7 +156,9 @@ async def add_assertion_to_step( ) -> BaseResult: request_result = await self.read(bucket_key, test_id, step_id, result_formatter=None) if not request_result or request_result.get("step_type") != "request": - return BaseResult(error=f"Step {step_id} is not a request step and cannot have a body added.") + return BaseResult( + error=f"Step {step_id} is not a request step and cannot have an assertion added." + ) if "assertions" not in request_result or not isinstance(request_result["assertions"], list): request_result["assertions"] = [] new_assertion = {"source": assertion_source, "comparison": assertion_comparison} diff --git a/tests/test_step_manager.py b/tests/test_step_manager.py index eef53f9..83cd0aa 100644 --- a/tests/test_step_manager.py +++ b/tests/test_step_manager.py @@ -230,3 +230,27 @@ async def test_add_assertion_to_step(self, mock_token, mock_context): assert result.error is None + async def test_add_assertion_to_non_request_step(self, mock_token, mock_context): + """Test adding assertion to a non-request step returns error""" + manager = StepManager(mock_token, mock_context) + + with patch.object(manager, 'read') as mock_read: + mock_read.return_value = { + "id": "step_123", + "step_type": "pause", + "duration": 5 + } + + result = await manager.add_assertion_to_step( + "bucket_abc", + "test_123", + "step_123", + "response_status", + "equals", + None, + "200" + ) + + assert result.error is not None + assert "cannot have an assertion added" in result.error + From 8fa484581f034f6d11ea8c389419333da0b2e916 Mon Sep 17 00:00:00 2001 From: Ben Thompson Date: Thu, 2 Jul 2026 18:33:36 -0700 Subject: [PATCH 3/3] Emit Content-Type header values as arrays in add_body_to_step The REST API documents header values as arrays of strings (e.g. {"Content-Type": ["application/json"]}); bare strings relied on undocumented server leniency. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01S2Ym1SshabyXjWNdeGFDfx --- src/tools/step_manager.py | 8 ++++---- tests/test_step_manager.py | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/tools/step_manager.py b/src/tools/step_manager.py index 8fb0f41..2a56fac 100644 --- a/src/tools/step_manager.py +++ b/src/tools/step_manager.py @@ -88,7 +88,7 @@ async def add_body_to_step( request_step_body["body"] = safe_json except json.JSONDecodeError as e: return BaseResult(error=f"Invalid JSON content provided for body_content: {str(e)}") - request_headers["Content-Type"] = "application/json" + request_headers["Content-Type"] = ["application/json"] case "xml": try: @@ -101,7 +101,7 @@ async def add_body_to_step( return BaseResult(error=f"Invalid XML content provided for body_content: {str(e)}") except Exception as e: return BaseResult(error=f"Error processing XML content: {str(e)}") - request_headers["Content-Type"] = "application/xml" + request_headers["Content-Type"] = ["application/xml"] case "html": try: @@ -110,7 +110,7 @@ async def add_body_to_step( request_step_body["body"] = safe_html except Exception as e: return BaseResult(error=f"Error processing HTML content: {str(e)}") - request_headers["Content-Type"] = "text/html" + request_headers["Content-Type"] = ["text/html"] case "text": # Remove any null bytes and control characters except newlines/tabs @@ -121,7 +121,7 @@ async def add_body_to_step( request_step_body["body"] = safe_text except Exception as e: return BaseResult(error=f"Error processing text content: {str(e)}") - request_headers["Content-Type"] = "text/plain" + request_headers["Content-Type"] = ["text/plain"] case _: return BaseResult( error=f"Unsupported body_type {body_type}. Supported types are: json, xml, html, text" diff --git a/tests/test_step_manager.py b/tests/test_step_manager.py index 83cd0aa..6fe33be 100644 --- a/tests/test_step_manager.py +++ b/tests/test_step_manager.py @@ -120,6 +120,9 @@ async def test_add_body_to_step_json(self, mock_token, mock_context): ) assert result.error is None + # Header values must be arrays per the REST API contract + put_payload = mock_api.call_args.kwargs["json"] + assert put_payload["headers"]["Content-Type"] == ["application/json"] async def test_add_body_to_step_xml(self, mock_token, mock_context): """Test adding XML body to step"""