test: 8 parallel agents — fix 150+ failures, add 30+ test files. schemas/models 99-100%, core/client_registry ~98%, maintenance 95-98%, git edges 97%, translate 98-100%, dashboard routes 95-96%, dataset_review 100%

This commit is contained in:
2026-06-15 17:31:43 +03:00
parent a18f19064f
commit 4e6bfa8549
43 changed files with 7792 additions and 92 deletions

View File

@@ -0,0 +1,243 @@
# #region Test.SupersetClient.Datasets.Edge [C:3] [TYPE Module] [SEMANTICS test, superset, datasets, mixin, edge]
# @BRIEF Edge case tests for SupersetDatasetsMixin — get_datasets_summary, get_dataset_linked_dashboard_count, get_dataset_detail edge cases.
# @RELATION BINDS_TO -> [SupersetDatasetsMixin]
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src"))
from unittest.mock import AsyncMock, MagicMock
import pytest
class TestGetDatasetsSummary:
@pytest.mark.asyncio
async def test_summary(self):
from src.core.superset_client._datasets import SupersetDatasetsMixin
mixin = SupersetDatasetsMixin()
mixin.get_datasets = AsyncMock(return_value=(2, [
{"id": 1, "table_name": "orders", "schema": "public", "database": {"database_name": "Main"}},
{"id": 2, "table_name": "users", "schema": "public"}, # no database key → fallback to Unknown
]))
result = await mixin.get_datasets_summary()
assert len(result) == 2
assert result[0]["database"] == "Main"
assert result[1]["database"] == "Unknown"
class TestGetDatasetLinkedDashboardCount:
@pytest.mark.asyncio
async def test_with_dashboards_in_response(self):
from src.core.superset_client._datasets import SupersetDatasetsMixin
mixin = SupersetDatasetsMixin()
mixin.client = AsyncMock()
mixin.client.request.return_value = {"dashboards": [{"id": 1}, {"id": 2}]}
count = await mixin.get_dataset_linked_dashboard_count(1)
assert count == 2
@pytest.mark.asyncio
async def test_with_result_key(self):
from src.core.superset_client._datasets import SupersetDatasetsMixin
mixin = SupersetDatasetsMixin()
mixin.client = AsyncMock()
mixin.client.request.return_value = {"result": {"dashboards": [{"id": 1}]}}
count = await mixin.get_dataset_linked_dashboard_count(1)
assert count == 1
@pytest.mark.asyncio
async def test_non_dict_response(self):
from src.core.superset_client._datasets import SupersetDatasetsMixin
mixin = SupersetDatasetsMixin()
mixin.client = AsyncMock()
mixin.client.request.return_value = "not_a_dict"
count = await mixin.get_dataset_linked_dashboard_count(1)
assert count == 0
@pytest.mark.asyncio
async def test_exception_returns_zero(self):
from src.core.superset_client._datasets import SupersetDatasetsMixin
mixin = SupersetDatasetsMixin()
mixin.client = AsyncMock()
mixin.client.request.side_effect = Exception("API error")
count = await mixin.get_dataset_linked_dashboard_count(1)
assert count == 0
@pytest.mark.asyncio
async def test_empty_dashboards(self):
from src.core.superset_client._datasets import SupersetDatasetsMixin
mixin = SupersetDatasetsMixin()
mixin.client = AsyncMock()
mixin.client.request.return_value = {"dashboards": []}
count = await mixin.get_dataset_linked_dashboard_count(1)
assert count == 0
@pytest.mark.asyncio
async def test_unknown_structure(self):
from src.core.superset_client._datasets import SupersetDatasetsMixin
mixin = SupersetDatasetsMixin()
mixin.client = AsyncMock()
mixin.client.request.return_value = {"other": "data"}
count = await mixin.get_dataset_linked_dashboard_count(1)
assert count == 0
class TestGetDatasetDetail:
@pytest.mark.asyncio
async def test_column_with_none_id_skipped(self):
from src.core.superset_client._datasets import SupersetDatasetsMixin
mixin = SupersetDatasetsMixin()
mixin.get_dataset = AsyncMock(return_value={
"result": {
"columns": [
{"id": None, "column_name": "skip_me"},
{"id": 2, "column_name": "keep_me"},
],
"metrics": [],
"sql": "SELECT 1",
"database": {"id": 1, "backend": "postgresql"},
}
})
mixin.client = MagicMock()
mixin.client.request = AsyncMock(return_value={"dashboards": [{"id": 1, "dashboard_title": "Dash"}]})
detail = await mixin.get_dataset_detail(1)
assert len(detail["columns"]) == 1
assert detail["columns"][0]["name"] == "keep_me"
@pytest.mark.asyncio
async def test_metric_with_none_id_skipped(self):
from src.core.superset_client._datasets import SupersetDatasetsMixin
mixin = SupersetDatasetsMixin()
mixin.get_dataset = AsyncMock(return_value={
"result": {
"columns": [],
"metrics": [
{"id": None, "metric_name": "skip"},
{"id": 5, "metric_name": "keep"},
],
"sql": "",
"database": {"id": 1},
}
})
mixin.client = MagicMock()
mixin.client.request = AsyncMock(return_value={"dashboards": []})
detail = await mixin.get_dataset_detail(1)
assert len(detail["metrics"]) == 1
assert detail["metrics"][0]["metric_name"] == "keep"
@pytest.mark.asyncio
async def test_dashboard_with_none_id_skipped(self):
from src.core.superset_client._datasets import SupersetDatasetsMixin
mixin = SupersetDatasetsMixin()
mixin.get_dataset = AsyncMock(return_value={
"result": {
"columns": [], "metrics": [], "sql": "",
"database": {"id": 1, "backend": "postgresql"},
}
})
mixin.client = MagicMock()
mixin.client.request = AsyncMock(return_value={
"dashboards": [{"id": None, "dashboard_title": "Skip"}]
})
detail = await mixin.get_dataset_detail(1)
assert len(detail["linked_dashboards"]) == 0
@pytest.mark.asyncio
async def test_dashboard_int_id(self):
from src.core.superset_client._datasets import SupersetDatasetsMixin
mixin = SupersetDatasetsMixin()
mixin.get_dataset = AsyncMock(return_value={
"result": {
"columns": [], "metrics": [], "sql": "",
"database": {"id": 1, "backend": "postgresql"},
}
})
mixin.client = MagicMock()
mixin.client.request = AsyncMock(return_value={
"dashboards": [
{"id": 10, "dashboard_title": "Good"},
{"id": None, "dashboard_title": "Skip"},
]
})
detail = await mixin.get_dataset_detail(1)
assert len(detail["linked_dashboards"]) == 1
@pytest.mark.asyncio
async def test_dashboards_from_result(self):
from src.core.superset_client._datasets import SupersetDatasetsMixin
mixin = SupersetDatasetsMixin()
mixin.get_dataset = AsyncMock(return_value={
"result": {"columns": [], "metrics": [], "sql": "", "database": {"id": 1}}
})
mixin.client = MagicMock()
# Use the result key path
mixin.client.request = AsyncMock(return_value={
"result": {"dashboards": {"result": [{"id": 1}]}}
})
detail = await mixin.get_dataset_detail(1)
assert len(detail["linked_dashboards"]) == 1
@pytest.mark.asyncio
async def test_dashboards_api_exception(self):
from src.core.superset_client._datasets import SupersetDatasetsMixin
mixin = SupersetDatasetsMixin()
mixin.get_dataset = AsyncMock(return_value={
"result": {"columns": [], "metrics": [], "sql": "", "database": {"id": 1}}
})
mixin.client = MagicMock()
mixin.client.request.side_effect = Exception("API error")
detail = await mixin.get_dataset_detail(1)
assert detail["linked_dashboards"] == []
@pytest.mark.asyncio
async def test_result_not_in_response_structure(self):
"""get_dataset returns data without 'result' key."""
from src.core.superset_client._datasets import SupersetDatasetsMixin
mixin = SupersetDatasetsMixin()
mixin.get_dataset = AsyncMock(return_value={
"id": 1, "columns": [{"id": 1, "column_name": "a"}],
"metrics": [], "sql": "", "database": {"id": 1},
})
mixin.client = MagicMock()
mixin.client.request = AsyncMock(return_value={"dashboards": []})
detail = await mixin.get_dataset_detail(1)
assert detail["id"] == 1
assert len(detail["columns"]) == 1
@pytest.mark.asyncio
async def test_as_bool_edge_cases(self):
"""as_bool handles various input types."""
from src.core.superset_client._datasets import SupersetDatasetsMixin
mixin = SupersetDatasetsMixin()
mixin.get_dataset = AsyncMock(return_value={
"result": {
"columns": [
{"id": 1, "column_name": "c1", "is_dttm": "true", "is_active": "yes"},
{"id": 2, "column_name": "c2", "is_dttm": "false", "is_active": "no"},
],
"metrics": [], "sql": "", "database": {"id": 1},
}
})
mixin.client = MagicMock()
mixin.client.request = AsyncMock(return_value={"dashboards": []})
detail = await mixin.get_dataset_detail(1)
cols = {c["name"]: c for c in detail["columns"]}
assert cols["c1"]["is_dttm"] is True
assert cols["c2"]["is_dttm"] is False
@pytest.mark.asyncio
async def test_database_backend_fallback(self):
"""Uses engine as fallback for database_backend."""
from src.core.superset_client._datasets import SupersetDatasetsMixin
mixin = SupersetDatasetsMixin()
mixin.get_dataset = AsyncMock(return_value={
"result": {
"columns": [], "metrics": [], "sql": "",
"database": {"id": 1, "engine": "postgresql"},
}
})
mixin.client = MagicMock()
mixin.client.request = AsyncMock(return_value={"dashboards": []})
detail = await mixin.get_dataset_detail(1)
assert detail["database_backend"] == "postgresql"
# #endregion Test.SupersetClient.Datasets.Edge

