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:
@@ -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"]
|
||||
|
||||
Reference in New Issue
Block a user