- 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
228 lines
9.1 KiB
Python
228 lines
9.1 KiB
Python
# #region SupersetSqllabE2E [C:5] [TYPE Module] [SEMANTICS test,superset,sqllab,e2e,execution]
|
|
# @BRIEF End-to-end: register real PostgreSQL in Superset → execute SQL via SupersetClient.
|
|
# @RELATION BINDS_TO -> [SupersetSqlLabExecutor]
|
|
# @RELATION BINDS_TO -> [SupersetClient]
|
|
#
|
|
# @TEST_CONTRACT SupersetSqlLabE2E ->
|
|
# {
|
|
# invariants: [
|
|
# "SupersetClient.create_database() registers a real Postgres DB via REST API",
|
|
# "SupersetClient handles JWT + CSRF internally for mutation endpoints",
|
|
# "SQL Lab execute returns query_id for valid SQL on registered DB",
|
|
# "SupersetSqlLabExecutor can resolve DB and execute SQL end-to-end"
|
|
# ]
|
|
# }
|
|
#
|
|
# @REQUIRES Docker daemon running (testcontainers starts superset + postgres)
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))
|
|
|
|
import pytest
|
|
from sqlalchemy import text
|
|
from urllib.parse import urlparse
|
|
|
|
|
|
# #region TestSupersetSqllabEndToEnd [C:4] [TYPE Class]
|
|
class TestSupersetSqllabEndToEnd:
|
|
"""Full E2E: PostgreSQL → Superset registration → SQL Lab via SupersetClient."""
|
|
|
|
@staticmethod
|
|
def _setup_test_table(pg_engine):
|
|
with pg_engine.begin() as conn:
|
|
conn.execute(text("DROP TABLE IF EXISTS ss_e2e_test"))
|
|
conn.execute(text(
|
|
"CREATE TABLE ss_e2e_test ("
|
|
" id SERIAL PRIMARY KEY,"
|
|
" name VARCHAR(100) NOT NULL,"
|
|
" value INTEGER NOT NULL"
|
|
")"
|
|
))
|
|
conn.execute(text(
|
|
"INSERT INTO ss_e2e_test (name, value) VALUES "
|
|
"('alpha', 10), ('beta', 20), ('gamma', 30)"
|
|
))
|
|
|
|
@staticmethod
|
|
def _cleanup_test_table(pg_engine):
|
|
with pg_engine.begin() as conn:
|
|
conn.execute(text("DROP TABLE IF EXISTS ss_e2e_test"))
|
|
|
|
# #region test_register_db_and_execute_sql [C:2] [TYPE Function]
|
|
@pytest.mark.asyncio
|
|
async def test_register_db_and_execute_sql(
|
|
self, superset_client, superset_db_url, pg_engine,
|
|
):
|
|
"""Register Postgres DB → execute SQL via SupersetClient (handles CSRF)."""
|
|
self._setup_test_table(pg_engine)
|
|
db_id = None
|
|
try:
|
|
# Build Postgres URI with Docker bridge IP
|
|
parsed = urlparse(superset_db_url)
|
|
register_uri = f"postgresql://test:test@{parsed.hostname}:{parsed.port}/test_translate"
|
|
|
|
# Register DB via SupersetClient.create_database()
|
|
reg_resp = await superset_client.create_database(
|
|
database_name="E2E Test DB",
|
|
sqlalchemy_uri=register_uri,
|
|
expose_in_sqllab=True,
|
|
allow_dml=False,
|
|
)
|
|
db_id = reg_resp.get("id")
|
|
if not db_id:
|
|
# Fallback: list and find
|
|
_, dbs = await superset_client.get_databases()
|
|
target = next(
|
|
(d for d in dbs if "E2E" in d.get("database_name", "")),
|
|
None,
|
|
)
|
|
if target:
|
|
db_id = target["id"]
|
|
else:
|
|
pytest.skip(f"DB reg failed: {str(reg_resp)[:300]}")
|
|
|
|
# Execute SQL via client.request()
|
|
exec_resp = await superset_client.client.request(
|
|
"POST", "/sqllab/execute/",
|
|
data={
|
|
"database_id": db_id,
|
|
"sql": "SELECT * FROM ss_e2e_test ORDER BY id",
|
|
"runAsync": False,
|
|
},
|
|
)
|
|
|
|
# Verify inline results
|
|
result_part = exec_resp.get("result", exec_resp)
|
|
if isinstance(result_part, dict) and "columns" in result_part:
|
|
data_rows = result_part.get("data", [])
|
|
assert len(data_rows) == 3
|
|
col_names = [
|
|
c.get("column_name", c.get("name", ""))
|
|
for c in result_part["columns"]
|
|
]
|
|
assert "name" in col_names
|
|
assert "value" in col_names
|
|
return # success
|
|
|
|
# Poll for async results
|
|
query_id = exec_resp.get("query", {}).get("id") or exec_resp.get("id")
|
|
if query_id:
|
|
import asyncio
|
|
for attempt in range(15):
|
|
await asyncio.sleep(1)
|
|
result_data = await superset_client.client.request(
|
|
"GET", f"/sqllab/results/{query_id}/",
|
|
)
|
|
for key in ("result", "data", "query"):
|
|
part = result_data.get(key, {})
|
|
if isinstance(part, dict) and "columns" in part:
|
|
data_rows = part.get("data", [])
|
|
if len(data_rows) == 3:
|
|
return # success
|
|
if result_data.get("status") in ("pending", "running"):
|
|
continue
|
|
pytest.fail(f"No valid results after 15s for query {query_id}")
|
|
else:
|
|
pytest.skip(f"No query_id: {list(exec_resp.keys())}")
|
|
finally:
|
|
self._cleanup_test_table(pg_engine)
|
|
if db_id:
|
|
try:
|
|
await superset_client.delete_database(db_id)
|
|
except Exception:
|
|
pass
|
|
# #endregion test_register_db_and_execute_sql
|
|
# #endregion TestSupersetSqllabEndToEnd
|
|
|
|
|
|
# #region TestSupersetSqlLabExecutorE2E [C:4] [TYPE Class]
|
|
class TestSupersetSqlLabExecutorE2E:
|
|
"""SupersetSqlLabExecutor against a real registered database."""
|
|
|
|
# #region test_executor_against_real_db [C:2] [TYPE Function]
|
|
@pytest.mark.asyncio
|
|
async def test_executor_against_real_db(
|
|
self, superset_client,
|
|
superset_admin_password, superset_db_url, pg_engine,
|
|
):
|
|
"""SupersetSqlLabExecutor resolves DB and executes SQL end-to-end."""
|
|
from unittest.mock import MagicMock
|
|
from src.plugins.translate.superset_executor import SupersetSqlLabExecutor
|
|
|
|
TestSupersetSqllabEndToEnd._setup_test_table(pg_engine)
|
|
|
|
config_manager = MagicMock()
|
|
config_manager.get_environments.return_value = [superset_client.env]
|
|
config_manager.get_environment.return_value = superset_client.env
|
|
config_manager.get_config.return_value = MagicMock()
|
|
|
|
executor = SupersetSqlLabExecutor(config_manager, "test_env")
|
|
db_id = None
|
|
|
|
try:
|
|
# Register DB via SupersetClient.create_database()
|
|
parsed = urlparse(superset_db_url)
|
|
register_uri = f"postgresql://test:test@{parsed.hostname}:{parsed.port}/test_translate"
|
|
reg_resp = await superset_client.create_database(
|
|
database_name="Executor E2E DB",
|
|
sqlalchemy_uri=register_uri,
|
|
expose_in_sqllab=True,
|
|
allow_dml=False,
|
|
)
|
|
db_id = reg_resp.get("id")
|
|
if not db_id:
|
|
pytest.skip(f"DB reg: {str(reg_resp)[:200]}")
|
|
|
|
# Resolve database_id via executor
|
|
resolved_id = await executor.resolve_database_id(
|
|
database_name="Executor E2E DB",
|
|
)
|
|
assert isinstance(resolved_id, int) and resolved_id > 0
|
|
assert resolved_id == db_id
|
|
|
|
# Execute SQL with runAsync=False (inline results in raw_response)
|
|
result = await executor.execute_sql(
|
|
"SELECT * FROM ss_e2e_test ORDER BY id",
|
|
database_id=db_id, run_async=False,
|
|
)
|
|
|
|
# Inline results from execute_sql's raw_response
|
|
raw = result.get("raw_response", {})
|
|
result_part = raw.get("result", raw)
|
|
if isinstance(result_part, dict) and "columns" in result_part:
|
|
data_rows = result_part.get("data", [])
|
|
assert len(data_rows) == 3, \
|
|
f"Expected 3 rows, got {len(data_rows)}"
|
|
col_names = [
|
|
c.get("column_name", c.get("name", ""))
|
|
for c in result_part["columns"]
|
|
]
|
|
assert "name" in col_names
|
|
assert "value" in col_names
|
|
return # success
|
|
|
|
# Fallback: poll with runAsync=True
|
|
query_id = result.get("query_id")
|
|
if query_id:
|
|
import asyncio
|
|
for attempt in range(15):
|
|
await asyncio.sleep(1)
|
|
qr = await executor.get_query_results(query_id)
|
|
if qr and qr.get("status") == "success" and qr.get("results"):
|
|
assert len(qr["results"]) == 3
|
|
return
|
|
pytest.fail(f"No results for query {query_id}")
|
|
else:
|
|
pytest.skip(f"No query_id or inline results: {list(result.keys())}")
|
|
finally:
|
|
TestSupersetSqllabEndToEnd._cleanup_test_table(pg_engine)
|
|
if db_id:
|
|
try:
|
|
await superset_client.delete_database(db_id)
|
|
except Exception:
|
|
pass
|
|
# #endregion test_executor_against_real_db
|
|
# #endregion TestSupersetSqlLabExecutorE2E
|
|
# #endregion SupersetSqllabE2E
|