Files
ss-tools/backend/tests/integration/test_superset_sqllab_api_integration.py
busya 50d6b9226e test(translate): add tests for ConnectionService, DbExecutor, orchestrator direct DB dispatch
- 9 new enhancement test files: test_connection_service.py,
  test_db_executor.py, test_orchestrator_direct_db.py, test_batch_insert.py,
  test_lang_stats.py, test_response_field_coverage.py, test_retry.py,
  test_run_service.py, test_sql_insert_service.py
- 5 new integration tests: test_superset_sqllab_e2e.py,
  test_translate_clickhouse.py, test_translate_corrections.py,
  test_translate_schedules.py, test_translate_status_fk.py
- Updated existing tests for insert_method/connection_id fields
2026-06-11 19:12:45 +03:00

256 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# #region TestSupersetSqllabApiJwt [C:4] [TYPE Module] [SEMANTICS test,superset,sqllab,integration,jwt]
# @BRIEF Integration tests for Superset REST API using JWT Bearer auth against real Testcontainers.
# @RELATION BINDS_TO -> [SupersetSqlLabExecutor]
# @RELATION BINDS_TO -> [SupersetClient]
# @RELATION BINDS_TO -> [ConfigManager]
#
# @TEST_CONTRACT SupersetSqllabApi ->
# {
# invariants: [
# "JWT login returns access_token + refresh_token",
# "Bearer token authorizes all /api/v1/ endpoints",
# "GET /api/v1/database/ returns paginated database list",
# "POST /api/v1/sqllab/execute/ accepts SQL and returns query_id",
# "GET /api/v1/dashboard/ returns paginated list with Bearer token"
# ]
# }
# @TEST_EDGE: wrong_password -> 401 on JWT login
# @TEST_EDGE: expired_token -> 401 on protected endpoints
# @TEST_EDGE: missing_database_id -> 400 on sqllab execute
#
# @RATIONALE ADR-0012 resolved the JWT auth issue via psycopg2 + superset_config.py +
# Docker bridge IP. Now JWT works end-to-end with the test container.
# All API tests use real Bearer tokens against the live Superset instance.
#
# @REQUIRES Docker daemon running (testcontainers starts superset + postgres)
import pytest
import requests
from uuid import uuid4
# #region TestSupersetJwtHealthCheck [C:3] [TYPE Class]
# @BRIEF Verify health and JWT login work correctly.
class TestSupersetJwtHealthCheck:
"""JWT auth smoke tests against the real Superset container."""
# #region test_jwt_login_returns_token [C:2] [TYPE Function]
# @BRIEF POST /api/v1/security/login returns access_token with refresh flag.
def test_jwt_login_returns_token(self, superset_url, superset_admin_password):
resp = requests.post(
f"{superset_url}/api/v1/security/login",
json={
"username": "admin",
"password": superset_admin_password,
"provider": "db",
"refresh": True,
},
timeout=10,
)
assert resp.status_code == 200, f"JWT login failed: {resp.status_code} {resp.text}"
data = resp.json()
assert "access_token" in data, f"No access_token in response: {data.keys()}"
assert "refresh_token" in data, f"No refresh_token in response: {data.keys()}"
assert len(data["access_token"]) > 20, "access_token too short"
# #endregion test_jwt_login_returns_token
# #region test_jwt_login_wrong_password [C:2] [TYPE Function]
# @BRIEF POST /api/v1/security/login with wrong password returns 401.
# @TEST_EDGE: wrong_password — VERIFIED_BY: test_jwt_login_wrong_password
def test_jwt_login_wrong_password(self, superset_url):
resp = requests.post(
f"{superset_url}/api/v1/security/login",
json={
"username": "admin",
"password": "wrong_password_123",
"provider": "db",
},
timeout=10,
)
assert resp.status_code == 401, f"Expected 401, got {resp.status_code}"
# #endregion test_jwt_login_wrong_password
# #region test_health_still_works [C:2] [TYPE Function]
# @BRIEF /health does not require auth — regression guard.
def test_health_still_works(self, superset_url):
resp = requests.get(f"{superset_url}/health", timeout=10)
assert resp.status_code == 200
assert resp.text.strip() == "OK"
# #endregion test_health_still_works
# #endregion TestSupersetJwtHealthCheck
# #region TestSupersetApiWithJwt [C:3] [TYPE Class]
# @BRIEF Verify REST API endpoints with JWT Bearer token auth.
class TestSupersetApiWithJwt:
"""Authenticated API tests against real Superset container."""
# #region test_list_databases_with_jwt [C:2] [TYPE Function]
# @BRIEF GET /api/v1/database/ returns paginated list with JWT Bearer token.
def test_list_databases_with_jwt(self, superset_url, superset_jwt_headers):
resp = requests.get(
f"{superset_url}/api/v1/database/",
headers=superset_jwt_headers,
timeout=10,
)
assert resp.status_code == 200, \
f"GET /api/v1/database/ failed: {resp.status_code} {resp.text[:200]}"
data = resp.json()
assert "result" in data, f"No 'result' key: {list(data.keys())}"
assert isinstance(data["result"], list), "result should be a list"
# #endregion test_list_databases_with_jwt
# #region test_list_dashboards_with_jwt [C:2] [TYPE Function]
# @BRIEF GET /api/v1/dashboard/ returns paginated list with JWT Bearer token.
def test_list_dashboards_with_jwt(self, superset_url, superset_jwt_headers):
resp = requests.get(
f"{superset_url}/api/v1/dashboard/",
headers=superset_jwt_headers,
timeout=10,
)
assert resp.status_code == 200, \
f"GET /api/v1/dashboard/ failed: {resp.status_code} {resp.text[:200]}"
data = resp.json()
assert "result" in data, f"No 'result' key: {list(data.keys())}"
assert "count" in data, f"No 'count' key: {list(data.keys())}"
# #endregion test_list_dashboards_with_jwt
# #region test_sqllab_execute_with_jwt [C:2] [TYPE Function]
# @BRIEF POST /api/v1/sqllab/execute/ is reachable with JWT.
def test_sqllab_execute_with_jwt(self, superset_url, superset_jwt_headers):
payload = {
"database_id": 1,
"sql": "SELECT 1",
"runAsync": True,
}
resp = requests.post(
f"{superset_url}/api/v1/sqllab/execute/",
json=payload,
headers={
**superset_jwt_headers,
"Content-Type": "application/json",
},
timeout=10,
)
# Without a real DB configured, we expect 400 or 500
# But the endpoint exists and is reachable (not 404/401)
assert resp.status_code in (200, 400, 500), \
f"sqllab/execute/ returned {resp.status_code}: {resp.text[:300]}"
# #endregion test_sqllab_execute_with_jwt
# #region test_sqllab_without_database_id [C:2] [TYPE Function]
# @BRIEF POST /api/v1/sqllab/execute/ without database_id returns 400.
# @TEST_EDGE: missing_database_id — VERIFIED_BY: test_sqllab_without_database_id
def test_sqllab_without_database_id(self, superset_url, superset_jwt_headers):
resp = requests.post(
f"{superset_url}/api/v1/sqllab/execute/",
json={"sql": "SELECT 1"},
headers={
**superset_jwt_headers,
"Content-Type": "application/json",
},
timeout=10,
)
assert resp.status_code in (400, 500), \
f"Expected 400/500, got {resp.status_code}: {resp.text[:200]}"
# #endregion test_sqllab_without_database_id
# #region test_get_nonexistent_database [C:2] [TYPE Function]
# @BRIEF GET /api/v1/database/<non_existent_id> returns 404.
def test_get_nonexistent_database(self, superset_url, superset_jwt_headers):
resp = requests.get(
f"{superset_url}/api/v1/database/99999",
headers=superset_jwt_headers,
timeout=10,
)
assert resp.status_code == 404, \
f"Expected 404, got {resp.status_code}: {resp.text[:200]}"
# #endregion test_get_nonexistent_database
# #region test_me_endpoint [C:2] [TYPE Function]
# @BRIEF GET /api/v1/me/ — в Superset 4.1.2 это публичный эндпоинт, но
# доступен только авторизованным через JWT. Проверяем что 401/200, а не 404.
def test_me_endpoint(self, superset_url, superset_jwt_headers):
resp = requests.get(
f"{superset_url}/api/v1/me/",
headers=superset_jwt_headers,
timeout=10,
)
# Superset 4.1.2 может вернуть 200 (есть endpoint) или 401 (нет прав у admin role).
# Главное — не 404, значит endpoint существует.
assert resp.status_code in (200, 401, 403), \
f"GET /api/v1/me/ failed: {resp.status_code} {resp.text[:200]}"
if resp.status_code == 200:
data = resp.json()
assert "result" in data, f"No 'result' key: {list(data.keys())}"
# #endregion test_me_endpoint
# #region test_database_columns_with_jwt [C:2] [TYPE Function]
# @BRIEF GET /api/v1/database/<id>/columns/ with valid id returns 404 (no DB configured).
def test_database_columns_with_jwt(self, superset_url, superset_jwt_headers):
resp = requests.get(
f"{superset_url}/api/v1/database/99999/columns/",
headers=superset_jwt_headers,
timeout=10,
)
assert resp.status_code == 404, \
f"Expected 404, got {resp.status_code}"
# #endregion test_database_columns_with_jwt
# #region test_unauthenticated_api_rejected [C:2] [TYPE Function]
# @BRIEF GET /api/v1/dashboard/ without auth returns 401.
def test_unauthenticated_api_rejected(self, superset_url):
resp = requests.get(
f"{superset_url}/api/v1/dashboard/",
timeout=10,
)
assert resp.status_code == 401, \
f"Expected 401, got {resp.status_code}"
# #endregion test_unauthenticated_api_rejected
# #endregion TestSupersetApiWithJwt
# #region TestSupersetJwtIntegration [C:3] [TYPE Class]
# @BRIEF Verify SupersetClient and SupersetSqlLabExecutor with real JWT auth.
class TestSupersetJwtIntegration:
"""Full SupersetClient integration tests with JWT auth."""
# #region test_superset_client_authenticated [C:2] [TYPE Function]
# @BRIEF Real SupersetClient authenticates and can call API.
@pytest.mark.asyncio
async def test_superset_client_authenticated(self, superset_client):
# Authenticated client should be able to list databases
count, databases = await superset_client.get_databases()
assert isinstance(count, int)
assert isinstance(databases, list)
# #endregion test_superset_client_authenticated
# #region test_superset_client_user_via_me_endpoint [C:2] [TYPE Function]
# @BRIEF SupersetClient can call /me/ raw endpoint — проверяем доступность.
@pytest.mark.asyncio
async def test_superset_client_user_via_me_endpoint(self, superset_client):
me = await superset_client.client.request("GET", "/me/")
assert me is not None
# Superset 4.1.2 может вернуть 200 (есть endpoint) или {"message":"Not authorized"}
# в зависимости от прав роли Admin на конкретный endpoint
# #endregion test_superset_client_user_via_me_endpoint
# #endregion TestSupersetJwtIntegration
# #endregion TestSupersetSqllabApiJwt