🎉 FINAL: 98.4% real coverage! 7778 tests, 0 failures.

SESSION SUMMARY:
- Started at 7194 tests, 80% raw / 93.4% real
- Ended at 7778 tests, 84% raw / 98.4% real
- +584 tests, +4pp raw, +5pp real
- 0 failures, 0 production code changes

FIXED (12→0 failures):
- dataset_review_routes_extended: 201→200, DTO fields, candidate FK
- settings_consolidated: whitelisted keys, dict access
- llm_analysis_service: rate_limit parse mock
- migration_plugin: retry side_effect exhaustion
- preview: DB query instead of dict key
- scheduler: UTC→None for SQLite naive datetimes, patch targets, async wrappers

NEW TEST FILES (10+):
- scripts/: check_migration_chain, seed_superset_load_test, test_dataset_dashboard_relations, create_admin, seed_permissions, init_auth_db, delete_running_tasks
- llm_analysis: plugin_coverage +5, service_coverage +5, migration +2
- clean_release_ext +9, superset_compilation_adapter_edge +5
- service_inline_correction +7 (via __tests__)

MODULES AT 100%: clean_release models, superset_compilation_adapter,
service_inline_correction, llm_analysis/plugin, dependencies

DEAD CODE DOCUMENTED: search.py (L206-215 indentation bug),
llm_analysis/service (L459 HTTPS, L594 duplicate tab, L639-697 CDP-only)
This commit is contained in:
2026-06-16 11:01:31 +03:00
parent 901448a53c
commit 43fe5c59a7
26 changed files with 3280 additions and 798 deletions

View File

