1fanwang commented on code in PR #66888:
URL: https://github.com/apache/airflow/pull/66888#discussion_r3286596878


##########
airflow-core/tests/unit/api_fastapi/common/test_exceptions.py:
##########
@@ -388,6 +391,86 @@ def 
test_handle_multiple_columns_unique_constraint_error_with_stacktrace(
         assert exeinfo_response_error.value.detail == expected_exception.detail
 
 
+class TestDataErrorHandler:
+    handler = _DataErrorHandler()
+
+    @staticmethod
+    def _make_data_error(orig_msg: str) -> DataError:
+        return DataError(
+            statement="INSERT INTO dag_run (conf) VALUES (?)",
+            params={},
+            orig=Exception(orig_msg),
+        )
+
+    @pytest.mark.parametrize(
+        ("orig_msg", "expected_status", "expected_reason"),
+        [
+            pytest.param(
+                "(1406, \"Data too long for column 'conf' at row 1\")",
+                status.HTTP_413_CONTENT_TOO_LARGE,
+                "Payload exceeded database column limit",
+                id="mysql-1406-data-too-long",
+            ),
+            pytest.param(
+                "value too long for type character varying(250)",
+                status.HTTP_413_CONTENT_TOO_LARGE,
+                "Payload exceeded database column limit",
+                id="postgres-value-too-long",
+            ),
+            pytest.param(
+                "string or blob too big",
+                status.HTTP_413_CONTENT_TOO_LARGE,
+                "Payload exceeded database column limit",
+                id="sqlite-blob-too-big",
+            ),
+            pytest.param(
+                "(1264, \"Out of range value for column 'slots' at row 1\")",
+                status.HTTP_422_UNPROCESSABLE_ENTITY,
+                "Value rejected by database",
+                id="mysql-1264-out-of-range",
+            ),
+            pytest.param(
+                "numeric field overflow",
+                status.HTTP_422_UNPROCESSABLE_ENTITY,
+                "Value rejected by database",
+                id="postgres-numeric-field-overflow",
+            ),
+        ],
+    )
+    def test_dataerror_translates_to_actionable_http_response(
+        self,
+        orig_msg: str,
+        expected_status: int,
+        expected_reason: str,
+    ) -> None:
+        exc = self._make_data_error(orig_msg)
+        with pytest.raises(HTTPException) as exc_info:
+            self.handler.exception_handler(Mock(), exc)
+        assert exc_info.value.status_code == expected_status
+        detail = exc_info.value.detail
+        assert isinstance(detail, dict)
+        assert detail["reason"] == expected_reason
+        assert detail["orig_error"] == orig_msg
+        assert "MEDIUMTEXT" in detail["message"]
+
+    def test_dataerror_dispatched_through_fastapi_app(self) -> None:
+        """End-to-end: a route raising DataError returns 413 via the 
registered handler."""
+        app = FastAPI()
+        for h in ERROR_HANDLERS:
+            app.add_exception_handler(h.exception_cls, h.exception_handler)
+
+        @app.post("/test")
+        def trigger_data_error():
+            raise self._make_data_error("(1406, \"Data too long for column 
'conf' at row 1\")")
+
+        response = TestClient(app, raise_server_exceptions=False).post("/test")
+        assert response.status_code == status.HTTP_413_CONTENT_TOO_LARGE

Review Comment:
   Done in 3f8fdc6a20 — added MySQL `1366 Incorrect integer value` and Postgres 
`invalid input syntax for type integer` as additional 422 cases in the 
parametrize block.



##########
airflow-core/tests/unit/api_fastapi/common/test_exceptions.py:
##########
@@ -388,6 +391,86 @@ def 
test_handle_multiple_columns_unique_constraint_error_with_stacktrace(
         assert exeinfo_response_error.value.detail == expected_exception.detail
 
 
+class TestDataErrorHandler:
+    handler = _DataErrorHandler()
+
+    @staticmethod
+    def _make_data_error(orig_msg: str) -> DataError:
+        return DataError(
+            statement="INSERT INTO dag_run (conf) VALUES (?)",
+            params={},
+            orig=Exception(orig_msg),
+        )
+
+    @pytest.mark.parametrize(
+        ("orig_msg", "expected_status", "expected_reason"),
+        [
+            pytest.param(
+                "(1406, \"Data too long for column 'conf' at row 1\")",
+                status.HTTP_413_CONTENT_TOO_LARGE,
+                "Payload exceeded database column limit",
+                id="mysql-1406-data-too-long",
+            ),
+            pytest.param(
+                "value too long for type character varying(250)",
+                status.HTTP_413_CONTENT_TOO_LARGE,
+                "Payload exceeded database column limit",
+                id="postgres-value-too-long",
+            ),
+            pytest.param(
+                "string or blob too big",
+                status.HTTP_413_CONTENT_TOO_LARGE,
+                "Payload exceeded database column limit",
+                id="sqlite-blob-too-big",
+            ),
+            pytest.param(
+                "(1264, \"Out of range value for column 'slots' at row 1\")",
+                status.HTTP_422_UNPROCESSABLE_ENTITY,
+                "Value rejected by database",
+                id="mysql-1264-out-of-range",
+            ),
+            pytest.param(
+                "numeric field overflow",
+                status.HTTP_422_UNPROCESSABLE_ENTITY,
+                "Value rejected by database",
+                id="postgres-numeric-field-overflow",
+            ),
+        ],
+    )
+    def test_dataerror_translates_to_actionable_http_response(
+        self,
+        orig_msg: str,
+        expected_status: int,
+        expected_reason: str,
+    ) -> None:
+        exc = self._make_data_error(orig_msg)
+        with pytest.raises(HTTPException) as exc_info:
+            self.handler.exception_handler(Mock(), exc)
+        assert exc_info.value.status_code == expected_status
+        detail = exc_info.value.detail
+        assert isinstance(detail, dict)
+        assert detail["reason"] == expected_reason
+        assert detail["orig_error"] == orig_msg
+        assert "MEDIUMTEXT" in detail["message"]
+
+    def test_dataerror_dispatched_through_fastapi_app(self) -> None:
+        """End-to-end: a route raising DataError returns 413 via the 
registered handler."""
+        app = FastAPI()
+        for h in ERROR_HANDLERS:
+            app.add_exception_handler(h.exception_cls, h.exception_handler)
+
+        @app.post("/test")
+        def trigger_data_error():
+            raise self._make_data_error("(1406, \"Data too long for column 
'conf' at row 1\")")
+
+        response = TestClient(app, raise_server_exceptions=False).post("/test")
+        assert response.status_code == status.HTTP_413_CONTENT_TOO_LARGE
+        body = response.json()
+        detail = body["detail"]
+        assert detail["reason"] == "Payload exceeded database column limit"
+        assert "MEDIUMTEXT" in detail["message"]
+

Review Comment:
   Done in 3f8fdc6a20 — added `detail["statement"] == self._STATEMENT` and 
`detail["orig_error"] == orig_msg` assertions in both the unit and the 
end-to-end test.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to