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).
This commit is contained in:
Daniel Han 2026-04-29 03:38:49 +00:00
commit b0d33cff0e
2 changed files with 23 additions and 16 deletions

View file

@ -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(

View file

@ -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(),