# #region Test.DatasetsMixin [C:3] [TYPE Module] [SEMANTICS test, dataset, superset, crud, detail] # @BRIEF Tests for core/superset_client/_datasets.py — SupersetDatasetsMixin. # @RELATION BINDS_TO -> [SupersetDatasetsMixin] # @TEST_EDGE: missing_param -> Null query param handled # @TEST_EDGE: empty_result -> Empty dataset list returns 0 results # @TEST_EDGE: external_api_error -> Exception in API call caught and logged from pathlib import Path import sys sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src")) from unittest.mock import AsyncMock, MagicMock, patch import pytest class TestDatasetsMixin: """Test all methods of SupersetDatasetsMixin.""" @pytest.fixture def mixin(self): from src.core.superset_client._datasets import SupersetDatasetsMixin m = SupersetDatasetsMixin() m.client = MagicMock() m.client.request = AsyncMock() m._validate_query_params = MagicMock(return_value={"page": 0, "page_size": 100}) m._fetch_all_pages = AsyncMock(return_value=[{"id": 1}, {"id": 2}]) return m # ── get_datasets ── @pytest.mark.asyncio async def test_get_datasets_success(self, mixin): total, data = await mixin.get_datasets(query={"columns": ["id"]}) assert total == 2 assert len(data) == 2 mixin._validate_query_params.assert_called_once_with({"columns": ["id"]}) mixin._fetch_all_pages.assert_awaited_once() @pytest.mark.asyncio async def test_get_datasets_no_query(self, mixin): total, data = await mixin.get_datasets() assert total == 2 mixin._validate_query_params.assert_called_once_with(None) @pytest.mark.asyncio async def test_get_datasets_empty_result(self, mixin): mixin._fetch_all_pages = AsyncMock(return_value=[]) total, data = await mixin.get_datasets() assert total == 0 assert data == [] # ── get_datasets_summary ── @pytest.mark.asyncio async def test_get_datasets_summary(self, mixin): mixin.get_datasets = AsyncMock( return_value=( 2, [ { "id": 1, "table_name": "users", "schema": "public", "database": {"database_name": "Postgres"}, }, { "id": 2, "table_name": "orders", "schema": "public", "database": {"database_name": "Postgres"}, }, ], ) ) result = await mixin.get_datasets_summary() assert len(result) == 2 assert result[0]["table_name"] == "users" assert result[0]["database"] == "Postgres" assert result[1]["schema"] == "public" @pytest.mark.asyncio async def test_get_datasets_summary_missing_database(self, mixin): mixin.get_datasets = AsyncMock( return_value=( 1, [{"id": 1, "table_name": "t", "schema": "public"}], ) ) result = await mixin.get_datasets_summary() assert result[0]["database"] == "Unknown" @pytest.mark.asyncio async def test_get_datasets_summary_empty(self, mixin): mixin.get_datasets = AsyncMock(return_value=(0, [])) result = await mixin.get_datasets_summary() assert result == [] # ── get_dataset_linked_dashboard_count ── @pytest.mark.asyncio async def test_get_dataset_linked_dashboard_count_direct_key(self, mixin): mixin.client.request.return_value = {"dashboards": [{"id": 1}, {"id": 2}]} result = await mixin.get_dataset_linked_dashboard_count(1) assert result == 2 @pytest.mark.asyncio async def test_get_dataset_linked_dashboard_count_result_key(self, mixin): mixin.client.request.return_value = {"result": {"dashboards": [{"id": 1}]}} result = await mixin.get_dataset_linked_dashboard_count(1) assert result == 1 @pytest.mark.asyncio async def test_get_dataset_linked_dashboard_count_empty(self, mixin): mixin.client.request.return_value = {"result": {"dashboards": []}} result = await mixin.get_dataset_linked_dashboard_count(1) assert result == 0 @pytest.mark.asyncio async def test_get_dataset_linked_dashboard_count_non_dict(self, mixin): mixin.client.request.return_value = "not_a_dict" result = await mixin.get_dataset_linked_dashboard_count(1) assert result == 0 @pytest.mark.asyncio async def test_get_dataset_linked_dashboard_count_error(self, mixin): mixin.client.request.side_effect = Exception("API error") with patch("src.core.superset_client._datasets.app_logger") as mock_log: result = await mixin.get_dataset_linked_dashboard_count(1) assert result == 0 mock_log.explore.assert_called_once() # ── get_dataset_detail ── @pytest.fixture def detail_mixin(self): from src.core.superset_client._datasets import SupersetDatasetsMixin m = SupersetDatasetsMixin() m.client = MagicMock() m.client.request = AsyncMock() m.get_dataset = AsyncMock() return m @pytest.mark.asyncio async def test_get_dataset_detail_full(self, detail_mixin): detail_mixin.get_dataset.return_value = { "result": { "id": 1, "table_name": "users", "schema": "public", "database": {"id": 1, "backend": "postgresql", "database_name": "Postgres"}, "database_id": 1, "description": "User table", "columns": [ {"id": 1, "column_name": "name", "type": "text", "is_dttm": False, "is_active": True, "description": ""}, {"id": 2, "column_name": "created", "type": "timestamp", "is_dttm": True, "is_active": True, "description": ""}, ], "metrics": [ {"id": 1, "metric_name": "count", "expression": "COUNT(*)", "verbose_name": "Count", "description": "", "metric_type": "count"}, ], "sql": "SELECT * FROM users", "is_sqllab_view": False, "created_on": "2024-01-01", "changed_on": "2024-01-02", } } detail_mixin.client.request.return_value = { "dashboards": {"result": [{"id": 10, "dashboard_title": "Main", "slug": "main"}]} } result = await detail_mixin.get_dataset_detail(1) assert result["id"] == 1 assert result["table_name"] == "users" assert len(result["columns"]) == 2 assert result["columns"][0]["name"] == "name" assert result["columns"][1]["is_dttm"] is True assert len(result["metrics"]) == 1 assert result["metrics"][0]["metric_name"] == "count" assert result["linked_dashboard_count"] == 1 assert result["database_backend"] == "postgresql" assert result["is_sqllab_view"] is False @pytest.mark.asyncio async def test_get_dataset_detail_no_result_key(self, detail_mixin): detail_mixin.get_dataset.return_value = { "id": 1, "table_name": "no_result", "columns": [], "metrics": [], } detail_mixin.client.request.return_value = {} result = await detail_mixin.get_dataset_detail(1) assert result["table_name"] == "no_result" assert result["columns"] == [] assert result["metrics"] == [] @pytest.mark.asyncio async def test_get_dataset_detail_related_objects_error(self, detail_mixin): detail_mixin.get_dataset.return_value = { "result": { "id": 1, "table_name": "test", "columns": [], "metrics": [], } } detail_mixin.client.request.side_effect = Exception("Connection error") with patch("src.core.superset_client._datasets.app_logger") as mock_log: result = await detail_mixin.get_dataset_detail(1) assert result["linked_dashboards"] == [] mock_log.explore.assert_called_once() @pytest.mark.asyncio async def test_get_dataset_detail_related_objects_has_dashboards_direct(self, detail_mixin): detail_mixin.get_dataset.return_value = { "result": { "id": 1, "table_name": "test", "columns": [], "metrics": [], "database": {}, } } detail_mixin.client.request.return_value = {"dashboards": [{"id": 5, "dashboard_title": "Dash"}]} result = await detail_mixin.get_dataset_detail(1) assert result["linked_dashboard_count"] == 1 assert result["linked_dashboards"][0]["id"] == 5 @pytest.mark.asyncio async def test_get_dataset_detail_related_objects_with_int_list(self, detail_mixin): detail_mixin.get_dataset.return_value = { "result": { "id": 1, "table_name": "test", "columns": [], "metrics": [], } } detail_mixin.client.request.return_value = {"dashboards": [5, 6]} result = await detail_mixin.get_dataset_detail(1) assert result["linked_dashboard_count"] == 2 assert result["linked_dashboards"][0]["id"] == 5 @pytest.mark.asyncio async def test_get_dataset_detail_as_bool_variants(self, detail_mixin): detail_mixin.get_dataset.return_value = { "result": { "id": 1, "table_name": "test", "columns": [ {"id": 1, "column_name": "a", "is_dttm": "1", "is_active": "true"}, {"id": 2, "column_name": "b", "is_dttm": "yes", "is_active": "on"}, {"id": 3, "column_name": "c", "is_dttm": None, "is_active": None}, {"id": 4, "column_name": "d", "is_dttm": 1, "is_active": 0}, ], "metrics": [], "database": {"backend": "mysql"}, } } detail_mixin.client.request.return_value = {} result = await detail_mixin.get_dataset_detail(1) assert result["columns"][0]["is_dttm"] is True assert result["columns"][1]["is_dttm"] is True assert result["columns"][2]["is_dttm"] is False assert result["columns"][3]["is_dttm"] is True assert result["columns"][0]["is_active"] is True assert result["columns"][3]["is_active"] is False # ── get_dataset ── @pytest.mark.asyncio async def test_get_dataset_success(self, mixin): mixin.client.request.return_value = {"result": {"id": 1, "table_name": "test"}} result = await mixin.get_dataset(1) assert result["result"]["id"] == 1 # ── update_dataset ── @pytest.mark.asyncio async def test_update_dataset_success(self, mixin): mixin.client.request.return_value = {"result": "ok"} result = await mixin.update_dataset(1, {"columns": []}, override_columns=True) mixin.client.request.assert_awaited_once() assert result["result"] == "ok" @pytest.mark.asyncio async def test_update_dataset_no_override(self, mixin): mixin.client.request.return_value = {"result": "ok"} result = await mixin.update_dataset(1, {"columns": []}) assert result["result"] == "ok" @pytest.mark.asyncio async def test_update_dataset_with_json_data(self, mixin): mixin.client.request.return_value = {"result": "ok"} result = await mixin.update_dataset(1, {"table_name": "new_name"}, override_columns=False) assert result["result"] == "ok" # #endregion Test.DatasetsMixin