codex specify

This commit is contained in:
2026-02-25 21:19:48 +03:00
parent b7d1ee2b71
commit 5ec1254336
40 changed files with 3535 additions and 238 deletions

View File

@@ -10,6 +10,7 @@ import os
import asyncio
from types import SimpleNamespace
from datetime import datetime, timedelta
import pytest
# Force isolated sqlite databases for test module before dependencies import.
os.environ.setdefault("DATABASE_URL", "sqlite:////tmp/ss_tools_assistant_api.db")
@@ -446,7 +447,7 @@ def test_list_conversations_groups_by_conversation_and_marks_archived():
conversation_id="conv-old",
role="user",
text="old chat",
created_at=now - timedelta(days=assistant_module.ASSISTANT_ARCHIVE_AFTER_DAYS + 2),
created_at=now - timedelta(days=32), # Hardcoded threshold+2
)
)
@@ -536,7 +537,7 @@ def test_list_conversations_archived_only_filters_active():
conversation_id="conv-archived-2",
role="user",
text="archived",
created_at=now - timedelta(days=assistant_module.ASSISTANT_ARCHIVE_AFTER_DAYS + 3),
created_at=now - timedelta(days=33), # Hardcoded threshold+3
)
)
@@ -624,5 +625,25 @@ def test_guarded_operation_confirm_roundtrip():
assert second.task_id is not None
# [DEF:test_confirm_nonexistent_id_returns_404:Function]
# @PURPOSE: Confirming a non-existent ID should raise 404.
# @PRE: user tries to confirm a random/fake UUID.
# @POST: FastAPI HTTPException with status 404.
def test_confirm_nonexistent_id_returns_404():
from fastapi import HTTPException
_clear_assistant_state()
with pytest.raises(HTTPException) as exc:
_run_async(
assistant_module.confirm_operation(
confirmation_id="non-existent-id",
current_user=_admin_user(),
task_manager=_FakeTaskManager(),
config_manager=_FakeConfigManager(),
db=_FakeDb(),
)
)
assert exc.value.status_code == 404
# [/DEF:test_guarded_operation_confirm_roundtrip:Function]
# [/DEF:backend.src.api.routes.__tests__.test_assistant_api:Module]

View File

@@ -249,6 +249,7 @@ def _make_sync_config_manager(environments):
config.environments = environments
cm = MagicMock()
cm.get_config.return_value = config
cm.get_environments.return_value = environments
return cm
@@ -343,4 +344,67 @@ async def test_trigger_sync_now_idempotent_env_upsert(db_session, _mock_env):
assert env_count == 1
# --- get_dashboards tests ---
@pytest.mark.asyncio
async def test_get_dashboards_success(_mock_env):
from src.api.routes.migration import get_dashboards
cm = _make_sync_config_manager([_mock_env])
with patch("src.api.routes.migration.SupersetClient") as MockClient:
mock_client = MagicMock()
mock_client.get_dashboards_summary.return_value = [{"id": 1, "title": "Test"}]
MockClient.return_value = mock_client
result = await get_dashboards(env_id="test-env-1", config_manager=cm, _=None)
assert len(result) == 1
assert result[0]["id"] == 1
@pytest.mark.asyncio
async def test_get_dashboards_invalid_env_raises_404(_mock_env):
from src.api.routes.migration import get_dashboards
cm = _make_sync_config_manager([_mock_env])
with pytest.raises(HTTPException) as exc:
await get_dashboards(env_id="wrong-env", config_manager=cm, _=None)
assert exc.value.status_code == 404
# --- execute_migration tests ---
@pytest.mark.asyncio
async def test_execute_migration_success(_mock_env):
from src.api.routes.migration import execute_migration
from src.models.dashboard import DashboardSelection
cm = _make_sync_config_manager([_mock_env, _mock_env]) # Need both source/target
tm = MagicMock()
tm.create_task = AsyncMock(return_value=MagicMock(id="task-123"))
selection = DashboardSelection(
source_env_id="test-env-1",
target_env_id="test-env-1",
selected_ids=[1, 2]
)
result = await execute_migration(selection=selection, config_manager=cm, task_manager=tm, _=None)
assert result["task_id"] == "task-123"
tm.create_task.assert_called_once()
@pytest.mark.asyncio
async def test_execute_migration_invalid_env_raises_400(_mock_env):
from src.api.routes.migration import execute_migration
from src.models.dashboard import DashboardSelection
cm = _make_sync_config_manager([_mock_env])
selection = DashboardSelection(
source_env_id="test-env-1",
target_env_id="non-existent",
selected_ids=[1]
)
with pytest.raises(HTTPException) as exc:
await execute_migration(selection=selection, config_manager=cm, task_manager=MagicMock(), _=None)
assert exc.value.status_code == 400
# [/DEF:backend.src.api.routes.__tests__.test_migration_routes:Module]

