test: cover YarnError -> 502 across all YARN-touching tools

In 3b698e1 I added the YarnError exception handler and tested it
against get_job_status only. The other four YARN-touching tools
(get_external_job_status, get_external_job_result,
get_external_job_logs, list_applications) were assumed to be
covered by the same handler but never actually exercised against a
YarnError.

Add one integration test per remaining tool. Each one:
  - saves a Connection
  - mocks the underlying yarn_client symbol the tool uses
    (get_application_status / get_application_logs /
    list_applications_yarn) to raise YarnError with a distinctive
    message
  - hits the tool's route via TestClient
  - asserts HTTP 502 + the YarnError message in the response detail

The new tests prove the YarnError -> 502 contract holds uniformly:
  - test_external_job_status_returns_502_on_yarn_error
    (get_application_status raising "not found")
  - test_external_job_result_returns_502_on_yarn_error
    (get_application_status raising "Connection refused")
  - test_external_job_logs_returns_502_on_yarn_error
    (get_application_logs raising "HTTP 500 cluster overloaded")
  - test_list_applications_returns_502_on_yarn_error
    (list_applications_yarn raising "HTTP 503")

Each one uses a different YARN exception message so the test
distinguishes which code path produced the error. Combined with
the existing get_job_status test, the YarnError -> 502 contract
is now verified end-to-end for all five YARN REST-call sites.

Tests: 405 passed (was 401, +4 net).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-07-09 18:08:20 +08:00
co-authored by Claude Fable 5
parent 3b698e1bdc
commit 6109c6d11a
+110
View File
@@ -579,3 +579,113 @@ def test_yarn_error_returns_502_with_detail(tmp_path, monkeypatch):
detail = r.json()["detail"]
assert "YARN connection failed" in detail
assert "Connection refused" in detail
def test_external_job_status_returns_502_on_yarn_error(tmp_path):
"""get_external_job_status uses get_application_status under the
hood — same handler, same 502, same detail."""
from unittest.mock import patch
from spark_executor.core.yarn_client import YarnError
connection_store.store.save(
Connection(
name="prod",
master="yarn",
yarn_rm_url="http://ccam1:8088",
)
)
c = TestClient(app)
with patch(
"spark_executor.tools.external_jobs.get_application_status",
side_effect=YarnError("YARN application 'application_xxx' not found"),
):
r = c.post(
"/get_external_job_status",
json={"application_id": "application_1740000000001_0001", "connection_name": "prod"},
)
assert r.status_code == 502
assert "not found" in r.json()["detail"]
def test_external_job_result_returns_502_on_yarn_error(tmp_path):
"""get_external_job_result also uses get_application_status (same
underlying YARN endpoint), and exercises the same YarnError path."""
from unittest.mock import patch
from spark_executor.core.yarn_client import YarnError
connection_store.store.save(
Connection(
name="prod",
master="yarn",
yarn_rm_url="http://ccam1:8088",
)
)
c = TestClient(app)
with patch(
"spark_executor.tools.external_jobs.get_application_status",
side_effect=YarnError("YARN connection failed: ConnectError: Connection refused"),
):
r = c.post(
"/get_external_job_result",
json={"application_id": "application_1740000000001_0001", "connection_name": "prod"},
)
assert r.status_code == 502
assert "YARN connection failed" in r.json()["detail"]
def test_external_job_logs_returns_502_on_yarn_error(tmp_path):
"""get_external_job_logs uses get_application_logs — different
function, but raises YarnError the same way. Handler must catch
it the same way."""
from unittest.mock import patch
from spark_executor.core.yarn_client import YarnError
connection_store.store.save(
Connection(
name="prod",
master="yarn",
yarn_rm_url="http://ccam1:8088",
)
)
c = TestClient(app)
with patch(
"spark_executor.tools.external_jobs.get_application_logs",
side_effect=YarnError("YARN GET logs returned HTTP 500: cluster overloaded"),
):
r = c.post(
"/get_external_job_logs",
json={
"application_id": "application_1740000000001_0001",
"connection_name": "prod",
"tail_chars": 5000,
},
)
assert r.status_code == 502
assert "YARN GET logs returned HTTP 500" in r.json()["detail"]
assert "cluster overloaded" in r.json()["detail"]
def test_list_applications_returns_502_on_yarn_error(tmp_path):
"""list_applications uses list_applications_yarn — yet another
YARN-touching tool. Same YarnError -> 502 contract."""
from unittest.mock import patch
from spark_executor.core.yarn_client import YarnError
connection_store.store.save(
Connection(
name="prod",
master="yarn",
yarn_rm_url="http://ccam1:8088",
)
)
c = TestClient(app)
with patch(
"spark_executor.tools.external_jobs.list_applications_yarn",
side_effect=YarnError("YARN list applications failed: 503 Service Unavailable"),
):
r = c.post(
"/list_applications",
json={"connection_name": "prod"},
)
assert r.status_code == 502
assert "YARN list applications failed" in r.json()["detail"]