From 6e7768f965735827586a79e4d3f2e156889770a0 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 8 May 2026 10:12:50 +0000 Subject: [PATCH 1/6] refactor: make changes to auth failed error message --- src/blueapi/utils/numtracker.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/blueapi/utils/numtracker.py b/src/blueapi/utils/numtracker.py index 91e3c66c2a..bb6cccccea 100644 --- a/src/blueapi/utils/numtracker.py +++ b/src/blueapi/utils/numtracker.py @@ -105,6 +105,14 @@ async def create_scan( json = response.json() if json.get("errors") is not None: + for error in json["errors"]: + code = error.get("extensions").get("code") + + if code == "AUTH_FAILED": + raise RuntimeError( + f"Not authorised to create a scan number for " + f"{instrument} and {instrument_session}." + ) raise RuntimeError(f"Numtracker error: {json['errors']}") new_collection = NumtrackerScanMutationResponse.model_validate(json["data"]) From c421585333b2ecf6058ca953c81fddebaf25caef Mon Sep 17 00:00:00 2001 From: root Date: Fri, 8 May 2026 14:04:40 +0000 Subject: [PATCH 2/6] test: add runtime auth error test --- src/blueapi/utils/numtracker.py | 2 +- tests/unit_tests/utils/test_numtracker.py | 28 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/blueapi/utils/numtracker.py b/src/blueapi/utils/numtracker.py index bb6cccccea..ddd489f877 100644 --- a/src/blueapi/utils/numtracker.py +++ b/src/blueapi/utils/numtracker.py @@ -106,7 +106,7 @@ async def create_scan( if json.get("errors") is not None: for error in json["errors"]: - code = error.get("extensions").get("code") + code = (error.get("extensions") or {}).get("code") if code == "AUTH_FAILED": raise RuntimeError( diff --git a/tests/unit_tests/utils/test_numtracker.py b/tests/unit_tests/utils/test_numtracker.py index 859dbaf098..c69ff0b01d 100644 --- a/tests/unit_tests/utils/test_numtracker.py +++ b/tests/unit_tests/utils/test_numtracker.py @@ -33,6 +33,18 @@ def numtracker() -> NumtrackerClient: ], } +AUTH_ERROR = { + "data": None, + "errors": [ + { + "message": "No configuration available for instrument p46", + "locations": [{"line": 3, "column": 5}], + "path": ["scan"], + "extensions": {"code": "AUTH_FAILED"}, + } + ], +} + async def test_create_scan( numtracker: NumtrackerClient, httpx_mock: HTTPXMock, nt_query, nt_response @@ -109,3 +121,19 @@ async def test_create_scan_raises_runtime_error_on_graphql_error( ) with pytest.raises(RuntimeError, match="Numtracker error:"): await numtracker.create_scan("ab123", "p46") + + +async def test_create_scan_raises_runtime_auth_error_on_graphql_error( + numtracker: NumtrackerClient, httpx_mock: HTTPXMock, nt_query +): + httpx_mock.add_response( + method="POST", + url=URL, + match_json=nt_query, + status_code=200, + json=AUTH_ERROR, + ) + with pytest.raises( + RuntimeError, match="Not authorised to create a scan number for p46 and ab123" + ): + await numtracker.create_scan("ab123", "p46") From 0e40a4b58d2ae0bddda2e7d585e84d5a1afcae87 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 18 May 2026 08:22:02 +0000 Subject: [PATCH 3/6] refactor: use match case for numtracker auth error exts --- src/blueapi/utils/numtracker.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/blueapi/utils/numtracker.py b/src/blueapi/utils/numtracker.py index ddd489f877..71b4c20b3f 100644 --- a/src/blueapi/utils/numtracker.py +++ b/src/blueapi/utils/numtracker.py @@ -106,14 +106,19 @@ async def create_scan( if json.get("errors") is not None: for error in json["errors"]: - code = (error.get("extensions") or {}).get("code") - - if code == "AUTH_FAILED": - raise RuntimeError( - f"Not authorised to create a scan number for " - f"{instrument} and {instrument_session}." - ) - raise RuntimeError(f"Numtracker error: {json['errors']}") + code = error.get("extensions", {}).get("code") + match code: + case "AUTH_FAILED": + raise RuntimeError( + f"Not authorised to create a scan number for " + f"{instrument} and {instrument_session}" + ) + case "AUTH_MISSING": + raise RuntimeError("Numtracker authentication missing") + case "AUTH_SERVER_ERROR": + raise RuntimeError("Server authentication error") + case _: + raise RuntimeError(f"Numtracker error: {json['errors']}") new_collection = NumtrackerScanMutationResponse.model_validate(json["data"]) LOGGER.debug("New NumtrackerNewScan: %s", new_collection) From 91de46df0206b5f9fdbc629e45061e0ae84433dd Mon Sep 17 00:00:00 2001 From: root Date: Mon, 18 May 2026 08:26:15 +0000 Subject: [PATCH 4/6] test: use pytest parametrise for auth error test --- tests/unit_tests/utils/test_numtracker.py | 46 ++++++++++++++--------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/tests/unit_tests/utils/test_numtracker.py b/tests/unit_tests/utils/test_numtracker.py index c69ff0b01d..1df59d8be3 100644 --- a/tests/unit_tests/utils/test_numtracker.py +++ b/tests/unit_tests/utils/test_numtracker.py @@ -33,18 +33,6 @@ def numtracker() -> NumtrackerClient: ], } -AUTH_ERROR = { - "data": None, - "errors": [ - { - "message": "No configuration available for instrument p46", - "locations": [{"line": 3, "column": 5}], - "path": ["scan"], - "extensions": {"code": "AUTH_FAILED"}, - } - ], -} - async def test_create_scan( numtracker: NumtrackerClient, httpx_mock: HTTPXMock, nt_query, nt_response @@ -123,17 +111,39 @@ async def test_create_scan_raises_runtime_error_on_graphql_error( await numtracker.create_scan("ab123", "p46") -async def test_create_scan_raises_runtime_auth_error_on_graphql_error( - numtracker: NumtrackerClient, httpx_mock: HTTPXMock, nt_query +@pytest.mark.parametrize( + ("auth_error_code", "expected_message"), + [ + ("AUTH_FAILED", "Not authorised to create a scan number for p46 and ab123"), + ("AUTH_MISSING", "Numtracker authentication missing"), + ("AUTH_SERVER_ERROR", "Server authentication error"), + ("UNKWNOWN_ELSE", "Numtracker error:"), + ], +) +async def test_numtracker_auth_error_types( + numtracker: NumtrackerClient, + httpx_mock: HTTPXMock, + nt_query, + auth_error_code, + expected_message, ): + error_response = { + "data": None, + "errors": [ + { + "message": "xyz", + "locations": [{"line": 3, "column": 5}], + "path": ["scan"], + "extensions": {"code": auth_error_code}, + } + ], + } httpx_mock.add_response( method="POST", url=URL, match_json=nt_query, status_code=200, - json=AUTH_ERROR, + json=error_response, ) - with pytest.raises( - RuntimeError, match="Not authorised to create a scan number for p46 and ab123" - ): + with pytest.raises(RuntimeError, match=expected_message): await numtracker.create_scan("ab123", "p46") From 916214ac2bd5c8329ac912efde964d71bb2e576c Mon Sep 17 00:00:00 2001 From: root Date: Mon, 18 May 2026 10:05:40 +0000 Subject: [PATCH 5/6] refactor: move fallback case out of for loop --- src/blueapi/utils/numtracker.py | 2 +- tests/unit_tests/utils/test_numtracker.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/blueapi/utils/numtracker.py b/src/blueapi/utils/numtracker.py index 71b4c20b3f..d187fa40a0 100644 --- a/src/blueapi/utils/numtracker.py +++ b/src/blueapi/utils/numtracker.py @@ -116,7 +116,7 @@ async def create_scan( case "AUTH_MISSING": raise RuntimeError("Numtracker authentication missing") case "AUTH_SERVER_ERROR": - raise RuntimeError("Server authentication error") + raise RuntimeError("Numtracker server authentication error") case _: raise RuntimeError(f"Numtracker error: {json['errors']}") diff --git a/tests/unit_tests/utils/test_numtracker.py b/tests/unit_tests/utils/test_numtracker.py index 1df59d8be3..2401b2e1be 100644 --- a/tests/unit_tests/utils/test_numtracker.py +++ b/tests/unit_tests/utils/test_numtracker.py @@ -117,7 +117,7 @@ async def test_create_scan_raises_runtime_error_on_graphql_error( ("AUTH_FAILED", "Not authorised to create a scan number for p46 and ab123"), ("AUTH_MISSING", "Numtracker authentication missing"), ("AUTH_SERVER_ERROR", "Server authentication error"), - ("UNKWNOWN_ELSE", "Numtracker error:"), + ("UNKNOWN_ERROR", "Numtracker error:"), ], ) async def test_numtracker_auth_error_types( From 9c50c9819e40c064ce9175a578d21f911d8cdaa4 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 18 May 2026 10:56:10 +0000 Subject: [PATCH 6/6] refactor: move fallback out of for loop --- src/blueapi/utils/numtracker.py | 3 +-- tests/unit_tests/utils/test_numtracker.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/blueapi/utils/numtracker.py b/src/blueapi/utils/numtracker.py index d187fa40a0..0fde7c699e 100644 --- a/src/blueapi/utils/numtracker.py +++ b/src/blueapi/utils/numtracker.py @@ -117,8 +117,7 @@ async def create_scan( raise RuntimeError("Numtracker authentication missing") case "AUTH_SERVER_ERROR": raise RuntimeError("Numtracker server authentication error") - case _: - raise RuntimeError(f"Numtracker error: {json['errors']}") + raise RuntimeError(f"Numtracker error: {json['errors']}") new_collection = NumtrackerScanMutationResponse.model_validate(json["data"]) LOGGER.debug("New NumtrackerNewScan: %s", new_collection) diff --git a/tests/unit_tests/utils/test_numtracker.py b/tests/unit_tests/utils/test_numtracker.py index 2401b2e1be..1aa6df9e88 100644 --- a/tests/unit_tests/utils/test_numtracker.py +++ b/tests/unit_tests/utils/test_numtracker.py @@ -116,7 +116,7 @@ async def test_create_scan_raises_runtime_error_on_graphql_error( [ ("AUTH_FAILED", "Not authorised to create a scan number for p46 and ab123"), ("AUTH_MISSING", "Numtracker authentication missing"), - ("AUTH_SERVER_ERROR", "Server authentication error"), + ("AUTH_SERVER_ERROR", "Numtracker server authentication error"), ("UNKNOWN_ERROR", "Numtracker error:"), ], )