blob: 71221941af2ac7718bcb4e8bdd67be8c8cdc7be0 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""Security and evidence contracts for the formal Query-domain runtime."""
from __future__ import annotations
import math
from contextlib import asynccontextmanager
from decimal import Decimal
from types import SimpleNamespace
from typing import Any
from unittest.mock import AsyncMock
import pytest
from doris_mcp_server.utils.config import DorisConfig
from doris_mcp_server.utils.db import QueryResult
from doris_mcp_server.utils.doris_http_client import DorisHTTPResponse
from doris_mcp_server.utils.query_runtime import (
DorisQueryRuntime,
QueryRuntimeFailure,
ReadOnlySQLGuard,
)
from doris_mcp_server.utils.security import (
AuthContext,
reset_auth_context,
set_current_auth_context,
)
def _query_result(
*,
sql: str = "SELECT 1",
rows: list[dict[str, Any]] | None = None,
columns: list[str] | None = None,
truncated: bool = False,
) -> QueryResult:
data = rows if rows is not None else [{"answer": 1}]
return QueryResult(
data=data,
metadata={
"columns": columns or ["answer"],
"truncated": truncated,
"result_bytes": 16,
"truncation_reason": "row_limit" if truncated else None,
},
execution_time=0.01,
row_count=len(data),
sql=sql,
)
class _Connection:
def __init__(
self,
results: list[QueryResult] | None = None,
error: Exception | None = None,
) -> None:
self.results = list(results or [_query_result()])
self.error = error
self.calls: list[tuple[str, Any, dict[str, Any]]] = []
async def execute(
self,
sql: str,
params: Any = None,
**kwargs: Any,
) -> QueryResult:
self.calls.append((sql, params, kwargs))
if self.error is not None:
raise self.error
if sql.startswith(("USE ", "SET ", "SWITCH ")):
return _query_result(sql=sql, rows=[], columns=[])
if self.results:
return self.results.pop(0)
return _query_result(sql=sql, rows=[], columns=[])
class _ConnectionManager:
def __init__(self, connection: _Connection) -> None:
self.config = DorisConfig()
self.connection = connection
self.session_ids: list[str] = []
@asynccontextmanager
async def get_connection_context_for_auth_context(
self,
session_id: str,
_auth_context: Any,
):
self.session_ids.append(session_id)
yield self.connection
def _runtime(
*,
connection: _Connection | None = None,
adbc: Any | None = None,
) -> tuple[DorisQueryRuntime, _ConnectionManager, Any]:
manager = _ConnectionManager(connection or _Connection())
adbc_tools = adbc or SimpleNamespace(
exec_adbc_query=AsyncMock(),
get_adbc_connection_info=AsyncMock(),
)
return DorisQueryRuntime(manager, adbc_tools), manager, adbc_tools
@pytest.mark.parametrize(
("sql", "operation"),
[
("SELECT 1", "SELECT"),
("WITH value AS (SELECT 1) SELECT * FROM value", "SELECT"),
("SHOW DATABASES", "SHOW"),
("DESC internal.__internal_schema.audit_log", "DESC"),
("DESCRIBE t", "DESCRIBE"),
("EXPLAIN SELECT 1", "EXPLAIN"),
("EXPLAIN SELECT REPLACE(name, 'a', 'b') FROM t", "EXPLAIN"),
("SELECT REPLACE(name, 'a', 'b') FROM t", "SELECT"),
("SELECT admin, load FROM metrics", "SELECT"),
("SELECT 'DROP TABLE t' AS harmless", "SELECT"),
("/* DELETE FROM t */ SELECT 1", "SELECT"),
("SELECT 1; /* trailing comment */", "SELECT"),
],
)
def test_read_only_guard_accepts_supported_statements(
sql: str,
operation: str,
) -> None:
assert ReadOnlySQLGuard.validate(sql).operation == operation
@pytest.mark.parametrize(
"sql",
[
"SELECT 1; SELECT 2",
"INSERT INTO t VALUES (1)",
"WITH old AS (SELECT 1) DELETE FROM t",
"EXPLAIN DELETE FROM t",
"SELECT * FROM t INTO OUTFILE '/tmp/result'",
"SELECT SLEEP(1)",
"SELECT @value := 1",
"SELECT * FROM t FOR UPDATE",
"SELECT * FROM t LOCK IN SHARE MODE",
"/*!50000 DROP TABLE t */ SELECT 1",
"SET enable_profile=true",
"USE analytics",
],
)
def test_read_only_guard_rejects_mutating_or_side_effecting_sql(
sql: str,
) -> None:
with pytest.raises(
QueryRuntimeFailure,
match="read-only|allowed|assignment|Locking|comments|export|side-effecting",
) as error:
ReadOnlySQLGuard.validate(sql)
assert error.value.reason_code == "QUERY_READ_ONLY_VIOLATION"
def test_query_target_rejects_nested_explain() -> None:
with pytest.raises(QueryRuntimeFailure) as error:
ReadOnlySQLGuard.validate("EXPLAIN SELECT 1", query_target=True)
assert error.value.reason_code == "QUERY_READ_ONLY_VIOLATION"
def test_named_parameters_are_exact_and_ignore_literals_and_comments() -> None:
sql = (
"SELECT %(customer_id)s AS customer_id, "
"'%(literal)s' AS literal /* %(comment)s */"
)
assert ReadOnlySQLGuard.validate_parameters(
sql,
{"customer_id": 7},
) == {"customer_id": 7}
with pytest.raises(QueryRuntimeFailure, match="match exactly"):
ReadOnlySQLGuard.validate_parameters(
"SELECT %(customer_id)s",
{"customer_id": 7, "extra": True},
)
with pytest.raises(QueryRuntimeFailure, match="missing"):
ReadOnlySQLGuard.validate_parameters(
"SELECT %(customer_id)s",
None,
)
with pytest.raises(QueryRuntimeFailure, match="JSON scalars"):
ReadOnlySQLGuard.validate_parameters(
"SELECT %(customer_id)s",
{"customer_id": {"nested": "not allowed"}},
)
with pytest.raises(QueryRuntimeFailure, match="finite"):
ReadOnlySQLGuard.validate_parameters(
"SELECT %(customer_id)s",
{"customer_id": math.inf},
)
with pytest.raises(QueryRuntimeFailure, match="Positional"):
ReadOnlySQLGuard.validate_parameters("SELECT %s", None)
def test_driver_sql_escapes_non_placeholder_percent_signs() -> None:
sql = (
"SELECT %(customer_id)s AS customer_id, "
"'100%' AS ratio, '%(literal)s' AS literal "
"/* %(comment)s and 50% */"
)
parameters = {"customer_id": 7}
prepared = ReadOnlySQLGuard.prepare_driver_sql(sql, parameters)
assert prepared % parameters == (
"SELECT 7 AS customer_id, '100%' AS ratio, "
"'%(literal)s' AS literal /* %(comment)s and 50% */"
)
@pytest.mark.asyncio
async def test_execute_query_binds_context_parameters_and_result_limits() -> None:
runtime, manager, _ = _runtime()
result = await runtime.execute_query(
sql="SELECT %(answer)s AS answer, '100%' AS ratio",
catalog="internal",
database="analytics",
parameters={"answer": 42},
max_rows=25,
timeout_ms=1500,
)
assert result["status"] == "success"
assert result["data"] == {
"columns": [{"name": "answer"}],
"rows": [{"answer": 1}],
"row_count": 1,
"truncated": False,
}
assert [call[0] for call in manager.connection.calls] == [
"SWITCH `internal`",
"USE `analytics`",
"SELECT %(answer)s AS answer, '100%%' AS ratio",
]
assert manager.connection.calls[-1][1] == {"answer": 42}
assert manager.connection.calls[-1][2]["max_rows"] == 25
assert result["metadata"]["limits"]["timeout_seconds"] == 2
assert manager.connection.is_healthy is False
@pytest.mark.asyncio
async def test_execute_query_preserves_decimal_precision_as_json_string() -> None:
connection = _Connection(
results=[
_query_result(
rows=[{"amount": Decimal("12345678901234567890.123456789012345678")}],
columns=["amount"],
)
]
)
runtime, _, _ = _runtime(connection=connection)
result = await runtime.execute_query(sql="SELECT amount FROM payments")
assert result["data"]["rows"] == [
{"amount": "12345678901234567890.123456789012345678"}
]
@pytest.mark.asyncio
async def test_execute_query_sanitizes_backend_errors() -> None:
runtime, _, _ = _runtime(
connection=_Connection(
error=RuntimeError(
"access denied for user root password=secret at 10.0.0.8"
)
)
)
with pytest.raises(QueryRuntimeFailure) as error:
await runtime.execute_query(sql="SELECT * FROM restricted")
assert str(error.value) == "Doris denied the query on the active route."
assert error.value.reason_code == "QUERY_PERMISSION_DENIED"
assert "secret" not in str(error.value)
assert "10.0.0.8" not in str(error.value)
@pytest.mark.asyncio
async def test_explain_costs_uses_supported_verbose_syntax() -> None:
connection = _Connection(
results=[
_query_result(
sql="EXPLAIN VERBOSE SELECT 1",
rows=[{"Explain String": "PLAN FRAGMENT 0\ncardinality=1"}],
columns=["Explain String"],
)
]
)
runtime, manager, _ = _runtime(connection=connection)
manager.config.security.max_result_rows = 200
result = await runtime.explain_query(sql="SELECT 1", level="costs")
assert connection.calls[0][0] == "EXPLAIN VERBOSE SELECT 1"
assert connection.calls[0][2]["max_rows"] == 200
assert result["status"] == "partial"
assert result["data"]["requested_level"] == "costs"
assert result["data"]["effective_level"] == "verbose"
assert "no EXPLAIN COSTS keyword" in result["warnings"][0]
@pytest.mark.asyncio
async def test_explain_reports_bounded_plan_truncation() -> None:
connection = _Connection(
results=[
_query_result(
sql="EXPLAIN SELECT 1",
rows=[{"Explain String": "PLAN FRAGMENT 0"}],
columns=["Explain String"],
truncated=True,
)
]
)
runtime, _, _ = _runtime(connection=connection)
result = await runtime.explain_query(sql="SELECT 1")
assert result["status"] == "partial"
assert result["metadata"]["plan_truncated"] is True
assert result["metadata"]["truncation_reason"] == "row_limit"
assert "truncated" in result["warnings"][0]
@pytest.mark.asyncio
async def test_profile_requires_exactly_one_input_and_bounds_text() -> None:
runtime, _, _ = _runtime()
runtime._fetch_profile = AsyncMock( # type: ignore[method-assign]
return_value={
"query_id": "query_123",
"profile_text": "PLAN\n SCAN\nPeak Memory: 10",
}
)
with pytest.raises(QueryRuntimeFailure, match="exactly one"):
await runtime.get_query_profile()
with pytest.raises(QueryRuntimeFailure, match="exactly one"):
await runtime.get_query_profile(
query_id="query_123",
sql="SELECT 1",
)
with pytest.raises(QueryRuntimeFailure, match="recent_window_minutes"):
await runtime.get_query_profile(
query_id="query_123",
recent_window_minutes=0,
)
result = await runtime.get_query_profile(
query_id="query_123",
include_operator_tree=True,
)
assert result["status"] == "success"
assert result["data"]["query_id"] == "query_123"
assert result["data"]["operator_tree"]
runtime._fetch_profile.assert_awaited_once_with("query_123")
@pytest.mark.asyncio
async def test_profiled_query_discards_connection_with_session_state() -> None:
runtime, manager, _ = _runtime()
limits = runtime._resolve_limits(max_rows=1, timeout_ms=1000)
await runtime._execute_profiled_statement(
"SELECT 1",
trace_id="trace_1",
database=None,
limits=limits,
)
assert [call[0] for call in manager.connection.calls] == [
'SET session_context="trace_id:trace_1"',
"SET enable_profile=true",
"SELECT 1",
]
assert manager.connection.is_healthy is False
@pytest.mark.asyncio
async def test_profile_http_payload_codes_are_handled_without_leaking_data() -> None:
runtime, _, _ = _runtime()
runtime._profile_http_get = AsyncMock( # type: ignore[method-assign]
side_effect=[
DorisHTTPResponse(
status=400,
headers={"content-type": "application/json"},
body=(
b'{"msg":"Bad Request","code":403,'
b'"data":"internal permission detail"}'
),
url="http://configured-fe/profile",
),
DorisHTTPResponse(
status=200,
headers={"content-type": "application/json"},
body=(b'{"msg":"success","code":0,"data":{"profile":"PLAN\\nSCAN"}}'),
url="http://configured-fe/profile",
),
DorisHTTPResponse(
status=400,
headers={"content-type": "application/json"},
body=(
b'{"msg":"Bad Request","code":403,"data":"secret backend detail"}'
),
url="http://configured-fe/profile",
),
]
)
assert await runtime._fetch_query_id("trace_1") is None
assert await runtime._fetch_profile("query_1") == {
"query_id": "query_1",
"profile_text": "PLAN\nSCAN",
}
with pytest.raises(QueryRuntimeFailure) as error:
await runtime._fetch_profile("query_2")
assert error.value.reason_code == "QUERY_PROFILE_PERMISSION_DENIED"
assert "secret" not in str(error.value)
@pytest.mark.asyncio
async def test_profile_content_is_not_used_as_a_not_found_sentinel() -> None:
runtime, _, _ = _runtime()
runtime._profile_http_get = AsyncMock( # type: ignore[method-assign]
side_effect=[
DorisHTTPResponse(
status=200,
headers={"content-type": "application/json"},
body=(
b'{"msg":"success","code":0,"data":{"profile":'
b'"SQL: SELECT * FROM messages WHERE state = \\"not found\\"\\nSCAN"}}'
),
url="http://configured-fe/profile",
),
DorisHTTPResponse(
status=200,
headers={"content-type": "application/json"},
body=b'{"msg":"Not Found","code":404,"data":""}',
url="http://configured-fe/profile",
),
]
)
profile = await runtime._fetch_profile("query_1")
assert '"not found"' in profile["profile_text"]
with pytest.raises(QueryRuntimeFailure) as error:
await runtime._fetch_profile("query_2")
assert error.value.reason_code == "QUERY_PROFILE_NOT_FOUND"
@pytest.mark.asyncio
async def test_profile_http_fails_closed_for_doris_oauth_route() -> None:
runtime, _, _ = _runtime()
context_token = set_current_auth_context(
AuthContext(
auth_method="doris_oauth",
doris_user="analyst",
)
)
try:
with pytest.raises(QueryRuntimeFailure) as error:
await runtime._profile_http_get(
"/rest/v2/manager/query/profile/text/query_1"
)
finally:
reset_auth_context(context_token)
assert error.value.reason_code == "QUERY_PROFILE_CREDENTIAL_ROUTE_UNAVAILABLE"
assert error.value.status_code == 503
@pytest.mark.asyncio
async def test_query_sql_http_evidence_is_validated_and_sanitized() -> None:
runtime, _, _ = _runtime()
runtime._profile_http_get = AsyncMock( # type: ignore[method-assign]
side_effect=[
DorisHTTPResponse(
status=200,
headers={"content-type": "application/json"},
body=(b'{"msg":"success","code":0,"data":{"sql":"SELECT 1"}}'),
url="http://configured-fe/sql",
),
DorisHTTPResponse(
status=400,
headers={"content-type": "application/json"},
body=(
b'{"msg":"Bad Request","code":403,"data":"secret backend detail"}'
),
url="http://configured-fe/sql",
),
]
)
assert await runtime._fetch_query_sql("query_1") == "SELECT 1"
with pytest.raises(QueryRuntimeFailure) as error:
await runtime._fetch_query_sql("query_2")
assert error.value.reason_code == "QUERY_PROFILE_PERMISSION_DENIED"
assert "secret" not in str(error.value)
@pytest.mark.asyncio
async def test_list_slow_queries_uses_bound_filters_and_truncates_sql() -> None:
long_sql = "SELECT '" + ("x" * 9000) + "'"
connection = _Connection(
results=[
_query_result(
rows=[
{
"query_id": "q1",
"time": "2026-07-31 00:00:00",
"user": "analyst",
"catalog": "internal",
"db": "analytics",
"state": "EOF",
"query_time": 2500,
"cpu_time_ms": 125,
"scan_bytes": 2048,
"scan_rows": 100,
"return_rows": 1,
"peak_memory_bytes": 4096,
"sql_hash": "hash",
"sql_digest": "digest",
"stmt": long_sql,
}
],
columns=["query_id"],
)
]
)
runtime, _, _ = _runtime(connection=connection)
result = await runtime.list_slow_queries(
window_minutes=15,
limit=10,
min_duration_ms=2000,
database="analytics",
user="analyst",
)
sql, params, _ = connection.calls[0]
assert "(%s IS NULL OR `db` = %s)" in sql
assert "(%s IS NULL OR `user` = %s)" in sql
assert "`state` <> 'ERR'" not in sql
assert params[1:] == (
2000,
"analytics",
"analytics",
"analyst",
"analyst",
11,
)
item = result["data"]["items"][0]
assert len(item["sql"]) == 8192
assert item["sql_truncated"] is True
@pytest.mark.asyncio
async def test_list_slow_queries_rejects_unsafe_filter_before_doris() -> None:
runtime, manager, _ = _runtime()
with pytest.raises(QueryRuntimeFailure) as error:
await runtime.list_slow_queries(database="analytics`; DROP TABLE t")
assert error.value.reason_code == "QUERY_ARGUMENT_INVALID"
assert manager.connection.calls == []
@pytest.mark.asyncio
async def test_list_slow_queries_masks_audit_rows_for_requester() -> None:
audit_row = {
"query_id": "q1",
"time": "2026-07-31 00:00:00",
"user": "analyst",
"catalog": "internal",
"db": "analytics",
"state": "EOF",
"query_time": 2500,
"stmt": "SELECT secret_phone FROM customers",
}
runtime, manager, _ = _runtime(
connection=_Connection(
results=[_query_result(rows=[audit_row], columns=["query_id"])]
)
)
manager.security_manager = SimpleNamespace(
apply_data_masking=AsyncMock(
return_value=[{**audit_row, "stmt": "SELECT [MASKED]"}]
)
)
context_token = set_current_auth_context(
AuthContext(auth_method="token", user_id="reader")
)
try:
result = await runtime.list_slow_queries(limit=1)
finally:
reset_auth_context(context_token)
assert result["data"]["items"][0]["sql"] == "SELECT [MASKED]"
manager.security_manager.apply_data_masking.assert_awaited_once()
@pytest.mark.asyncio
async def test_slow_query_bounds_distinguish_zero_from_missing_values() -> None:
runtime, manager, _ = _runtime(
connection=_Connection(results=[_query_result(rows=[])])
)
result = await runtime.list_slow_queries(min_duration_ms=0)
_, params, _ = manager.connection.calls[0]
assert params[1] == 0
assert result["status"] == "success"
with pytest.raises(QueryRuntimeFailure):
await runtime.list_slow_queries(window_minutes=0)
with pytest.raises(QueryRuntimeFailure):
await runtime.list_slow_queries(limit=0)
@pytest.mark.asyncio
async def test_adbc_guard_runs_before_provider_and_normalizes_rows() -> None:
adbc = SimpleNamespace(
exec_adbc_query=AsyncMock(
return_value={
"success": True,
"result": {
"format": "dict",
"column_names": ["id"],
"column_types": ["BIGINT"],
"num_rows": 1,
"data": [{"id": 1}],
},
"truncated": False,
"execution_time": 0.01,
}
),
get_adbc_connection_info=AsyncMock(),
)
runtime, _, _ = _runtime(adbc=adbc)
with pytest.raises(QueryRuntimeFailure) as error:
await runtime.execute_adbc_query(
explicit_adbc=True,
sql="DROP TABLE customer",
)
assert error.value.reason_code == "QUERY_READ_ONLY_VIOLATION"
adbc.exec_adbc_query.assert_not_awaited()
result = await runtime.execute_adbc_query(
explicit_adbc=True,
sql="SELECT id FROM customer",
max_rows=10,
timeout_ms=2000,
result_format="dict",
)
assert result["data"]["columns"] == [{"name": "id", "type": "BIGINT"}]
assert result["data"]["rows"] == [{"id": 1}]
adbc.exec_adbc_query.assert_awaited_once_with(
"SELECT id FROM customer",
max_rows=10,
timeout=2,
return_format="dict",
)
@pytest.mark.asyncio
async def test_adbc_requires_explicit_end_user_intent_before_provider() -> None:
adbc = SimpleNamespace(
exec_adbc_query=AsyncMock(),
get_adbc_connection_info=AsyncMock(),
)
runtime, _, _ = _runtime(adbc=adbc)
with pytest.raises(QueryRuntimeFailure) as query_error:
await runtime.execute_adbc_query(
explicit_adbc=False,
sql="SELECT 1",
)
with pytest.raises(QueryRuntimeFailure) as info_error:
await runtime.get_adbc_connection_info(explicit_adbc=False)
assert query_error.value.reason_code == "ADBC_EXPLICIT_USER_INTENT_REQUIRED"
assert info_error.value.reason_code == "ADBC_EXPLICIT_USER_INTENT_REQUIRED"
adbc.exec_adbc_query.assert_not_awaited()
adbc.get_adbc_connection_info.assert_not_awaited()
@pytest.mark.asyncio
async def test_adbc_rejects_invalid_runtime_options_before_provider() -> None:
adbc = SimpleNamespace(
exec_adbc_query=AsyncMock(),
get_adbc_connection_info=AsyncMock(),
)
runtime, _, _ = _runtime(adbc=adbc)
with pytest.raises(QueryRuntimeFailure, match="format"):
await runtime.execute_adbc_query(
explicit_adbc=True,
sql="SELECT 1",
result_format="csv",
)
with pytest.raises(QueryRuntimeFailure, match="timeout_ms"):
await runtime.execute_adbc_query(
explicit_adbc=True,
sql="SELECT 1",
timeout_ms=0,
)
adbc.exec_adbc_query.assert_not_awaited()
@pytest.mark.asyncio
async def test_adbc_connection_info_never_returns_endpoint_or_identity() -> None:
adbc = SimpleNamespace(
exec_adbc_query=AsyncMock(),
get_adbc_connection_info=AsyncMock(
return_value={
"status": "ready",
"configuration": {
"fe_host": "10.0.0.8",
"fe_arrow_flight_port": "8070",
"be_arrow_flight_port": "8060",
"user": "root",
},
"port_status": {
"success": True,
"be_available_count": 1,
"be_check_results": [{"host": "10.0.0.9", "port": 8060}],
},
"module_status": {
"success": True,
"adbc_manager_version": "1.8.0",
"flight_sql_version": "1.8.0",
},
}
),
)
runtime, _, _ = _runtime(adbc=adbc)
result = await runtime.get_adbc_connection_info(explicit_adbc=True)
serialized = repr(result)
assert result["data"]["status"] == "ready"
assert "10.0.0.8" not in serialized
assert "10.0.0.9" not in serialized
assert "root" not in serialized
@pytest.mark.asyncio
async def test_diagnosis_is_deterministic_and_marks_missing_optional_evidence() -> None:
runtime, _, _ = _runtime()
runtime.explain_query = AsyncMock( # type: ignore[method-assign]
return_value={
"status": "success",
"data": {"plan_text": "CROSS JOIN", "facets": {}},
"warnings": [],
"metadata": {"source": "doris_explain"},
"evidence": [{"source": "doris_sql", "kind": "explain"}],
}
)
runtime.get_query_profile = AsyncMock( # type: ignore[method-assign]
side_effect=QueryRuntimeFailure(
"Profile unavailable.",
reason_code="QUERY_PROFILE_NOT_FOUND",
status_code=404,
)
)
runtime._find_audit_record = AsyncMock( # type: ignore[method-assign]
return_value=None
)
result = await runtime.diagnose_query_performance(
sql="SELECT * FROM a CROSS JOIN b",
include_cluster_context=True,
)
assert result["status"] == "partial"
assert result["metadata"]["rule_version"] == "query-diagnosis-v1"
assert result["data"]["findings"] == [
{
"code": "CROSS_JOIN_OBSERVED",
"severity": "high",
"evidence": "The Doris explain plan contains CROSS JOIN.",
}
]
assert result["data"]["recommendations"][0]["finding_code"] == (
"CROSS_JOIN_OBSERVED"
)
assert result["data"]["cluster_context"]["reason_code"] == (
"CLUSTER_DOMAIN_NOT_YET_IMPLEMENTED"
)
assert (
result["data"]["steps"]["cluster_context"]
is not result["data"]["cluster_context"]
)
@pytest.mark.asyncio
async def test_diagnosis_uses_profile_sql_when_audit_record_is_missing() -> None:
runtime, _, _ = _runtime()
runtime._find_audit_record = AsyncMock( # type: ignore[method-assign]
return_value=None
)
runtime._fetch_query_sql = AsyncMock( # type: ignore[method-assign]
return_value="SELECT 1"
)
runtime.explain_query = AsyncMock( # type: ignore[method-assign]
return_value={
"status": "success",
"data": {"plan_text": "SCAN", "facets": {}},
"warnings": [],
"metadata": {"source": "doris_explain"},
"evidence": [{"source": "doris_sql", "kind": "explain"}],
}
)
runtime.get_query_profile = AsyncMock( # type: ignore[method-assign]
return_value={
"status": "success",
"data": {"query_id": "query_1", "profile": "SCAN"},
"warnings": [],
"metadata": {"source": "doris_profile_api"},
"evidence": [{"source": "doris_fe_http", "kind": "query_profile"}],
}
)
result = await runtime.diagnose_query_performance(query_id="query_1")
assert result["status"] == "partial"
runtime._fetch_query_sql.assert_awaited_once_with("query_1")
runtime.explain_query.assert_awaited_once_with(
sql="SELECT 1",
catalog=None,
database=None,
level="verbose",
)
@pytest.mark.asyncio
async def test_diagnosis_reuses_audit_context_masks_output_and_propagates_partial() -> (
None
):
audit_row = {
"query_id": "query_1",
"time": "2026-07-31 00:00:00",
"user": "analyst",
"catalog": "internal",
"db": "analytics",
"state": "EOF",
"query_time": 2500,
"stmt": "SELECT * FROM orders",
}
runtime, manager, _ = _runtime()
runtime._find_audit_record = AsyncMock( # type: ignore[method-assign]
return_value=audit_row
)
runtime.explain_query = AsyncMock( # type: ignore[method-assign]
return_value={
"status": "partial",
"data": {"plan_text": "SCAN", "facets": {}},
"warnings": ["The EXPLAIN output was truncated."],
"metadata": {"source": "doris_explain"},
"evidence": [{"source": "doris_sql", "kind": "explain"}],
}
)
runtime.get_query_profile = AsyncMock( # type: ignore[method-assign]
return_value={
"status": "partial",
"data": {"query_id": "query_1", "profile": "SCAN"},
"warnings": ["Profile text was truncated."],
"metadata": {"source": "doris_profile_api"},
"evidence": [{"source": "doris_fe_http", "kind": "query_profile"}],
}
)
manager.security_manager = SimpleNamespace(
apply_data_masking=AsyncMock(
return_value=[{**audit_row, "stmt": "SELECT [MASKED]"}]
)
)
context_token = set_current_auth_context(
AuthContext(auth_method="token", user_id="reader")
)
try:
result = await runtime.diagnose_query_performance(query_id="query_1")
finally:
reset_auth_context(context_token)
runtime.explain_query.assert_awaited_once_with(
sql="SELECT * FROM orders",
catalog="internal",
database="analytics",
level="verbose",
)
assert result["status"] == "partial"
assert result["warnings"] == [
"The EXPLAIN output was truncated.",
"Profile text was truncated.",
]
assert result["data"]["steps"]["explain"]["status"] == "partial"
assert result["data"]["steps"]["profile"]["status"] == "partial"
assert result["data"]["audit_record"]["sql"] == "SELECT [MASKED]"
manager.security_manager.apply_data_masking.assert_awaited_once()