View File

@@ -0,0 +1,203 @@
# #region Test.TaskManager.Graph [C:3] [TYPE Module] [SEMANTICS test, task, graph, registry, crud]
# @BRIEF Tests for TaskGraph — in-memory task registry with persistence.
# @RELATION BINDS_TO -> [TaskGraphModule]
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src"))
from datetime import UTC, datetime
from unittest.mock import MagicMock, AsyncMock
import pytest
from src.core.task_manager.graph import TaskGraph
from src.core.task_manager.models import Task, TaskStatus
from src.core.task_manager.persistence import TaskPersistenceService
def _make_task(**kwargs):
"""Helper to create a Task mock with required attributes."""
t = MagicMock(spec=Task)
t.started_at = kwargs.pop('started_at', None)
for k, v in kwargs.items():
setattr(t, k, v)
return t
@pytest.fixture
def persistence():
return MagicMock(spec=TaskPersistenceService)
@pytest.fixture
def graph(persistence):
return TaskGraph(persistence)
class TestInit:
def test_empty(self, graph):
assert graph.tasks == {}
assert graph.task_futures == {}
assert graph.persistence_service is not None
class TestLoadPersistedTasks:
def test_loads_new_tasks(self, graph, persistence):
task1 = _make_task(id="t1")
task2 = _make_task(id="t2")
persistence.load_tasks.return_value = [task1, task2]
graph.load_persisted_tasks(limit=50)
assert len(graph.tasks) == 2
assert graph.tasks["t1"] is task1
assert graph.tasks["t2"] is task2
persistence.load_tasks.assert_called_once_with(limit=50)
def test_skips_existing_tasks(self, graph, persistence):
existing = _make_task(id="t1")
graph.tasks["t1"] = existing
new_task = _make_task(id="t2")
loaded = _make_task(id="t1") # same ID as existing
persistence.load_tasks.return_value = [loaded, new_task]
graph.load_persisted_tasks()
assert graph.tasks["t1"] is existing # not overwritten
assert graph.tasks["t2"] is new_task
class TestGetTask:
def test_found(self, graph):
task = _make_task(id="t1")
graph.tasks["t1"] = task
assert graph.get_task("t1") is task
def test_not_found(self, graph):
assert graph.get_task("nonexistent") is None
class TestGetAllTasks:
def test_all(self, graph):
t1 = _make_task(id="a")
t2 = _make_task(id="b")
graph.tasks = {"a": t1, "b": t2}
all_tasks = graph.get_all_tasks()
assert len(all_tasks) == 2
class TestGetTasks:
def test_no_filters(self, graph):
t1 = _make_task(id="a", started_at=None)
t2 = _make_task(id="b", started_at=None)
graph.tasks = {"a": t1, "b": t2}
result = graph.get_tasks(limit=10, offset=0)
assert len(result) <= 2
def test_filter_by_status(self, graph):
t1 = _make_task(id="a", status=TaskStatus.SUCCESS, started_at=None)
t2 = _make_task(id="b", status=TaskStatus.FAILED, started_at=None)
graph.tasks = {"a": t1, "b": t2}
result = graph.get_tasks(limit=10, offset=0, status=TaskStatus.SUCCESS)
assert len(result) == 1
assert result[0] is t1
def test_filter_by_plugin_ids(self, graph):
t1 = _make_task(id="a", plugin_id="plugin_a", started_at=None)
t2 = _make_task(id="b", plugin_id="plugin_b", started_at=None)
graph.tasks = {"a": t1, "b": t2}
result = graph.get_tasks(limit=10, offset=0, plugin_ids=["plugin_a"])
assert len(result) == 1
assert result[0] is t1
def test_filter_completed_only(self, graph):
t1 = _make_task(id="a", status=TaskStatus.SUCCESS, started_at=None)
t2 = _make_task(id="b", status=TaskStatus.RUNNING, started_at=None)
t3 = _make_task(id="c", status=TaskStatus.FAILED, started_at=None)
graph.tasks = {"a": t1, "b": t2, "c": t3}
result = graph.get_tasks(limit=10, offset=0, completed_only=True)
assert len(result) == 2
def test_sort_key_none_started_at(self, graph):
"""Tasks with None started_at get sorted to the end."""
t1 = _make_task(id="a", started_at=None)
t2 = _make_task(id="b", started_at=datetime.now(UTC))
graph.tasks = {"a": t1, "b": t2}
result = graph.get_tasks(limit=10, offset=0)
assert result[0] is t2 # newest first
def test_sort_key_non_datetime(self, graph):
"""started_at is not a datetime → treated as -inf."""
t1 = _make_task(id="a", started_at="not_a_datetime")
t2 = _make_task(id="b", started_at=datetime.now(UTC))
graph.tasks = {"a": t1, "b": t2}
result = graph.get_tasks(limit=10, offset=0)
assert result[0] is t2
def test_sort_key_naive_datetime(self, graph):
"""Naive datetime gets UTC timezone."""
t1 = _make_task(id="a", started_at=datetime(2024, 1, 1, 12, 0, 0)) # no tz
t2 = _make_task(id="b", started_at=datetime(2024, 1, 1, 13, 0, 0, tzinfo=UTC))
graph.tasks = {"a": t1, "b": t2}
result = graph.get_tasks(limit=10, offset=0)
assert result[0] is t2
class TestAddTask:
def test_adds(self, graph):
task = _make_task(id="t1")
graph.add_task(task)
assert graph.tasks["t1"] is task
class TestRemoveTasks:
def test_removes_with_future(self, graph):
task = _make_task(id="t1")
future = MagicMock()
graph.tasks["t1"] = task
graph.task_futures["t1"] = future
count = graph.remove_tasks(["t1"])
assert count == 1
assert "t1" not in graph.tasks
assert "t1" not in graph.task_futures
future.cancel.assert_called_once()
def test_removes_without_future(self, graph, persistence):
task = _make_task(id="t1")
graph.tasks["t1"] = task
count = graph.remove_tasks(["t1"])
assert count == 1
persistence.delete_tasks.assert_called_once_with(["t1"])
def test_removes_empty_list(self, graph, persistence):
count = graph.remove_tasks([])
assert count == 0
persistence.delete_tasks.assert_not_called()
class TestCreateFuture:
def test_creates(self, graph):
future = MagicMock()
graph.create_future("t1", future)
assert graph.task_futures["t1"] is future
class TestResolveFuture:
def test_resolves(self, graph):
future = MagicMock()
graph.task_futures["t1"] = future
graph.resolve_future("t1", result=True)
future.set_result.assert_called_once_with(True)
assert "t1" not in graph.task_futures
def test_resolve_nonexistent(self, graph):
graph.resolve_future("nonexistent") # should not raise
class TestRemoveFuture:
def test_removes(self, graph):
future = MagicMock()
graph.task_futures["t1"] = future
graph.remove_future("t1")
assert "t1" not in graph.task_futures
def test_remove_nonexistent(self, graph):
graph.remove_future("nonexistent") # should not raise
# #endregion Test.TaskManager.Graph