@@ -595,60 +595,324 @@ class TestCheckApiKeyEnvironmentScope:
await check_api_key_environment_scope(request, mock_db, "env-1")
# ── require_api_key_or_jwt (partial — JWT path) ──
# ── require_api_key_or_jwt (full coverage) ──
class TestRequireApiKeyOrJwt:
"""require_api_key_or_jwt factory — JWT path testing."""
class TestRequireApiKeyOrJwtBase:
"""Shared helpers for require_api_key_or_jwt tests."""
def _build_dep(self, api_perm="dataset:read", jwt_res="dataset", jwt_act="read", env_id=None):
from src.dependencies import require_api_key_or_jwt
return require_api_key_or_jwt(
required_api_permission=api_perm,
required_jwt_resource=jwt_res,
required_jwt_action=jwt_act,
environment_id=env_id,
)
def _make_request(self, body=None, headers=None):
req = MagicMock(spec=Request)
req.json = AsyncMock(return_value=body or {})
req.headers.get = MagicMock(return_value=(headers or {}).get("X-API-Key"))
return req
class TestRequireApiKeyOrJwtJwt(TestRequireApiKeyOrJwtBase):
"""require_api_key_or_jwt — JWT auth path."""
@pytest.mark.asyncio
async def test_jwt_success_admin(self):
"""JWT with Admin role passes."""
from src.dependencies import require_api_key_or_jwt
dep = self._build_dep(env_id="env-1")
mock_user = MagicMock()
mock_user.username = "admin"
role = MagicMock()
role.name = "Admin"
role.permissions = []
mock_user.roles = [role]
mock_repo = MagicMock()
mock_repo.get_user_by_username.return_value = mock_user
# Build the dependency
dep = require_api_key_or_jwt(
required_api_permission="dataset:read",
required_jwt_resource="dataset",
required_jwt_action="read",
environment_id="env-1",
)
mock_request = MagicMock(spec=Request)
mock_request.json = AsyncMock(return_value={})
request = self._make_request()
with patch('src.dependencies.decode_token', return_value={"sub": "admin"}), \
patch('src.dependencies.AuthRepository', return_value=mock_repo), \
patch('src.core.auth.api_key.hash_api_key'):
result = await dep(mock_request, MagicMock(), MagicMock(), "valid_token")
result = await dep(request, MagicMock(), MagicMock(), "valid_token")
assert result == "jwt:admin"
@pytest.mark.asyncio
async def test_jwt_non_string_sub_raises_401(self):
"""JWT with non-string sub raises 401 (covers lines 298)."""
dep = self._build_dep()
request = self._make_request()
with patch('src.dependencies.decode_token', return_value={"sub": 12345}): # non-string
with pytest.raises(HTTPException) as exc:
await dep(request, MagicMock(), MagicMock(), "bad_token")
assert exc.value.status_code == 401
@pytest.mark.asyncio
async def test_jwt_decode_exception_raises_401(self):
"""JWT decode exception raises 401 (covers lines 304-305)."""
dep = self._build_dep()
request = self._make_request()
with patch('src.dependencies.decode_token', side_effect=Exception("Token expired")):
with pytest.raises(HTTPException) as exc:
await dep(request, MagicMock(), MagicMock(), "expired_token")
assert exc.value.status_code == 401
@pytest.mark.asyncio
async def test_jwt_user_not_found_raises_401(self):
"""JWT user not in DB raises 401 (covers line 314)."""
dep = self._build_dep()
mock_repo = MagicMock()
mock_repo.get_user_by_username.return_value = None
request = self._make_request()
with patch('src.dependencies.decode_token', return_value={"sub": "ghost"}), \
patch('src.dependencies.AuthRepository', return_value=mock_repo), \
patch('src.core.auth.api_key.hash_api_key'):
with pytest.raises(HTTPException) as exc:
await dep(request, MagicMock(), MagicMock(), "valid_token")
assert exc.value.status_code == 401
assert "User not found" in exc.value.detail
@pytest.mark.asyncio
async def test_jwt_permission_granted_via_role(self):
"""JWT non-admin user with matching permission passes (covers lines 325-331)."""
dep = self._build_dep(jwt_res="dataset", jwt_act="read")
mock_user = MagicMock()
mock_user.username = "editor"
perm = MagicMock()
perm.resource = "dataset"
perm.action = "read"
role = MagicMock()
role.name = "Editor"
role.is_admin = False
role.permissions = [perm]
mock_user.roles = [role]
mock_repo = MagicMock()
mock_repo.get_user_by_username.return_value = mock_user
request = self._make_request()
with patch('src.dependencies.decode_token', return_value={"sub": "editor"}), \
patch('src.dependencies.AuthRepository', return_value=mock_repo), \
patch('src.core.auth.api_key.hash_api_key'):
result = await dep(request, MagicMock(), MagicMock(), "valid_token")
assert result == "jwt:editor"
@pytest.mark.asyncio
async def test_jwt_permission_denied_raises_403(self):
"""JWT user without required permission raises 403 (covers line 334)."""
dep = self._build_dep(jwt_res="dataset", jwt_act="delete")
mock_user = MagicMock()
mock_user.username = "viewer"
perm = MagicMock()
perm.resource = "dataset"
perm.action = "read" # wrong action
role = MagicMock()
role.name = "Viewer"
role.is_admin = False
role.permissions = [perm]
mock_user.roles = [role]
mock_repo = MagicMock()
mock_repo.get_user_by_username.return_value = mock_user
request = self._make_request()
with patch('src.dependencies.decode_token', return_value={"sub": "viewer"}), \
patch('src.dependencies.AuthRepository', return_value=mock_repo), \
patch('src.core.auth.api_key.hash_api_key'):
with pytest.raises(HTTPException) as exc:
await dep(request, MagicMock(), MagicMock(), "valid_token")
assert exc.value.status_code == 403
assert "Permission denied" in exc.value.detail
@pytest.mark.asyncio
async def test_jwt_env_id_from_request_body(self):
"""When environment_id is None, read from request body (covers lines 289-290)."""
dep = self._build_dep() # no env_id — reads from body
mock_user = MagicMock()
mock_user.username = "admin"
role = MagicMock()
role.name = "Admin"
mock_user.roles = [role]
mock_repo = MagicMock()
mock_repo.get_user_by_username.return_value = mock_user
request = self._make_request(body={"environment_id": "env-from-body"})
with patch('src.dependencies.decode_token', return_value={"sub": "admin"}), \
patch('src.dependencies.AuthRepository', return_value=mock_repo), \
patch('src.core.auth.api_key.hash_api_key'):
result = await dep(request, MagicMock(), MagicMock(), "valid_token")
assert result == "jwt:admin"
@pytest.mark.asyncio
async def test_jwt_body_parse_error_ignored(self):
"""JSON parse error in request body does not crash (covers line 290 except branch)."""
import json
dep = self._build_dep()
mock_user = MagicMock()
mock_user.username = "admin"
role = MagicMock()
role.name = "Admin"
mock_user.roles = [role]
mock_repo = MagicMock()
mock_repo.get_user_by_username.return_value = mock_user
request = MagicMock(spec=Request)
request.json = AsyncMock(side_effect=json.JSONDecodeError("bad json", "", 0))
request.headers.get = MagicMock(return_value=None)
with patch('src.dependencies.decode_token', return_value={"sub": "admin"}), \
patch('src.dependencies.AuthRepository', return_value=mock_repo), \
patch('src.core.auth.api_key.hash_api_key'):
result = await dep(request, MagicMock(), MagicMock(), "valid_token")
assert result == "jwt:admin"
@pytest.mark.asyncio
async def test_no_auth_raises_401(self):
"""Neither JWT nor API key → 401."""
from src.dependencies import require_api_key_or_jwt
dep = require_api_key_or_jwt(
required_api_permission="test",
required_jwt_resource="test",
required_jwt_action="test",
)
mock_request = MagicMock(spec=Request)
mock_request.headers.get.return_value = None
mock_request.json = AsyncMock(return_value={})
dep = self._build_dep()
request = self._make_request()
with pytest.raises(HTTPException) as exc:
await dep(mock_request, MagicMock(), MagicMock(), None)
await dep(request, MagicMock(), MagicMock(), None)
assert exc.value.status_code == 401
assert "Authentication required" in exc.value.detail
class TestRequireApiKeyOrJwtApiKey(TestRequireApiKeyOrJwtBase):
"""require_api_key_or_jwt — API key auth path (lines 345-388)."""
def _mock_api_key(
self,
active=True,
expires_at=None,
permissions=None,
environment_id=None,
name="Test Key",
prefix="test",
):
from datetime import UTC, datetime
key = MagicMock()
key.name = name
key.prefix = prefix
key.active = active
key.expires_at = expires_at
key.environment_id = environment_id
key.permissions = permissions or ["dataset:read"]
key.last_used_at = None
return key
def _setup(self, api_key, body=None, env_id=None):
dep = self._build_dep(env_id=env_id)
request = self._make_request(body=body, headers={"X-API-Key": "raw-key-value"})
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.first.return_value = api_key
return dep, request, mock_db
@pytest.mark.asyncio
async def test_api_key_valid(self):
"""Valid API key returns principal name (covers lines 345-388 happy path)."""
api_key = self._mock_api_key()
dep, request, mock_db = self._setup(api_key)
with patch('src.core.auth.api_key.hash_api_key', return_value="hashed"):
result = await dep(request, mock_db, MagicMock(), None)
assert result == "api_key:Test Key"
# last_used_at should be updated
assert api_key.last_used_at is not None
@pytest.mark.asyncio
async def test_api_key_not_found_raises_401(self):
"""API key not in DB raises 401 (covers line 347-351)."""
dep, request, mock_db = self._setup(None)
mock_db.query.return_value.filter.return_value.first.return_value = None
with patch('src.core.auth.api_key.hash_api_key', return_value="bad_hash"):
with pytest.raises(HTTPException) as exc:
await dep(request, mock_db, MagicMock(), None)
assert exc.value.status_code == 401
assert "Invalid API key" in exc.value.detail
@pytest.mark.asyncio
async def test_api_key_revoked_raises_401(self):
"""Revoked API key raises 401 (covers lines 352-356)."""
api_key = self._mock_api_key(active=False)
dep, request, mock_db = self._setup(api_key)
with patch('src.core.auth.api_key.hash_api_key', return_value="hash"):
with pytest.raises(HTTPException) as exc:
await dep(request, mock_db, MagicMock(), None)
assert exc.value.status_code == 401
assert "revoked" in exc.value.detail.lower()
@pytest.mark.asyncio
async def test_api_key_expired_raises_401(self):
"""Expired API key raises 401 (covers lines 357-365)."""
from datetime import UTC, datetime, timedelta
api_key = self._mock_api_key(expires_at=datetime.now(UTC) - timedelta(hours=1))
dep, request, mock_db = self._setup(api_key)
with patch('src.core.auth.api_key.hash_api_key', return_value="hash"):
with pytest.raises(HTTPException) as exc:
await dep(request, mock_db, MagicMock(), None)
assert exc.value.status_code == 401
assert "expired" in exc.value.detail.lower()
@pytest.mark.asyncio
async def test_api_key_expired_naive_tz(self):
"""Expired API key without timezone on expires_at (covers line 359-360)."""
from datetime import datetime
api_key = self._mock_api_key(expires_at=datetime(2020, 1, 1)) # naive → past
dep, request, mock_db = self._setup(api_key)
with patch('src.core.auth.api_key.hash_api_key', return_value="hash"):
with pytest.raises(HTTPException) as exc:
await dep(request, mock_db, MagicMock(), None)
assert exc.value.status_code == 401
assert "expired" in exc.value.detail.lower()
@pytest.mark.asyncio
async def test_api_key_missing_permission_raises_403(self):
"""API key lacking required permission raises 403 (covers lines 368-373)."""
api_key = self._mock_api_key(permissions=["dataset:other"])
dep, request, mock_db = self._setup(api_key)
with patch('src.core.auth.api_key.hash_api_key', return_value="hash"):
with pytest.raises(HTTPException) as exc:
await dep(request, mock_db, MagicMock(), None)
assert exc.value.status_code == 403
assert "lacks permission" in exc.value.detail
@pytest.mark.asyncio
async def test_api_key_scope_mismatch_raises_400(self):
"""API key environment_id differs from target → 400 (covers lines 376-381)."""
api_key = self._mock_api_key(environment_id="env-prod")
dep, request, mock_db = self._setup(api_key, env_id="env-dev")
with patch('src.core.auth.api_key.hash_api_key', return_value="hash"):
with pytest.raises(HTTPException) as exc:
await dep(request, mock_db, MagicMock(), None)
assert exc.value.status_code == 400
assert "restricted" in exc.value.detail.lower()
@pytest.mark.asyncio
async def test_api_key_scope_from_request_body(self):
"""When env_id not in factory, scope comes from request body (covers env resolution)."""
api_key = self._mock_api_key(environment_id="env-from-body")
dep, request, mock_db = self._setup(api_key, body={"environment_id": "env-from-body"})
with patch('src.core.auth.api_key.hash_api_key', return_value="hash"):
result = await dep(request, mock_db, MagicMock(), None)
assert result == "api_key:Test Key"
@pytest.mark.asyncio
async def test_api_key_key_env_is_none_ok(self):
"""When API key has no environment_id, no scope check (no 400)."""
api_key = self._mock_api_key(environment_id=None)
dep, request, mock_db = self._setup(api_key, env_id="env-target")
with patch('src.core.auth.api_key.hash_api_key', return_value="hash"):
result = await dep(request, mock_db, MagicMock(), None)
assert result == "api_key:Test Key"