View File

@@ -579,6 +579,137 @@ def _resolve_dashboard_id_entity(
# [/DEF:_resolve_dashboard_id_entity:Function]
# [DEF:_get_environment_name_by_id:Function]
# @PURPOSE: Resolve human-readable environment name by id.
# @PRE: environment id may be None.
# @POST: Returns matching environment name or fallback id.
def _get_environment_name_by_id(env_id: Optional[str], config_manager: ConfigManager) -> str:
if not env_id:
return "unknown"
env = next((item for item in config_manager.get_environments() if item.id == env_id), None)
return env.name if env else env_id
# [/DEF:_get_environment_name_by_id:Function]
# [DEF:_extract_result_deep_links:Function]
# @PURPOSE: Build deep-link actions to verify task result from assistant chat.
# @PRE: task object is available.
# @POST: Returns zero or more assistant actions for dashboard open/diff.
def _extract_result_deep_links(task: Any, config_manager: ConfigManager) -> List[AssistantAction]:
plugin_id = getattr(task, "plugin_id", None)
params = getattr(task, "params", {}) or {}
result = getattr(task, "result", {}) or {}
actions: List[AssistantAction] = []
dashboard_id: Optional[int] = None
env_id: Optional[str] = None
if plugin_id == "superset-migration":
migrated = result.get("migrated_dashboards") if isinstance(result, dict) else None
if isinstance(migrated, list) and migrated:
first = migrated[0]
if isinstance(first, dict) and first.get("id") is not None:
dashboard_id = int(first.get("id"))
if dashboard_id is None and isinstance(params.get("selected_ids"), list) and params["selected_ids"]:
dashboard_id = int(params["selected_ids"][0])
env_id = params.get("target_env_id")
elif plugin_id == "superset-backup":
dashboards = result.get("dashboards") if isinstance(result, dict) else None
if isinstance(dashboards, list) and dashboards:
first = dashboards[0]
if isinstance(first, dict) and first.get("id") is not None:
dashboard_id = int(first.get("id"))
if dashboard_id is None and isinstance(params.get("dashboard_ids"), list) and params["dashboard_ids"]:
dashboard_id = int(params["dashboard_ids"][0])
env_id = params.get("environment_id") or _resolve_env_id(result.get("environment"), config_manager)
elif plugin_id == "llm_dashboard_validation":
if params.get("dashboard_id") is not None:
dashboard_id = int(params["dashboard_id"])
env_id = params.get("environment_id")
if dashboard_id is not None and env_id:
env_name = _get_environment_name_by_id(env_id, config_manager)
actions.append(
AssistantAction(
type="open_route",
label=f"Открыть дашборд в {env_name}",
target=f"/dashboards/{dashboard_id}?env_id={env_id}",
)
)
if dashboard_id is not None:
actions.append(
AssistantAction(
type="open_diff",
label="Показать Diff",
target=str(dashboard_id),
)
)
return actions
# [/DEF:_extract_result_deep_links:Function]
# [DEF:_build_task_observability_summary:Function]
# @PURPOSE: Build compact textual summary for completed tasks to reduce "black box" effect.
# @PRE: task may contain plugin-specific result payload.
# @POST: Returns non-empty summary line for known task types or empty string fallback.
def _build_task_observability_summary(task: Any, config_manager: ConfigManager) -> str:
plugin_id = getattr(task, "plugin_id", None)
status = str(getattr(task, "status", "")).upper()
params = getattr(task, "params", {}) or {}
result = getattr(task, "result", {}) or {}
if plugin_id == "superset-migration" and isinstance(result, dict):
migrated = len(result.get("migrated_dashboards") or [])
failed_rows = result.get("failed_dashboards") or []
failed = len(failed_rows)
selected = result.get("selected_dashboards", migrated + failed)
mappings = result.get("mapping_count", 0)
target_env_id = params.get("target_env_id")
target_env_name = _get_environment_name_by_id(target_env_id, config_manager)
warning = ""
if failed_rows:
first = failed_rows[0]
warning = (
f" Внимание: {first.get('title') or first.get('id')}: "
f"{first.get('error') or 'ошибка'}."
)
return (
f"Сводка миграции: выбрано {selected}, перенесено {migrated}, "
f"с ошибками {failed}, маппингов {mappings}, целевая среда {target_env_name}."
f"{warning}"
)
if plugin_id == "superset-backup" and isinstance(result, dict):
total = int(result.get("total_dashboards", 0) or 0)
ok = int(result.get("backed_up_dashboards", 0) or 0)
failed = int(result.get("failed_dashboards", 0) or 0)
env_id = params.get("environment_id") or _resolve_env_id(result.get("environment"), config_manager)
env_name = _get_environment_name_by_id(env_id, config_manager)
failures = result.get("failures") or []
warning = ""
if failures:
first = failures[0]
warning = (
f" Внимание: {first.get('title') or first.get('id')}: "
f"{first.get('error') or 'ошибка'}."
)
return (
f"Сводка бэкапа: среда {env_name}, всего {total}, успешно {ok}, "
f"с ошибками {failed}. {status}.{warning}"
)
if plugin_id == "llm_dashboard_validation" and isinstance(result, dict):
report_status = result.get("status") or status
report_summary = result.get("summary") or "Итог недоступен."
issues = result.get("issues") or []
return f"Сводка валидации: статус {report_status}, проблем {len(issues)}. {report_summary}"
# Fallback for unknown task payloads.
if status in {"SUCCESS", "FAILED"}:
return f"Задача завершена со статусом {status}."
return ""
# [/DEF:_build_task_observability_summary:Function]
# [DEF:_parse_command:Function]
# @PURPOSE: Deterministically parse RU/EN command text into intent payload.
# @PRE: message contains raw user text and config manager resolves environments.
@@ -1146,19 +1277,29 @@ async def _dispatch_intent(
if not recent:
return "У вас пока нет задач в истории.", None, []
task = recent[0]
actions = [AssistantAction(type="open_task", label="Open Task", target=task.id)]
if str(task.status).upper() in {"SUCCESS", "FAILED"}:
actions.extend(_extract_result_deep_links(task, config_manager))
summary_line = _build_task_observability_summary(task, config_manager)
return (
f"Последняя задача: {task.id}, статус: {task.status}.",
f"Последняя задача: {task.id}, статус: {task.status}."
+ (f"\n{summary_line}" if summary_line else ""),
task.id,
[AssistantAction(type="open_task", label="Open Task", target=task.id)],
actions,
)
task = task_manager.get_task(task_id)
if not task:
raise HTTPException(status_code=404, detail=f"Task {task_id} not found")
actions = [AssistantAction(type="open_task", label="Open Task", target=task.id)]
if str(task.status).upper() in {"SUCCESS", "FAILED"}:
actions.extend(_extract_result_deep_links(task, config_manager))
summary_line = _build_task_observability_summary(task, config_manager)
return (
f"Статус задачи {task.id}: {task.status}.",
f"Статус задачи {task.id}: {task.status}."
+ (f"\n{summary_line}" if summary_line else ""),
task.id,
[AssistantAction(type="open_task", label="Open Task", target=task.id)],
actions,
)
if operation == "create_branch":
@@ -1240,6 +1381,18 @@ async def _dispatch_intent(
[
AssistantAction(type="open_task", label="Open Task", target=task.id),
AssistantAction(type="open_reports", label="Open Reports", target="/reports"),
*(
[
AssistantAction(
type="open_route",
label=f"Открыть дашборд в {_get_environment_name_by_id(tgt, config_manager)}",
target=f"/dashboards/{dashboard_id}?env_id={tgt}",
),
AssistantAction(type="open_diff", label="Показать Diff", target=str(dashboard_id)),
]
if dashboard_id
else []
),
],
)
@@ -1268,6 +1421,18 @@ async def _dispatch_intent(
[
AssistantAction(type="open_task", label="Open Task", target=task.id),
AssistantAction(type="open_reports", label="Open Reports", target="/reports"),
*(
[
AssistantAction(
type="open_route",
label=f"Открыть дашборд в {_get_environment_name_by_id(env_id, config_manager)}",
target=f"/dashboards/{dashboard_id}?env_id={env_id}",
),
AssistantAction(type="open_diff", label="Показать Diff", target=str(dashboard_id)),
]
if entities.get("dashboard_id") or entities.get("dashboard_ref")
else []
),
],
)

View File

@@ -0,0 +1,725 @@
{
"dashboard": {
"result": {
"certification_details": null,
"certified_by": null,
"changed_by": {
"first_name": "Superset",
"id": 1,
"last_name": "Admin"
},
"changed_by_name": "Superset Admin",
"changed_on": "2026-02-10T13:39:35.945662",
"changed_on_delta_humanized": "15 days ago",
"charts": [
"TA-0001-001 test_chart"
],
"created_by": {
"first_name": "Superset",
"id": 1,
"last_name": "Admin"
},
"created_on_delta_humanized": "15 days ago",
"css": null,
"dashboard_title": "TA-0001 Test dashboard",
"id": 13,
"is_managed_externally": false,
"json_metadata": "{\"color_scheme_domain\": [], \"shared_label_colors\": [], \"map_label_colors\": {}, \"label_colors\": {}}",
"owners": [
{
"first_name": "Superset",
"id": 1,
"last_name": "Admin"
}
],
"position_json": null,
"published": true,
"roles": [],
"slug": null,
"tags": [],
"theme": null,
"thumbnail_url": "/api/v1/dashboard/13/thumbnail/3cfc57e6aea7188b139f94fb437a1426/",
"url": "/superset/dashboard/13/",
"uuid": "124b28d4-d54a-4ade-ade7-2d0473b90686"
}
},
"dataset": {
"id": 26,
"result": {
"always_filter_main_dttm": false,
"cache_timeout": null,
"catalog": "examples",
"changed_by": {
"first_name": "Superset",
"last_name": "Admin"
},
"changed_on": "2026-02-10T13:38:26.175551",
"changed_on_humanized": "15 days ago",
"column_formats": {},
"columns": [
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158196",
"column_name": "color",
"created_on": "2026-02-10T13:38:26.158189",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 772,
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "STRING",
"type_generic": 1,
"uuid": "4fa810ee-99cc-4d1f-8c0d-0f289c3b01f4",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158249",
"column_name": "deleted",
"created_on": "2026-02-10T13:38:26.158245",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 773,
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "ebc07e82-7250-4eef-8d13-ea61561fa52c",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158289",
"column_name": "has_2fa",
"created_on": "2026-02-10T13:38:26.158285",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 774,
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "08e72f4d-3ced-4d9a-9f7d-2f85291ce88b",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158328",
"column_name": "id",
"created_on": "2026-02-10T13:38:26.158324",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 775,
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "STRING",
"type_generic": 1,
"uuid": "fd11955c-0130-4ea1-b3c0-d8b159971789",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158366",
"column_name": "is_admin",
"created_on": "2026-02-10T13:38:26.158362",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 776,
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "13a6c8e1-c9f8-4f08-aa62-05bca7be547b",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158404",
"column_name": "is_app_user",
"created_on": "2026-02-10T13:38:26.158400",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 777,
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "6321ba8a-28d7-4d68-a6b3-5cef6cd681a2",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158442",
"column_name": "is_bot",
"created_on": "2026-02-10T13:38:26.158438",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 778,
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "f3ded50e-b1a2-4a88-b805-781d5923e062",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158480",
"column_name": "is_owner",
"created_on": "2026-02-10T13:38:26.158477",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 779,
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "8a1408eb-050d-4455-878c-22342df5da3d",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158532",
"column_name": "is_primary_owner",
"created_on": "2026-02-10T13:38:26.158528",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 780,
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "054b8c16-82fd-480c-82e0-a0975229673a",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158583",
"column_name": "is_restricted",
"created_on": "2026-02-10T13:38:26.158579",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 781,
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "6932c25f-0273-4595-85c1-29422a801ded",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158621",
"column_name": "is_ultra_restricted",
"created_on": "2026-02-10T13:38:26.158618",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 782,
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "9b14e5f9-3ab4-498e-b1e3-bbf49e9d61fe",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158660",
"column_name": "name",
"created_on": "2026-02-10T13:38:26.158656",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 783,
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "STRING",
"type_generic": 1,
"uuid": "ebee8249-0e10-4157-8a8e-96ae107887a3",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158697",
"column_name": "real_name",
"created_on": "2026-02-10T13:38:26.158694",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 784,
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "STRING",
"type_generic": 1,
"uuid": "553517a0-fe05-4ff5-a4eb-e9d2165d6f64",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158735",
"column_name": "team_id",
"created_on": "2026-02-10T13:38:26.158731",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 785,
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "STRING",
"type_generic": 1,
"uuid": "6c207fac-424d-465c-b80a-306b42b55ce8",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158773",
"column_name": "tz",
"created_on": "2026-02-10T13:38:26.158769",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 786,
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "STRING",
"type_generic": 1,
"uuid": "6efcc042-0b78-4362-9373-2f684077d574",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158824",
"column_name": "tz_label",
"created_on": "2026-02-10T13:38:26.158820",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 787,
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "STRING",
"type_generic": 1,
"uuid": "c6a6ac40-5c60-472d-a878-4b65b8460ccc",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158861",
"column_name": "tz_offset",
"created_on": "2026-02-10T13:38:26.158857",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 788,
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "LONGINTEGER",
"type_generic": 0,
"uuid": "cf6da93a-bba9-47df-9154-6cfd0c9922fc",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158913",
"column_name": "updated",
"created_on": "2026-02-10T13:38:26.158909",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 789,
"is_active": true,
"is_dttm": true,
"python_date_format": null,
"type": "DATETIME",
"type_generic": 2,
"uuid": "2aa0a72a-5602-4799-b5ab-f22000108d62",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-10T13:38:26.158967",
"column_name": "channel_name",
"created_on": "2026-02-10T13:38:26.158963",
"description": null,
"expression": null,
"extra": null,
"filterable": true,
"groupby": true,
"id": 790,
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "STRING",
"type_generic": 1,
"uuid": "a84bd658-c83c-4e7f-9e1b-192595092d9b",
"verbose_name": null
}
],
"created_by": {
"first_name": "Superset",
"last_name": "Admin"
},
"created_on": "2026-02-10T13:38:26.050436",
"created_on_humanized": "15 days ago",
"database": {
"allow_multi_catalog": false,
"backend": "postgresql",
"database_name": "examples",
"id": 1,
"uuid": "a2dc77af-e654-49bb-b321-40f6b559a1ee"
},
"datasource_name": "test_join_select",
"datasource_type": "table",
"default_endpoint": null,
"description": null,
"extra": null,
"fetch_values_predicate": null,
"filter_select_enabled": true,
"granularity_sqla": [
[
"updated",
"updated"
]
],
"id": 26,
"is_managed_externally": false,
"is_sqllab_view": false,
"kind": "virtual",
"main_dttm_col": "updated",
"metrics": [
{
"changed_on": "2026-02-10T13:38:26.182269",
"created_on": "2026-02-10T13:38:26.182264",
"currency": null,
"d3format": null,
"description": null,
"expression": "COUNT(*)",
"extra": null,
"id": 33,
"metric_name": "count",
"metric_type": "count",
"uuid": "7510f8ca-05ee-4a37-bec1-4a5d7bf2ac50",
"verbose_name": "COUNT(*)",
"warning_text": null
}
],
"name": "public.test_join_select",
"normalize_columns": false,
"offset": 0,
"order_by_choices": [
[
"[\"channel_name\", true]",
"channel_name [asc]"
],
[
"[\"channel_name\", false]",
"channel_name [desc]"
],
[
"[\"color\", true]",
"color [asc]"
],
[
"[\"color\", false]",
"color [desc]"
],
[
"[\"deleted\", true]",
"deleted [asc]"
],
[
"[\"deleted\", false]",
"deleted [desc]"
],
[
"[\"has_2fa\", true]",
"has_2fa [asc]"
],
[
"[\"has_2fa\", false]",
"has_2fa [desc]"
],
[
"[\"id\", true]",
"id [asc]"
],
[
"[\"id\", false]",
"id [desc]"
],
[
"[\"is_admin\", true]",
"is_admin [asc]"
],
[
"[\"is_admin\", false]",
"is_admin [desc]"
],
[
"[\"is_app_user\", true]",
"is_app_user [asc]"
],
[
"[\"is_app_user\", false]",
"is_app_user [desc]"
],
[
"[\"is_bot\", true]",
"is_bot [asc]"
],
[
"[\"is_bot\", false]",
"is_bot [desc]"
],
[
"[\"is_owner\", true]",
"is_owner [asc]"
],
[
"[\"is_owner\", false]",
"is_owner [desc]"
],
[
"[\"is_primary_owner\", true]",
"is_primary_owner [asc]"
],
[
"[\"is_primary_owner\", false]",
"is_primary_owner [desc]"
],
[
"[\"is_restricted\", true]",
"is_restricted [asc]"
],
[
"[\"is_restricted\", false]",
"is_restricted [desc]"
],
[
"[\"is_ultra_restricted\", true]",
"is_ultra_restricted [asc]"
],
[
"[\"is_ultra_restricted\", false]",
"is_ultra_restricted [desc]"
],
[
"[\"name\", true]",
"name [asc]"
],
[
"[\"name\", false]",
"name [desc]"
],
[
"[\"real_name\", true]",
"real_name [asc]"
],
[
"[\"real_name\", false]",
"real_name [desc]"
],
[
"[\"team_id\", true]",
"team_id [asc]"
],
[
"[\"team_id\", false]",
"team_id [desc]"
],
[
"[\"tz\", true]",
"tz [asc]"
],
[
"[\"tz\", false]",
"tz [desc]"
],
[
"[\"tz_label\", true]",
"tz_label [asc]"
],
[
"[\"tz_label\", false]",
"tz_label [desc]"
],
[
"[\"tz_offset\", true]",
"tz_offset [asc]"
],
[
"[\"tz_offset\", false]",
"tz_offset [desc]"
],
[
"[\"updated\", true]",
"updated [asc]"
],
[
"[\"updated\", false]",
"updated [desc]"
]
],
"owners": [
{
"first_name": "Superset",
"id": 1,
"last_name": "Admin"
}
],
"schema": "public",
"select_star": "SELECT\n *\nFROM public.test_join_select\nLIMIT 100",
"sql": "SELECT t_u.*,\nt_c.name as channel_name\nfrom public.users t_u \njoin public.users_channels t_c ON\nt_c.user_id = t_u.id",
"table_name": "test_join_select",
"template_params": null,
"time_grain_sqla": [
[
"PT1S",
"Second"
],
[
"PT5S",
"5 second"
],
[
"PT30S",
"30 second"
],
[
"PT1M",
"Minute"
],
[
"PT5M",
"5 minute"
],
[
"PT10M",
"10 minute"
],
[
"PT15M",
"15 minute"
],
[
"PT30M",
"30 minute"
],
[
"PT1H",
"Hour"
],
[
"P1D",
"Day"
],
[
"P1W",
"Week"
],
[
"P1M",
"Month"
],
[
"P3M",
"Quarter"
],
[
"P1Y",
"Year"
]
],
"uid": "26__table",
"url": "/tablemodelview/edit/26",
"uuid": "e6f56489-6040-4720-8393-ddfc5c4c5574",
"verbose_map": {
"__timestamp": "Time",
"channel_name": "channel_name",
"color": "color",
"count": "COUNT(*)",
"deleted": "deleted",
"has_2fa": "has_2fa",
"id": "id",
"is_admin": "is_admin",
"is_app_user": "is_app_user",
"is_bot": "is_bot",
"is_owner": "is_owner",
"is_primary_owner": "is_primary_owner",
"is_restricted": "is_restricted",
"is_ultra_restricted": "is_ultra_restricted",
"name": "name",
"real_name": "real_name",
"team_id": "team_id",
"tz": "tz",
"tz_label": "tz_label",
"tz_offset": "tz_offset",
"updated": "updated"
}
}
}
}

View File

@@ -121,9 +121,11 @@ class TestReportsServiceList:
_make_task(task_id="t2", status_value="FAILED"),
]
svc = self._make_service(tasks)
# Order: FAILED (desc) -> SUCCESS (asc) if it's alphanumeric
# status values are 'SUCCESS', 'FAILED'. 'FAILED' < 'SUCCESS'
result = svc.list_reports(ReportQuery(sort_by="status", sort_order="asc"))
statuses = [item.status.value for item in result.items]
assert statuses == sorted(statuses)
assert result.items[0].status.value == "failed"
assert result.items[1].status.value == "success"
def test_applied_filters_echoed(self):
from src.models.report import ReportQuery