From b0d33cff0ec7eff8c80808bf55bbe264489a7fb4 Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Wed, 29 Apr 2026 03:38:49 +0000 Subject: [PATCH] Studio: fix 4 failing studio_unit_tests on main Three of the failing tests had drifted from production: 1. test_health_response_reports_desktop_capability_fields stubbed `routes` with a SimpleNamespace that omitted `inference_studio_router`, so importing studio.backend.main raised ImportError. Add the missing router stub. 2. test_local_recipe_token_preserves_desktop_marker and test_local_recipe_token_keeps_web_marker_absent decoded the local provider's api_key as a JWT, but _inject_local_providers now mints a unified sk-unsloth-* internal API key (not a forwarded JWT), so jwt.decode raised "Not enough segments". Renamed and rewrote both tests to validate the API-key contract: starts with storage.API_KEY_PREFIX and authenticates via get_current_subject as the real admin user. The web vs desktop distinction is irrelevant at this layer because the unified API-key path does not carry session flags. The fourth failure was a real production bug: 3. test_github_validate_skips_live_access_with_honest_note expected github-seed validation to return valid=True per _GITHUB_VALIDATE_NOTE ("GitHub access and rate limits are checked when the run starts"). The validate route called build_config_builder which lazy-imports the optional data_designer module; when it is missing, the bare except blocked the recipe. Catch ImportError specifically and treat it as a deferred check, matching the documented intent. Verified all 4 tests pass and the rest of studio/backend/tests still pass (608 total, with the only remaining failures being environment specific: 4 GPU-aware tests on a no-GPU host and 1 Anthropic-API smoke test, both unrelated). --- studio/backend/routes/data_recipe/validate.py | 6 ++++ studio/backend/tests/test_desktop_auth.py | 33 ++++++++++--------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/studio/backend/routes/data_recipe/validate.py b/studio/backend/routes/data_recipe/validate.py index 87eef939b4..3a54bb580a 100644 --- a/studio/backend/routes/data_recipe/validate.py +++ b/studio/backend/routes/data_recipe/validate.py @@ -151,6 +151,12 @@ def validate(payload: RecipePayload) -> ValidateResponse: return ValidateResponse(valid = False, errors = static_errors) try: build_config_builder(recipe) + except ImportError: + # data_designer is an optional runtime dep. Static validation already + # passed; live access + full config validation are deferred to run + # start (per _GITHUB_VALIDATE_NOTE), so a missing optional import + # at validate time should not block the recipe. + pass except Exception as exc: detail = str(exc).strip() or "Validation failed." return ValidateResponse( diff --git a/studio/backend/tests/test_desktop_auth.py b/studio/backend/tests/test_desktop_auth.py index c8cf1c7081..a5508c1c8b 100644 --- a/studio/backend/tests/test_desktop_auth.py +++ b/studio/backend/tests/test_desktop_auth.py @@ -246,7 +246,10 @@ def test_desktop_session_uses_real_admin_identity_for_api_keys(): assert [row["name"] for row in rows] == ["desktop"] -def test_local_recipe_token_preserves_desktop_marker(loaded_local_model): +def test_local_recipe_token_authenticates_as_admin_for_desktop_user(loaded_local_model): + # _inject_local_providers mints an internal sk-unsloth-* API key (not a + # forwarded JWT). The unified API-key path validates as the real admin + # user regardless of whether the incoming session was desktop or web. from auth.authentication import create_access_token, get_current_subject seed_user(must_change_password = True) @@ -260,13 +263,7 @@ def test_local_recipe_token_preserves_desktop_marker(loaded_local_model): jobs_route._inject_local_providers(recipe, local_recipe_request(incoming_token)) local_token = recipe["model_providers"][0]["api_key"] - payload = jwt.decode( - local_token, - storage.get_jwt_secret(storage.DEFAULT_ADMIN_USERNAME), - algorithms = ["HS256"], - ) - assert payload["sub"] == storage.DEFAULT_ADMIN_USERNAME - assert payload["desktop"] is True + assert local_token.startswith(storage.API_KEY_PREFIX) credentials = HTTPAuthorizationCredentials( scheme = "Bearer", credentials = local_token, @@ -276,8 +273,10 @@ def test_local_recipe_token_preserves_desktop_marker(loaded_local_model): ) -def test_local_recipe_token_keeps_web_marker_absent(loaded_local_model): - from auth.authentication import create_access_token +def test_local_recipe_token_authenticates_as_admin_for_web_user(loaded_local_model): + # Mirror of the desktop variant: API-key issuance is identical for web + # and desktop incoming tokens; auth via get_current_subject works the same. + from auth.authentication import create_access_token, get_current_subject seed_user(must_change_password = False) jobs_route = data_recipe_jobs_module() @@ -287,13 +286,14 @@ def test_local_recipe_token_keeps_web_marker_absent(loaded_local_model): jobs_route._inject_local_providers(recipe, local_recipe_request(incoming_token)) local_token = recipe["model_providers"][0]["api_key"] - payload = jwt.decode( - local_token, - storage.get_jwt_secret(storage.DEFAULT_ADMIN_USERNAME), - algorithms = ["HS256"], + assert local_token.startswith(storage.API_KEY_PREFIX) + credentials = HTTPAuthorizationCredentials( + scheme = "Bearer", + credentials = local_token, + ) + assert ( + asyncio.run(get_current_subject(credentials)) == storage.DEFAULT_ADMIN_USERNAME ) - assert payload["sub"] == storage.DEFAULT_ADMIN_USERNAME - assert "desktop" not in payload def test_desktop_login_rejects_invalid_secret(): @@ -381,6 +381,7 @@ def test_health_response_reports_desktop_capability_fields(monkeypatch): datasets_router = APIRouter(), export_router = APIRouter(), inference_router = APIRouter(), + inference_studio_router = APIRouter(), models_router = APIRouter(), training_history_router = APIRouter(), training_router = APIRouter(),