From b0be397f2edd77550f3fd2dedc56bbf834967c72 Mon Sep 17 00:00:00 2001 From: Felix Shi <3283655504@qq.com> Date: Thu, 29 Oct 2026 12:37:20 +0000 Subject: [PATCH] refactor(api): enable strict typing for web/openapi controller test files (#44890) Co-authored-by: Felix Shi <204130665+shi-felix@users.noreply.github.com> --- .../controllers/openapi/test__errors.py | 61 ++++++++++++------- .../controllers/openapi/test_account.py | 8 +-- .../unit_tests/controllers/web/test_app.py | 7 ++- .../controllers/web/test_completion.py | 24 +++++--- .../controllers/web/test_conversation.py | 30 ++++----- .../unit_tests/controllers/web/test_login.py | 2 +- .../controllers/web/test_workflow.py | 9 ++- api/tests/unit_tests/pyrefly.toml | 7 --- 8 files changed, 85 insertions(+), 63 deletions(-) diff --git a/api/tests/unit_tests/controllers/openapi/test__errors.py b/api/tests/unit_tests/controllers/openapi/test__errors.py index f2b5e9d8edc..54115b3b02c 100644 --- a/api/tests/unit_tests/controllers/openapi/test__errors.py +++ b/api/tests/unit_tests/controllers/openapi/test__errors.py @@ -2,9 +2,10 @@ import uuid from types import SimpleNamespace -from unittest.mock import patch +from unittest.mock import MagicMock, patch import pytest +from flask import Flask from sqlalchemy.orm import Session from werkzeug.exceptions import ( BadGateway, @@ -70,14 +71,14 @@ def fmt() -> OpenApiErrorFormatter: class TestErrorBodyModel: - def test_minimal_body_serializes_without_optional_fields(self): + def test_minimal_body_serializes_without_optional_fields(self) -> None: body = ErrorBody(code=OpenApiErrorCode.NOT_FOUND, message="app not found", status=404) wire = body.model_dump(mode="json", exclude_none=True) assert wire == {"code": "not_found", "message": "app not found", "status": 404} - def test_full_body_round_trips(self): + def test_full_body_round_trips(self) -> None: body = ErrorBody( code=OpenApiErrorCode.INVALID_PARAM, message="Request validation failed", @@ -91,7 +92,7 @@ class TestErrorBodyModel: assert wire["details"] == [{"type": "int_parsing", "loc": ["page"], "msg": "must be >= 1"}] assert ErrorBody.model_validate(wire) == body - def test_code_field_is_open_string_for_forward_compat(self): + def test_code_field_is_open_string_for_forward_compat(self) -> None: # Old CLIs must not hard-fail when a future server adds a code, so the # schema type is str; enum membership is enforced by the formatter tests. body = ErrorBody.model_validate({"code": "some_future_code", "message": "x", "status": 400}) @@ -99,12 +100,22 @@ class TestErrorBodyModel: assert body.code == "some_future_code" +class _DataCarryingUnprocessableEntity(UnprocessableEntity): + """flask_restx attaches ``abort(422, ...)`` kwargs as ``e.data`` at runtime. + + Declaring the attribute keeps the type checker honest without an ignore, and + the instance is still the real ``UnprocessableEntity`` the framework raises. + """ + + data: dict[str, object] + + class TestOpenApiErrorFormatter: - def test_flask_restx_abort_data_path_yields_canonical_body(self, fmt): + def test_flask_restx_abort_data_path_yields_canonical_body(self, fmt: OpenApiErrorFormatter) -> None: # Simulates _contract.py's abort(422, message=..., errors=...): flask_restx # attaches kwargs to e.data, which handle_error would otherwise put on the # wire verbatim (no code/status). - e = UnprocessableEntity() + e = _DataCarryingUnprocessableEntity() e.data = { "message": "Request validation failed", "errors": [{"type": "int_parsing", "loc": ["page"], "msg": "must be >= 1", "extra": "drop me"}], @@ -120,8 +131,8 @@ class TestOpenApiErrorFormatter: # the override channel now carries the canonical body assert e.data == wire - def test_finalize_is_idempotent(self, fmt): - e = UnprocessableEntity() + def test_finalize_is_idempotent(self, fmt: OpenApiErrorFormatter) -> None: + e = _DataCarryingUnprocessableEntity() e.data = { "message": "Request validation failed", "errors": [{"type": "int_parsing", "loc": ["page"], "msg": "must be >= 1"}], @@ -133,10 +144,10 @@ class TestOpenApiErrorFormatter: assert second == first - def test_malformed_canonical_details_falls_back_instead_of_raising(self, fmt): + def test_malformed_canonical_details_falls_back_instead_of_raising(self, fmt: OpenApiErrorFormatter) -> None: # finalize runs inside the framework error handler; a ValidationError # escaping it would replace the response with an unformatted 500 - e = UnprocessableEntity() + e = _DataCarryingUnprocessableEntity() e.data = {"message": "broken", "details": [{"bad": "shape"}]} data = {"code": "unprocessable_entity", "message": "broken", "status": 422} @@ -144,7 +155,7 @@ class TestOpenApiErrorFormatter: assert wire == {"code": "invalid_param", "message": "Unprocessable Entity", "status": 422} - def test_params_shape_becomes_details(self, fmt): + def test_params_shape_becomes_details(self, fmt: OpenApiErrorFormatter) -> None: e = ValueError("is required") data = {"code": "invalid_param", "message": "is required", "params": "email", "status": 400} @@ -153,7 +164,7 @@ class TestOpenApiErrorFormatter: assert "params" not in wire assert wire["details"] == [{"type": "invalid", "loc": ["email"], "msg": "is required"}] - def test_catch_all_exception_never_leaks_str_e(self, fmt): + def test_catch_all_exception_never_leaks_str_e(self, fmt: OpenApiErrorFormatter) -> None: e = RuntimeError("postgres password=hunter2 connection refused") data = {"message": str(e), "code": "unknown", "status": 500} @@ -162,7 +173,7 @@ class TestOpenApiErrorFormatter: assert wire["code"] == "internal_server_error" assert "hunter2" not in wire["message"] - def test_unmapped_status_falls_back_to_unknown(self, fmt): + def test_unmapped_status_falls_back_to_unknown(self, fmt: OpenApiErrorFormatter) -> None: from werkzeug.exceptions import Gone e = Gone() @@ -173,7 +184,7 @@ class TestOpenApiErrorFormatter: assert wire["code"] == "unknown" @pytest.mark.parametrize("message", [None, "custom reason"], ids=["class_description", "call_site_override"]) - def test_openapi_error_subclass_is_throw_and_done(self, fmt, message): + def test_openapi_error_subclass_is_throw_and_done(self, fmt: OpenApiErrorFormatter, message: str) -> None: # The dedicated throwable: subclass declares status + code + message once, # call sites just `raise`, or pass a message to override the description; # the formatter emits everything verbatim. @@ -194,7 +205,9 @@ class TestOpenApiErrorFormatter: class TestQuotaExceptions: @pytest.mark.parametrize("exc_class", [MemberLimitExceeded, MemberLicenseExceeded]) - def test_quota_exception_carries_declared_code_and_message(self, fmt, exc_class): + def test_quota_exception_carries_declared_code_and_message( + self, fmt: OpenApiErrorFormatter, exc_class: type[OpenApiError] + ) -> None: # Single source: assertions read the class attributes, no re-typed strings. e = exc_class() data = {"code": "forbidden", "message": e.description, "status": 403} @@ -211,8 +224,8 @@ class TestWireContract: """End-to-end: request in, canonical JSON out, through the real openapi blueprint.""" def test_accepts_422_carries_code_status_details( - self, openapi_app, sqlite_session: Session, monkeypatch: pytest.MonkeyPatch - ): + self, openapi_app: Flask, sqlite_session: Session, monkeypatch: pytest.MonkeyPatch + ) -> None: """`@endpoint` runs auth outermost, so reaching `@accepts` means passing it: one account row and a stubbed authenticator are the whole world `GET /account/sessions` needs. @@ -250,7 +263,7 @@ class TestWireContract: assert wire["status"] == 422 assert wire["details"] - def test_unknown_route_404_is_canonical_without_route_suggestions(self, openapi_app): + def test_unknown_route_404_is_canonical_without_route_suggestions(self, openapi_app: Flask) -> None: client = openapi_app.test_client() resp = client.get("/openapi/v1/definitely-not-a-route") @@ -261,7 +274,7 @@ class TestWireContract: assert wire["code"] == "not_found" assert "did you mean" not in wire["message"].lower() - def test_404_outside_blueprint_prefix_is_not_claimed(self, openapi_app): + def test_404_outside_blueprint_prefix_is_not_claimed(self, openapi_app: Flask) -> None: # catch_all_404s wraps the app-level exception handler; the prefix # guard must keep non-/openapi/v1 paths on the app's own 404 handling client = openapi_app.test_client() @@ -273,7 +286,9 @@ class TestWireContract: assert "application/json" not in (resp.content_type or "") @patch("controllers.openapi.oauth_device.application_services") - def test_oauth_device_token_keeps_rfc8628_shape(self, mock_application_services, openapi_app): + def test_oauth_device_token_keeps_rfc8628_shape( + self, mock_application_services: MagicMock, openapi_app: Flask + ) -> None: mock_application_services.return_value.oauth_device.poll.side_effect = ExpiredTokenError client = openapi_app.test_client() @@ -333,7 +348,9 @@ class TestErrorMatrix: ERROR_MATRIX, ids=lambda v: type(v).__name__ if isinstance(v, Exception) else str(v), ) - def test_every_known_error_path_yields_canonical_code(self, fmt, exc, status, expected_code): + def test_every_known_error_path_yields_canonical_code( + self, fmt: OpenApiErrorFormatter, exc: Exception, status: int, expected_code: str + ) -> None: data = dict(getattr(exc, "data", None) or {"message": str(exc), "status": status}) wire = fmt.finalize(exc, data, status) @@ -345,7 +362,7 @@ class TestErrorMatrix: class TestErrorCodeEnumRegistration: - def test_enum_registered_with_all_values(self): + def test_enum_registered_with_all_values(self) -> None: from controllers.openapi import openapi_ns from controllers.openapi._errors import OpenApiErrorCode diff --git a/api/tests/unit_tests/controllers/openapi/test_account.py b/api/tests/unit_tests/controllers/openapi/test_account.py index 13f86049a3c..cfd49784fd0 100644 --- a/api/tests/unit_tests/controllers/openapi/test_account.py +++ b/api/tests/unit_tests/controllers/openapi/test_account.py @@ -33,7 +33,7 @@ def test_revoke_by_id_hides_a_session_the_caller_does_not_own( _stub_account_service(monkeypatch) with app.test_request_context(f"/openapi/v1/account/sessions/{session_id}", method="DELETE"): with pytest.raises(NotFound, match="session not found"): - api.delete.__handler__(api, _ctx(), session_id=session_id) + api.delete.__handler__(api, _ctx(), session_id=session_id) # type: ignore[attr-defined] # --- GET /account/sessions query validation. The application service is replaced @@ -61,12 +61,12 @@ def _stub_account_service(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(mod, "application_services", lambda: services) -def test_sessions_list_valid_query_parses_page_and_limit(app: Flask, monkeypatch: pytest.MonkeyPatch): +def test_sessions_list_valid_query_parses_page_and_limit(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: """A valid page/limit round-trips through SessionListQuery into the response envelope.""" api = AccountSessionsApi() _stub_account_service(monkeypatch) with app.test_request_context("/openapi/v1/account/sessions?page=2&limit=5"): - result = api.get.__handler__(api, _ctx(), query=SessionListQuery(page=2, limit=5)) + result = api.get.__handler__(api, _ctx(), query=SessionListQuery(page=2, limit=5)) # type: ignore[attr-defined] assert result.page == 2 assert result.limit == 5 assert result.total == 0 @@ -82,6 +82,6 @@ def test_sessions_list_valid_query_parses_page_and_limit(app: Flask, monkeypatch {"foo": "bar"}, ], ) -def test_session_list_query_rejects_out_of_bounds(params): +def test_session_list_query_rejects_out_of_bounds(params: dict[str, object]) -> None: with pytest.raises(ValidationError): SessionListQuery.model_validate(params) diff --git a/api/tests/unit_tests/controllers/web/test_app.py b/api/tests/unit_tests/controllers/web/test_app.py index bb661be0c2c..64ec70a12db 100644 --- a/api/tests/unit_tests/controllers/web/test_app.py +++ b/api/tests/unit_tests/controllers/web/test_app.py @@ -17,9 +17,10 @@ from controllers.web.error import ( WebAppAuthRequiredError, WebAppNotFoundError, ) -from core.app.app_config.common.parameters_mapping import get_parameters_from_feature_dict +from core.app.app_config.common.parameters_mapping import AppParametersDict, get_parameters_from_feature_dict from enums import WebAppAccessMode from extensions.ext_application_services import ApplicationServices +from libs.exception import BaseHTTPException from libs.passport import PassportService from models.enums import EndUserType from models.model import App, AppMode, EndUser @@ -79,7 +80,7 @@ class TestAppParameterApi: ) -> None: calls: list[str] = [] - def get_public_parameters(app_id: str): + def get_public_parameters(app_id: str) -> AppParametersDict: calls.append(app_id) return get_parameters_from_feature_dict( features_dict={"opening_statement": "Hello"}, @@ -233,7 +234,7 @@ class TestAppAccessMode: real_application_services: ApplicationServices, monkeypatch: pytest.MonkeyPatch, service_error: Exception, - http_error: type[Exception], + http_error: type[BaseHTTPException], expected_data: dict[str, object], app: Flask, ) -> None: diff --git a/api/tests/unit_tests/controllers/web/test_completion.py b/api/tests/unit_tests/controllers/web/test_completion.py index c42856c193c..63b400ff813 100644 --- a/api/tests/unit_tests/controllers/web/test_completion.py +++ b/api/tests/unit_tests/controllers/web/test_completion.py @@ -83,7 +83,8 @@ class TestCompletionApi: @patch("controllers.web.completion.AppGenerateService.generate") @patch("controllers.web.completion.web_ns") def test_happy_path(self, mock_ns: MagicMock, mock_gen: MagicMock, mock_compact: MagicMock, app: Flask) -> None: - mock_ns.payload = {"inputs": {}, "query": "test"} + empty_inputs: dict[str, object] = {} + mock_ns.payload = {"inputs": empty_inputs, "query": "test"} mock_gen.return_value = "response-obj" with app.test_request_context("/completion-messages", method="POST"): @@ -97,7 +98,8 @@ class TestCompletionApi: ) @patch("controllers.web.completion.web_ns") def test_provider_not_init_error(self, mock_ns: MagicMock, mock_gen: MagicMock, app: Flask) -> None: - mock_ns.payload = {"inputs": {}} + empty_inputs: dict[str, object] = {} + mock_ns.payload = {"inputs": empty_inputs} with app.test_request_context("/completion-messages", method="POST"): with pytest.raises(ProviderNotInitializeError): @@ -109,7 +111,8 @@ class TestCompletionApi: ) @patch("controllers.web.completion.web_ns") def test_quota_exceeded_error(self, mock_ns: MagicMock, mock_gen: MagicMock, app: Flask) -> None: - mock_ns.payload = {"inputs": {}} + empty_inputs: dict[str, object] = {} + mock_ns.payload = {"inputs": empty_inputs} with app.test_request_context("/completion-messages", method="POST"): with pytest.raises(ProviderQuotaExceededError): @@ -121,7 +124,8 @@ class TestCompletionApi: ) @patch("controllers.web.completion.web_ns") def test_model_not_support_error(self, mock_ns: MagicMock, mock_gen: MagicMock, app: Flask) -> None: - mock_ns.payload = {"inputs": {}} + empty_inputs: dict[str, object] = {} + mock_ns.payload = {"inputs": empty_inputs} with app.test_request_context("/completion-messages", method="POST"): with pytest.raises(ProviderModelCurrentlyNotSupportError): @@ -159,7 +163,8 @@ class TestChatApi: @patch("controllers.web.completion.AppGenerateService.generate") @patch("controllers.web.completion.web_ns") def test_happy_path(self, mock_ns: MagicMock, mock_gen: MagicMock, mock_compact: MagicMock, app: Flask) -> None: - mock_ns.payload = {"inputs": {}, "query": "hi"} + empty_inputs: dict[str, object] = {} + mock_ns.payload = {"inputs": empty_inputs, "query": "hi"} mock_gen.return_value = "response" with app.test_request_context("/chat-messages", method="POST"): @@ -173,7 +178,8 @@ class TestChatApi: ) @patch("controllers.web.completion.web_ns") def test_invoke_error_mapped(self, mock_ns: MagicMock, mock_gen: MagicMock, app: Flask) -> None: - mock_ns.payload = {"inputs": {}, "query": "x"} + empty_inputs: dict[str, object] = {} + mock_ns.payload = {"inputs": empty_inputs, "query": "x"} with app.test_request_context("/chat-messages", method="POST"): with pytest.raises(CompletionRequestError): @@ -185,7 +191,8 @@ class TestChatApi: ) @patch("controllers.web.completion.web_ns") def test_agent_not_published_error_mapped(self, mock_ns: MagicMock, mock_gen: MagicMock, app: Flask) -> None: - mock_ns.payload = {"inputs": {}, "query": "x"} + empty_inputs: dict[str, object] = {} + mock_ns.payload = {"inputs": empty_inputs, "query": "x"} app_model = _app(AppMode.AGENT) with app.test_request_context("/chat-messages", method="POST"): @@ -203,7 +210,8 @@ class TestChatApi: app: Flask, unbound_session: Session, ) -> None: - mock_ns.payload = {"inputs": {}, "query": "hi", "conversation_id": str(uuid.uuid4())} + empty_inputs: dict[str, object] = {} + mock_ns.payload = {"inputs": empty_inputs, "query": "hi", "conversation_id": str(uuid.uuid4())} session = unbound_session with app.test_request_context("/chat-messages", method="POST"): diff --git a/api/tests/unit_tests/controllers/web/test_conversation.py b/api/tests/unit_tests/controllers/web/test_conversation.py index 2bf1e40fa33..be18dd776e0 100644 --- a/api/tests/unit_tests/controllers/web/test_conversation.py +++ b/api/tests/unit_tests/controllers/web/test_conversation.py @@ -125,7 +125,7 @@ class TestCompletionMessagePayload: def test_invalid_response_mode(self) -> None: with pytest.raises(ValidationError): - CompletionMessagePayload(inputs={}, response_mode="invalid") + CompletionMessagePayload(inputs={}, response_mode="invalid") # pyrefly: ignore[bad-argument-type] class TestChatMessagePayload: @@ -151,7 +151,7 @@ class TestChatMessagePayload: def test_query_required(self) -> None: with pytest.raises(ValidationError): - ChatMessagePayload(inputs={}) + ChatMessagePayload(inputs={}) # pyrefly: ignore[missing-argument] class TestConversationListQuery: @@ -164,11 +164,11 @@ class TestConversationListQuery: def test_limit_lower_bound(self) -> None: with pytest.raises(ValidationError): - ConversationListQuery(limit=0) + ConversationListQuery(limit=0) # pyrefly: ignore[bad-argument-type] def test_limit_upper_bound(self) -> None: with pytest.raises(ValidationError): - ConversationListQuery(limit=101) + ConversationListQuery(limit=101) # pyrefly: ignore[bad-argument-type] def test_limit_boundaries_valid(self) -> None: assert ConversationListQuery(limit=1).limit == 1 @@ -180,7 +180,7 @@ class TestConversationListQuery: def test_invalid_sort_by(self) -> None: with pytest.raises(ValidationError): - ConversationListQuery(sort_by="invalid") + ConversationListQuery(sort_by="invalid") # pyrefly: ignore[bad-argument-type] def test_valid_last_id(self) -> None: uid = str(uuid4()) @@ -229,9 +229,9 @@ class TestMessageListQuery: def test_limit_bounds(self) -> None: cid = str(uuid4()) with pytest.raises(ValidationError): - MessageListQuery(conversation_id=cid, limit=0) + MessageListQuery(conversation_id=cid, limit=0) # pyrefly: ignore[bad-argument-type] with pytest.raises(ValidationError): - MessageListQuery(conversation_id=cid, limit=101) + MessageListQuery(conversation_id=cid, limit=101) # pyrefly: ignore[bad-argument-type] def test_valid_first_id(self) -> None: cid = str(uuid4()) @@ -257,7 +257,7 @@ class TestMessageFeedbackPayload: def test_invalid_rating(self) -> None: with pytest.raises(ValidationError): - MessageFeedbackPayload(rating="neutral") + MessageFeedbackPayload(rating="neutral") # pyrefly: ignore[bad-argument-type] class TestMessageMoreLikeThisQuery: @@ -267,11 +267,11 @@ class TestMessageMoreLikeThisQuery: def test_invalid_mode(self) -> None: with pytest.raises(ValidationError): - MessageMoreLikeThisQuery(response_mode="invalid") + MessageMoreLikeThisQuery(response_mode="invalid") # pyrefly: ignore[bad-argument-type] def test_required(self) -> None: with pytest.raises(ValidationError): - MessageMoreLikeThisQuery() + MessageMoreLikeThisQuery() # pyrefly: ignore[missing-argument] class TestRemoteFileUploadPayload: @@ -286,7 +286,7 @@ class TestRemoteFileUploadPayload: def test_url_required(self) -> None: with pytest.raises(ValidationError): - RemoteFileUploadPayload() + RemoteFileUploadPayload() # pyrefly: ignore[missing-argument] class TestSavedMessageListQuery: @@ -297,9 +297,9 @@ class TestSavedMessageListQuery: def test_limit_bounds(self) -> None: with pytest.raises(ValidationError): - SavedMessageListQuery(limit=0) + SavedMessageListQuery(limit=0) # pyrefly: ignore[bad-argument-type] with pytest.raises(ValidationError): - SavedMessageListQuery(limit=101) + SavedMessageListQuery(limit=101) # pyrefly: ignore[bad-argument-type] def test_valid_last_id(self) -> None: uid = str(uuid4()) @@ -319,7 +319,7 @@ class TestSavedMessageCreatePayload: def test_required(self) -> None: with pytest.raises(ValidationError): - SavedMessageCreatePayload() + SavedMessageCreatePayload() # pyrefly: ignore[missing-argument] class TestWorkflowRunPayload: @@ -334,7 +334,7 @@ class TestWorkflowRunPayload: def test_inputs_required(self) -> None: with pytest.raises(ValidationError): - WorkflowRunPayload() + WorkflowRunPayload() # pyrefly: ignore[missing-argument] class TestForgotPasswordSendPayload: diff --git a/api/tests/unit_tests/controllers/web/test_login.py b/api/tests/unit_tests/controllers/web/test_login.py index d2a05c06697..8e6e79f2913 100644 --- a/api/tests/unit_tests/controllers/web/test_login.py +++ b/api/tests/unit_tests/controllers/web/test_login.py @@ -202,7 +202,7 @@ def test_unavailable_identity_is_canonical_404_before_authentication( id=app_id, tenant_id=app_id, name="Private fixture", - mode="chat", + mode="chat", # pyrefly: ignore[bad-argument-type] enable_site=condition != "app-disabled", enable_api=True, app_model_config_id=config_id if condition != "unpublished" else None, diff --git a/api/tests/unit_tests/controllers/web/test_workflow.py b/api/tests/unit_tests/controllers/web/test_workflow.py index 79597949d33..26740c0ae6c 100644 --- a/api/tests/unit_tests/controllers/web/test_workflow.py +++ b/api/tests/unit_tests/controllers/web/test_workflow.py @@ -61,7 +61,8 @@ class TestWorkflowRunApi: ) @patch("controllers.web.workflow.web_ns") def test_provider_not_init(self, mock_ns: MagicMock, mock_gen: MagicMock, app: Flask) -> None: - mock_ns.payload = {"inputs": {}} + empty_inputs: dict[str, object] = {} + mock_ns.payload = {"inputs": empty_inputs} with app.test_request_context("/workflows/run", method="POST"): with pytest.raises(ProviderNotInitializeError): @@ -78,7 +79,8 @@ class TestWorkflowRunApi: mock_gen: MagicMock, app: Flask, ) -> None: - mock_ns.payload = {"inputs": {}} + empty_inputs: dict[str, object] = {} + mock_ns.payload = {"inputs": empty_inputs} with app.test_request_context("/workflows/run", method="POST"): with pytest.raises(TriggerWorkflowServiceModeUnavailableError) as exc_info: @@ -93,7 +95,8 @@ class TestWorkflowRunApi: ) @patch("controllers.web.workflow.web_ns") def test_quota_exceeded(self, mock_ns: MagicMock, mock_gen: MagicMock, app: Flask) -> None: - mock_ns.payload = {"inputs": {}} + empty_inputs: dict[str, object] = {} + mock_ns.payload = {"inputs": empty_inputs} with app.test_request_context("/workflows/run", method="POST"): with pytest.raises(ProviderQuotaExceededError): diff --git a/api/tests/unit_tests/pyrefly.toml b/api/tests/unit_tests/pyrefly.toml index 3b2bbdfe43d..cf241456de9 100644 --- a/api/tests/unit_tests/pyrefly.toml +++ b/api/tests/unit_tests/pyrefly.toml @@ -93,11 +93,9 @@ project-excludes = [ "controllers/inner_api/test_mail.py", "controllers/inner_api/test_runtime_credentials.py", "controllers/mcp/test_mcp.py", - "controllers/openapi/test_account.py", "controllers/openapi/test_app_run.py", "controllers/openapi/test__contract.py", "controllers/openapi/test_oauth_device.py", - "controllers/openapi/test__errors.py", "controllers/openapi/test_human_input_form.py", "controllers/openapi/test__models.py", "controllers/openapi/test_workflow_events.py", @@ -122,14 +120,9 @@ project-excludes = [ "controllers/service_api/test___init__.py", "controllers/trigger/test_trigger.py", "controllers/trigger/test_webhook.py", - "controllers/web/test_app.py", - "controllers/web/test_completion.py", "controllers/web/test_human_input_file_upload.py", "controllers/web/test_human_input_form.py", "controllers/web/test_message.py", - "controllers/web/test_conversation.py", - "controllers/web/test_login.py", - "controllers/web/test_workflow.py", "controllers/web/test_wraps.py", "core/agent/conftest.py", "core/agent/output_parser/test_cot_output_parser.py",