Files
egutierrez 7913116a8e chore: auto-commit (129 archivos)
- .claude/agents/fn-analizador/SKILL.md
- .claude/agents/fn-constructor/SKILL.md
- .claude/agents/fn-executor/SKILL.md
- .claude/agents/fn-mejorador/SKILL.md
- .claude/agents/fn-orquestador/SKILL.md
- .claude/agents/fn-recopilador/SKILL.md
- .claude/commands/app.md
- .claude/commands/compile.md
- .claude/commands/cpp-app.md
- .claude/commands/create_functions.md
- ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 22:23:12 +02:00

391 lines
13 KiB
Python

"""Tests para metabase_validate_card_payload y metabase_validate_dashboard_payload."""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from metabase.validation import (
metabase_validate_card_payload,
metabase_validate_dashboard_payload,
)
# ---------------------------------------------------------------------------
# metabase_validate_card_payload
# ---------------------------------------------------------------------------
def _base_card() -> dict:
return {
"name": "Revenue by Month",
"display": "line",
"dataset_query": {
"database": 1,
"type": "native",
"native": {"query": "SELECT 1"},
},
}
def test_card_valido_retorna_lista_vacia():
issues = metabase_validate_card_payload(_base_card())
assert issues == [], f"Se esperaba lista vacia, got: {issues}"
def test_card_display_invalido():
payload = _base_card()
payload["display"] = "foobar"
issues = metabase_validate_card_payload(payload)
assert any("display" in i and "foobar" in i for i in issues), issues
def test_card_display_ausente():
payload = _base_card()
del payload["display"]
issues = metabase_validate_card_payload(payload)
assert any("display" in i and "ausente" in i for i in issues), issues
def test_card_name_ausente():
payload = _base_card()
del payload["name"]
issues = metabase_validate_card_payload(payload)
assert any("name" in i and "ausente" in i for i in issues), issues
def test_card_name_vacio():
payload = _base_card()
payload["name"] = " "
issues = metabase_validate_card_payload(payload)
assert any("name" in i for i in issues), issues
def test_card_dataset_query_ausente():
payload = _base_card()
del payload["dataset_query"]
issues = metabase_validate_card_payload(payload)
assert any("dataset_query" in i and "ausente" in i for i in issues), issues
def test_card_dataset_query_sin_database():
payload = _base_card()
del payload["dataset_query"]["database"]
issues = metabase_validate_card_payload(payload)
assert any("database" in i for i in issues), issues
def test_card_nativa_sin_sql():
payload = _base_card()
payload["dataset_query"]["native"]["query"] = ""
issues = metabase_validate_card_payload(payload)
assert any("SQL" in i or "native" in i for i in issues), issues
def test_card_nativa_mbql5():
"""Acepta SQL en stages[0].native (formato MBQL5)."""
payload = {
"name": "MBQL5 card",
"display": "table",
"dataset_query": {
"database": 2,
"stages": [{"native": "SELECT 1"}],
},
}
issues = metabase_validate_card_payload(payload)
assert issues == [], f"Se esperaba lista vacia, got: {issues}"
def test_card_type_invalido():
payload = _base_card()
payload["type"] = "unknown_type"
issues = metabase_validate_card_payload(payload)
assert any("type" in i and "unknown_type" in i for i in issues), issues
def test_card_type_valido():
for t in ("question", "model", "metric"):
payload = _base_card()
payload["type"] = t
issues = metabase_validate_card_payload(payload)
assert issues == [], f"type={t!r} no deberia dar issues: {issues}"
def test_card_visualization_settings_no_dict():
payload = _base_card()
payload["visualization_settings"] = "no es un dict"
issues = metabase_validate_card_payload(payload)
assert any("visualization_settings" in i for i in issues), issues
def test_card_parameters_no_list():
payload = _base_card()
payload["parameters"] = {"not": "a list"}
issues = metabase_validate_card_payload(payload)
assert any("parameters" in i for i in issues), issues
def test_card_archived_no_bool():
payload = _base_card()
payload["archived"] = "yes"
issues = metabase_validate_card_payload(payload)
assert any("archived" in i for i in issues), issues
def test_card_acumula_multiples_errores():
"""Un payload con varios errores debe retornar todos los issues, no solo el primero."""
payload = {
"display": "invalid_display",
"dataset_query": "not a dict",
"archived": "yes",
}
issues = metabase_validate_card_payload(payload)
assert len(issues) >= 3, f"Se esperaban >= 3 issues, got {len(issues)}: {issues}"
# ---------------------------------------------------------------------------
# metabase_validate_dashboard_payload
# ---------------------------------------------------------------------------
def _base_dashboard() -> dict:
return {"name": "My Dashboard"}
def test_dashboard_valido_sin_dashcards():
issues = metabase_validate_dashboard_payload(_base_dashboard(), known_card_ids=set())
assert issues == [], issues
def test_dashboard_valido_con_dashcards():
payload = {
"name": "KPIs",
"dashcards": [
{"card_id": 1, "row": 0, "col": 0, "size_x": 6, "size_y": 4},
{"card_id": 2, "row": 0, "col": 6, "size_x": 6, "size_y": 4},
],
}
issues = metabase_validate_dashboard_payload(payload, known_card_ids={1, 2})
assert issues == [], issues
def test_dashboard_card_id_desconocido():
payload = {
"name": "Dashboard",
"dashcards": [{"card_id": 999, "row": 0, "col": 0, "size_x": 6, "size_y": 4}],
}
issues = metabase_validate_dashboard_payload(payload, known_card_ids={1, 2})
assert any("999" in i for i in issues), issues
def test_dashboard_card_virtual_null_permitido():
"""card_id null = dashcard virtual (texto/heading), siempre permitido."""
payload = {
"name": "Dashboard",
"dashcards": [{"card_id": None, "row": 0, "col": 0, "size_x": 6, "size_y": 2}],
}
issues = metabase_validate_dashboard_payload(payload, known_card_ids=set())
assert issues == [], issues
def test_dashboard_dashcards_solapadas():
payload = {
"name": "Dashboard",
"dashcards": [
{"card_id": 1, "row": 0, "col": 0, "size_x": 6, "size_y": 4},
{"card_id": 2, "row": 2, "col": 2, "size_x": 4, "size_y": 4},
],
}
issues = metabase_validate_dashboard_payload(payload, known_card_ids={1, 2})
assert any("solapan" in i for i in issues), issues
def test_dashboard_dashcards_adyacentes_no_solapan():
"""Dos dashcards que se tocan en el borde NO solapan."""
payload = {
"name": "Dashboard",
"dashcards": [
{"card_id": 1, "row": 0, "col": 0, "size_x": 6, "size_y": 4},
{"card_id": 2, "row": 0, "col": 6, "size_x": 6, "size_y": 4},
],
}
issues = metabase_validate_dashboard_payload(payload, known_card_ids={1, 2})
assert not any("solapan" in i for i in issues), issues
def test_dashboard_col_fuera_de_bounds():
payload = {
"name": "Dashboard",
"dashcards": [{"card_id": 1, "row": 0, "col": 25, "size_x": 1, "size_y": 1}],
}
issues = metabase_validate_dashboard_payload(payload, known_card_ids={1})
assert any("col" in i for i in issues), issues
def test_dashboard_col_mas_size_x_excede_grid():
payload = {
"name": "Dashboard",
"dashcards": [{"card_id": 1, "row": 0, "col": 20, "size_x": 6, "size_y": 4}],
}
issues = metabase_validate_dashboard_payload(payload, known_card_ids={1})
assert any("24" in i for i in issues), issues
def test_dashboard_size_y_fuera_de_bounds():
payload = {
"name": "Dashboard",
"dashcards": [{"card_id": 1, "row": 0, "col": 0, "size_x": 6, "size_y": 0}],
}
issues = metabase_validate_dashboard_payload(payload, known_card_ids={1})
assert any("size_y" in i for i in issues), issues
def test_dashboard_name_ausente():
issues = metabase_validate_dashboard_payload({}, known_card_ids=set())
assert any("name" in i and "ausente" in i for i in issues), issues
def test_dashboard_tabs_invalidos():
payload = {"name": "Dashboard", "tabs": [{"name": "Tab1"}]} # falta id
issues = metabase_validate_dashboard_payload(payload, known_card_ids=set())
assert any("id" in i for i in issues), issues
def test_dashboard_parameters_no_list():
payload = {"name": "Dashboard", "parameters": "not a list"}
issues = metabase_validate_dashboard_payload(payload, known_card_ids=set())
assert any("parameters" in i for i in issues), issues
# ---------------------------------------------------------------------------
# metabase_validate_document_payload
# ---------------------------------------------------------------------------
from metabase.validation import metabase_validate_document_payload
def _base_doc(content):
return {"name": "Notas", "document": {"type": "doc", "content": content}}
def test_document_valido_minimo():
issues = metabase_validate_document_payload(_base_doc([
{"type": "paragraph", "content": [{"type": "text", "text": "hola"}]}
]))
assert issues == [], issues
def test_document_name_ausente():
issues = metabase_validate_document_payload({"document": {"type": "doc", "content": []}})
assert any("name" in i and "ausente" in i for i in issues), issues
def test_document_nodo_desconocido_callout():
issues = metabase_validate_document_payload(_base_doc([
{"type": "callout", "content": [{"type": "paragraph", "content": [{"type": "text", "text": "x"}]}]}
]))
assert any("callout" in i and "no soportado" in i for i in issues), issues
def test_document_nodo_desconocido_taskList():
issues = metabase_validate_document_payload(_base_doc([
{"type": "taskList", "content": []}
]))
assert any("taskList" in i for i in issues), issues
def test_document_mark_desconocido_underline():
issues = metabase_validate_document_payload(_base_doc([
{"type": "paragraph", "content": [
{"type": "text", "marks": [{"type": "underline"}], "text": "x"}
]}
]))
assert any("underline" in i and "no soportado" in i for i in issues), issues
def test_document_heading_level_invalido():
issues = metabase_validate_document_payload(_base_doc([
{"type": "heading", "attrs": {"level": 9}, "content": [{"type": "text", "text": "x"}]}
]))
assert any("level" in i for i in issues), issues
def test_document_cardEmbed_sin_id_ni_slug():
issues = metabase_validate_document_payload(_base_doc([
{"type": "cardEmbed", "attrs": {}}
]))
assert any("cardEmbed" in i and "id" in i for i in issues), issues
def test_document_cardEmbed_con_id_valido():
issues = metabase_validate_document_payload(_base_doc([
{"type": "cardEmbed", "attrs": {"id": 42}}
]))
assert issues == [], issues
def test_document_cardEmbed_slug_desconocido():
issues = metabase_validate_document_payload(
_base_doc([{"type": "cardEmbed", "attrs": {"card": "inventado"}}]),
known_card_slugs={"real_one", "real_two"},
)
assert any("inventado" in i for i in issues), issues
def test_document_flexContainer_demasiados_hijos():
children = [{"type": "cardEmbed", "attrs": {"id": 1}} for _ in range(4)]
issues = metabase_validate_document_payload(_base_doc([
{"type": "flexContainer", "attrs": {"columnWidths": [25, 25, 25, 25]}, "content": children}
]))
assert any("1-3" in i for i in issues), issues
def test_document_flexContainer_hijo_invalido():
issues = metabase_validate_document_payload(_base_doc([
{"type": "flexContainer", "attrs": {"columnWidths": [100]}, "content": [
{"type": "paragraph", "content": [{"type": "text", "text": "x"}]}
]}
]))
assert any("flexContainer solo acepta" in i for i in issues), issues
def test_document_flexContainer_columnWidths_mismatch():
issues = metabase_validate_document_payload(_base_doc([
{"type": "flexContainer", "attrs": {"columnWidths": [50, 50]}, "content": [
{"type": "cardEmbed", "attrs": {"id": 1}}
]}
]))
assert any("columnWidths" in i for i in issues), issues
def test_document_vacio_string_es_valido():
issues = metabase_validate_document_payload({"name": "vacio", "document": ""})
assert issues == [], issues
def test_document_kitchen_sink_valido():
"""Simula el kitchen_sink real y debe pasar sin issues."""
content = [
{"type": "heading", "attrs": {"level": 1}, "content": [{"type": "text", "text": "T"}]},
{"type": "paragraph", "content": [
{"type": "text", "text": "a "},
{"type": "text", "marks": [{"type": "bold"}], "text": "b"},
{"type": "text", "marks": [{"type": "link", "attrs": {"href": "https://x"}}], "text": "l"},
]},
{"type": "bulletList", "content": [
{"type": "listItem", "content": [
{"type": "paragraph", "content": [{"type": "text", "text": "i"}]}
]}
]},
{"type": "blockquote", "content": [
{"type": "paragraph", "content": [{"type": "text", "text": "q"}]}
]},
{"type": "codeBlock", "attrs": {"language": "py"}, "content": [{"type": "text", "text": "x=1"}]},
{"type": "horizontalRule"},
{"type": "cardEmbed", "attrs": {"id": 1}},
{"type": "flexContainer", "attrs": {"columnWidths": [50, 50]}, "content": [
{"type": "cardEmbed", "attrs": {"id": 1}},
{"type": "cardEmbed", "attrs": {"id": 2}},
]},
]
issues = metabase_validate_document_payload(_base_doc(content))
assert issues == [], issues