import pytest from pydantic import ValidationError as PydanticValidationError from src.models.query_topsql import ( TOPSQL_CPU_TIME_METRIC, TOPSQL_SQL_META_METRIC, TopSQLEntry, TopSQLRequest, ) class TestTopSQLRequestValidation: def test_valid_minimal_request(self): req = TopSQLRequest.model_validate( { "cluster_id": "123", "env": "prod", "deploy_type": "premium", "start": "2024-01-01T00:00:00Z", "end": "2024-01-01T01:00:00Z", } ) assert req.cluster_id == "123" assert req.limit == 20 assert req.instance_type is None def test_normalizes_env_and_deploy_type(self): req = TopSQLRequest.model_validate( { "cluster_id": "123", "env": " Prod ", "deploy_type": " Premium ", "start": "2024-01-01T00:00:00Z", "end": "2024-01-01T01:00:00Z", } ) assert req.env == "prod" assert req.deploy_type == "premium" def test_rejects_non_string_cluster_id(self): with pytest.raises(PydanticValidationError): TopSQLRequest.model_validate( { "cluster_id": 123, "env": "prod", "deploy_type": "premium", "start": "2024-01-01T00:00:00Z", "end": "2024-01-01T01:00:00Z", } ) def test_rejects_invalid_timestamp(self): with pytest.raises(PydanticValidationError): TopSQLRequest.model_validate( { "cluster_id": "123", "env": "prod", "deploy_type": "premium", "start": "not-a-timestamp", "end": "2024-01-01T01:00:00Z", } ) def test_rejects_start_after_end(self): with pytest.raises(PydanticValidationError): TopSQLRequest.model_validate( { "cluster_id": "123", "env": "prod", "deploy_type": "premium", "start": "2024-01-02T00:00:00Z", "end": "2024-01-01T00:00:00Z", } ) def test_limit_bounds(self): with pytest.raises(PydanticValidationError): TopSQLRequest.model_validate( { "cluster_id": "123", "env": "prod", "deploy_type": "premium", "start": "2024-01-01T00:00:00Z", "end": "2024-01-01T01:00:00Z", "limit": 0, } ) with pytest.raises(PydanticValidationError): TopSQLRequest.model_validate( { "cluster_id": "123", "env": "prod", "deploy_type": "premium", "start": "2024-01-01T00:00:00Z", "end": "2024-01-01T01:00:00Z", "limit": 101, } ) class TestTopSQLRequestMethods: def _make_request(self, **overrides): defaults = { "cluster_id": "123", "env": "prod", "deploy_type": "premium", "start": "2024-01-01T00:00:00Z", "end": "2024-01-01T01:00:00Z", } defaults.update(overrides) return TopSQLRequest.model_validate(defaults) def test_instance_type_normalizes_case(self): req = self._make_request(instance_type=" TiKV ") assert req.instance_type == "tikv" def test_instance_type_rejects_invalid(self): with pytest.raises(PydanticValidationError): self._make_request(instance_type="tiflash") def test_compute_range_duration_1h(self): req = self._make_request( start="2024-01-01T00:00:00Z", end="2024-01-01T01:00:00Z", ) assert req.compute_range_duration() == "1h" def test_compute_range_duration_30m(self): req = self._make_request( start="2024-01-01T00:00:00Z", end="2024-01-01T00:30:00Z", ) assert req.compute_range_duration() == "30m" def test_compute_range_duration_rounds_up(self): req = self._make_request( start="2024-01-01T00:00:00Z", end="2024-01-01T00:01:30Z", ) assert req.compute_range_duration() == "2m" def test_compute_range_duration_zero_defaults_to_5m(self): req = self._make_request( start="2024-01-01T00:00:00Z", end="2024-01-01T00:00:00Z", ) assert req.compute_range_duration() == "5m" def test_compute_range_duration_multi_hour(self): req = self._make_request( start="2024-01-01T00:00:00Z", end="2024-01-01T06:00:00Z", ) assert req.compute_range_duration() == "6h" class TestTopSQLEntry: def test_creates_entry(self): entry = TopSQLEntry(rank=1, sql_digest="abc", cpu_time_ms=123.4) assert entry.rank == 1 assert entry.sql_digest == "abc" assert entry.sql_text is None assert entry.cpu_time_ms == 123.4 def test_creates_entry_with_sql_text(self): entry = TopSQLEntry(rank=1, sql_digest="abc", sql_text="SELECT 1", cpu_time_ms=0.5) assert entry.sql_text == "SELECT 1" class TestConstants: def test_metric_names_are_strings(self): assert isinstance(TOPSQL_CPU_TIME_METRIC, str) assert isinstance(TOPSQL_SQL_META_METRIC, str) def test_metric_names_start_with_topsql(self): assert TOPSQL_CPU_TIME_METRIC.startswith("topsql_") assert TOPSQL_SQL_META_METRIC.startswith("topsql_") def test_cpu_time_metric_is_raw(self): assert TOPSQL_CPU_TIME_METRIC == "topsql_cpu_time_ms" def test_sql_meta_metric(self): assert TOPSQL_SQL_META_METRIC == "topsql_sql_meta"