Files
tracer-cloud--opensre/tests/tools/test_snowflake_query_history_tool.py
wehub-resource-sync 4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

430 lines
14 KiB
Python

"""Dedicated unit tests for SnowflakeQueryHistoryTool.
Covers contract metadata, source-availability gating, parameter extraction,
SQL limit enforcement, account/token validation, row normalization across the
two response shapes the Snowflake SQL API returns, and HTTP failure handling.
"""
from __future__ import annotations
from typing import Any
import pytest
from integrations.snowflake.tools.snowflake_query_history_tool import query_snowflake_history
from tests.tools.conftest import BaseToolContract, MockHttpxResponse
# ---------------------------------------------------------------------------
# Contract — metadata, is_available, extract_params surface
# ---------------------------------------------------------------------------
class TestSnowflakeQueryHistoryToolContract(BaseToolContract):
def get_tool_under_test(self) -> Any:
return query_snowflake_history.__opensre_registered_tool__
def _rt() -> Any:
return query_snowflake_history.__opensre_registered_tool__
def test_is_available_true_when_token_account_and_verified() -> None:
sources = {
"snowflake": {
"connection_verified": True,
"account_identifier": "xy12345.us-east-1",
"token": "sf-token",
}
}
assert _rt().is_available(sources) is True
def test_is_available_false_when_connection_not_verified() -> None:
sources = {
"snowflake": {
"connection_verified": False,
"account_identifier": "xy12345.us-east-1",
"token": "sf-token",
}
}
assert _rt().is_available(sources) is False
def test_is_available_false_when_token_missing() -> None:
sources = {
"snowflake": {
"connection_verified": True,
"account_identifier": "xy12345.us-east-1",
"token": " ",
}
}
assert _rt().is_available(sources) is False
def test_is_available_false_when_account_identifier_missing() -> None:
sources = {
"snowflake": {
"connection_verified": True,
"account_identifier": "",
"token": "sf-token",
}
}
assert _rt().is_available(sources) is False
def test_is_available_false_when_no_snowflake_source() -> None:
assert _rt().is_available({}) is False
def test_extract_params_strips_and_defaults_max_results() -> None:
sources = {
"snowflake": {
"account_identifier": " xy12345.us-east-1 ",
"user": " sf-user ",
"password": " sf-pass ",
"token": " sf-token ",
"warehouse": " WH_X ",
"role": " ANALYST ",
"database": " ANALYTICS ",
"schema": " PUBLIC ",
"query": " SELECT 1 ",
"integration_id": " sf-1 ",
}
}
params = _rt().extract_params(sources)
assert params["account_identifier"] == "xy12345.us-east-1"
assert params["user"] == "sf-user"
assert params["password"] == "sf-pass"
assert params["token"] == "sf-token"
assert params["warehouse"] == "WH_X"
assert params["role"] == "ANALYST"
assert params["database"] == "ANALYTICS"
assert params["db_schema"] == "PUBLIC"
assert params["query"] == "SELECT 1"
assert params["integration_id"] == "sf-1"
# Default max_results when key absent
assert params["max_results"] == 50
# The tool always extracts a fixed limit of 50; bounding happens at run time
assert params["limit"] == 50
def test_extract_params_uses_default_max_results_when_zero_or_missing() -> None:
# max_results explicitly 0 should fall back to the default (truthy guard)
sources = {"snowflake": {"account_identifier": "x", "token": "t", "max_results": 0}}
assert _rt().extract_params(sources)["max_results"] == 50
# ---------------------------------------------------------------------------
# Validation guards — missing config / required fields
# ---------------------------------------------------------------------------
def test_returns_missing_account_when_account_identifier_blank() -> None:
result = query_snowflake_history(account_identifier=" ", token="sf-token")
assert result["available"] is False
assert result["error"] == "Missing account identifier."
assert result["rows"] == []
def test_returns_missing_token_when_token_blank() -> None:
result = query_snowflake_history(account_identifier="xy12345.us-east-1", token=" ")
assert result["available"] is False
assert result["error"] == "Missing Snowflake token."
assert result["rows"] == []
# ---------------------------------------------------------------------------
# Request shaping — SQL LIMIT enforcement, payload contents, endpoint
# ---------------------------------------------------------------------------
def test_default_query_used_when_query_blank(monkeypatch: pytest.MonkeyPatch) -> None:
captured: dict[str, Any] = {}
def _fake_post(url: str, headers: dict[str, str], json: dict[str, Any], timeout: float):
captured["url"] = url
captured["statement"] = json["statement"]
captured["headers"] = headers
return MockHttpxResponse({"data": []})
monkeypatch.setattr(
"integrations.snowflake.tools.snowflake_query_history_tool.httpx.post", _fake_post
)
result = query_snowflake_history(
account_identifier="xy12345.us-east-1",
token="sf-token",
query="",
max_results=10,
)
assert result["available"] is True
assert "INFORMATION_SCHEMA.QUERY_HISTORY" in captured["statement"]
# Default query embeds RESULT_LIMIT directly, not a trailing LIMIT clause
assert "RESULT_LIMIT => 10" in captured["statement"]
# Endpoint is correctly templated from the account identifier
assert captured["url"] == "https://xy12345.us-east-1.snowflakecomputing.com/api/v2/statements"
assert captured["headers"]["Authorization"] == "Bearer sf-token"
assert captured["headers"]["Content-Type"] == "application/json"
def test_user_query_gets_limit_appended_if_missing(monkeypatch: pytest.MonkeyPatch) -> None:
captured: dict[str, Any] = {}
def _fake_post(url: str, headers: dict[str, str], json: dict[str, Any], timeout: float):
captured["statement"] = json["statement"]
return MockHttpxResponse({"data": []})
monkeypatch.setattr(
"integrations.snowflake.tools.snowflake_query_history_tool.httpx.post", _fake_post
)
query_snowflake_history(
account_identifier="xy12345.us-east-1",
token="sf-token",
query="SELECT * FROM T;",
limit=1000, # caller asks for 1000 but max_results caps it
max_results=7,
)
assert captured["statement"].endswith("LIMIT 7")
assert ";" not in captured["statement"] # trailing semicolon stripped
def test_user_query_with_existing_limit_is_left_untouched(
monkeypatch: pytest.MonkeyPatch,
) -> None:
captured: dict[str, Any] = {}
def _fake_post(url: str, headers: dict[str, str], json: dict[str, Any], timeout: float):
captured["statement"] = json["statement"]
return MockHttpxResponse({"data": []})
monkeypatch.setattr(
"integrations.snowflake.tools.snowflake_query_history_tool.httpx.post", _fake_post
)
query_snowflake_history(
account_identifier="xy12345.us-east-1",
token="sf-token",
query="SELECT * FROM T LIMIT 3",
limit=500,
max_results=50,
)
# The ensure_sql_limit helper must not double-stamp the LIMIT clause
assert captured["statement"].lower().count("limit") == 1
assert captured["statement"].endswith("LIMIT 3")
def test_optional_session_params_only_included_when_set(
monkeypatch: pytest.MonkeyPatch,
) -> None:
captured: dict[str, Any] = {}
def _fake_post(url: str, headers: dict[str, str], json: dict[str, Any], timeout: float):
captured["payload"] = json
return MockHttpxResponse({"data": []})
monkeypatch.setattr(
"integrations.snowflake.tools.snowflake_query_history_tool.httpx.post", _fake_post
)
query_snowflake_history(
account_identifier="xy12345.us-east-1",
token="sf-token",
warehouse="WH_X",
role="ANALYST",
database="DB",
db_schema="PUBLIC",
max_results=5,
)
payload = captured["payload"]
assert payload["warehouse"] == "WH_X"
assert payload["role"] == "ANALYST"
assert payload["database"] == "DB"
assert payload["schema"] == "PUBLIC"
assert "statement" in payload
assert "timeout" in payload
def test_optional_session_params_omitted_when_blank(
monkeypatch: pytest.MonkeyPatch,
) -> None:
captured: dict[str, Any] = {}
def _fake_post(url: str, headers: dict[str, str], json: dict[str, Any], timeout: float):
captured["payload"] = json
return MockHttpxResponse({"data": []})
monkeypatch.setattr(
"integrations.snowflake.tools.snowflake_query_history_tool.httpx.post", _fake_post
)
query_snowflake_history(
account_identifier="xy12345.us-east-1",
token="sf-token",
max_results=5,
)
payload = captured["payload"]
for key in ("warehouse", "role", "database", "schema"):
assert key not in payload
def test_bounded_limit_caps_caller_request(monkeypatch: pytest.MonkeyPatch) -> None:
"""Caller asks for limit=500 but max_results=6 ⇒ server query and rows capped at 6."""
def _fake_post(url: str, headers: dict[str, str], json: dict[str, Any], timeout: float):
# Server returns 20 rows — tool must trim to effective_limit (6)
return MockHttpxResponse({"data": [{"id": idx} for idx in range(20)]})
monkeypatch.setattr(
"integrations.snowflake.tools.snowflake_query_history_tool.httpx.post", _fake_post
)
result = query_snowflake_history(
account_identifier="xy12345.us-east-1",
token="sf-token",
query="SELECT * FROM TABLE(INFORMATION_SCHEMA.QUERY_HISTORY())",
limit=500,
max_results=6,
)
assert result["available"] is True
assert len(result["rows"]) == 6
assert result["total_returned"] == 6
# ---------------------------------------------------------------------------
# Row normalization — both API response shapes
# ---------------------------------------------------------------------------
def test_normalizes_dict_rows_passthrough(monkeypatch: pytest.MonkeyPatch) -> None:
"""When the API returns ``data=[{...}, ...]``, rows are passed through as-is."""
def _fake_post(*_args: Any, **_kwargs: Any) -> MockHttpxResponse:
return MockHttpxResponse(
{
"data": [
{"query_id": "q1", "user_name": "alice"},
{"query_id": "q2", "user_name": "bob"},
]
}
)
monkeypatch.setattr(
"integrations.snowflake.tools.snowflake_query_history_tool.httpx.post", _fake_post
)
result = query_snowflake_history(
account_identifier="xy12345.us-east-1",
token="sf-token",
max_results=10,
)
assert result["rows"] == [
{"query_id": "q1", "user_name": "alice"},
{"query_id": "q2", "user_name": "bob"},
]
def test_normalizes_tabular_rows_using_result_set_metadata(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""When the API returns lists of values + rowType metadata, rows are zipped to dicts."""
def _fake_post(*_args: Any, **_kwargs: Any) -> MockHttpxResponse:
return MockHttpxResponse(
{
"data": [
["q1", "alice"],
["q2", "bob"],
],
"resultSetMetaData": {
"rowType": [
{"name": "query_id"},
{"name": "user_name"},
]
},
}
)
monkeypatch.setattr(
"integrations.snowflake.tools.snowflake_query_history_tool.httpx.post", _fake_post
)
result = query_snowflake_history(
account_identifier="xy12345.us-east-1",
token="sf-token",
max_results=10,
)
assert result["rows"] == [
{"query_id": "q1", "user_name": "alice"},
{"query_id": "q2", "user_name": "bob"},
]
def test_normalizes_to_empty_when_payload_unrecognized(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Unknown response shapes degrade gracefully to an empty rows list, not an exception."""
def _fake_post(*_args: Any, **_kwargs: Any) -> MockHttpxResponse:
return MockHttpxResponse({"unexpected": "shape"})
monkeypatch.setattr(
"integrations.snowflake.tools.snowflake_query_history_tool.httpx.post", _fake_post
)
result = query_snowflake_history(
account_identifier="xy12345.us-east-1",
token="sf-token",
max_results=10,
)
assert result["available"] is True
assert result["rows"] == []
assert result["total_returned"] == 0
# ---------------------------------------------------------------------------
# HTTP failure handling
# ---------------------------------------------------------------------------
def test_returns_unavailable_on_http_error_status(monkeypatch: pytest.MonkeyPatch) -> None:
def _fake_post(*_args: Any, **_kwargs: Any) -> MockHttpxResponse:
return MockHttpxResponse(
{"error": "boom"},
raise_for_status_error=RuntimeError("HTTP 500 from Snowflake"),
)
monkeypatch.setattr(
"integrations.snowflake.tools.snowflake_query_history_tool.httpx.post", _fake_post
)
result = query_snowflake_history(
account_identifier="xy12345.us-east-1",
token="sf-token",
max_results=5,
)
assert result["available"] is False
assert "HTTP 500 from Snowflake" in result["error"]
assert result["rows"] == []
def test_returns_unavailable_on_request_exception(monkeypatch: pytest.MonkeyPatch) -> None:
def _raise(*_args: Any, **_kwargs: Any) -> None:
raise ConnectionError("connection refused")
monkeypatch.setattr(
"integrations.snowflake.tools.snowflake_query_history_tool.httpx.post", _raise
)
result = query_snowflake_history(
account_identifier="xy12345.us-east-1",
token="sf-token",
max_results=5,
)
assert result["available"] is False
assert "connection refused" in result["error"]
assert result["rows"] == []