"""Tests del visual heredado del Group (issue 0035e). El binario C++ implementa `apply_group_inherited_visuals` en data.cpp: para cada nodo Group del grafo, consulta `SELECT DISTINCT type_ref FROM entities WHERE group_id = ? AND type_ref != 'Group'`. Si la familia es homogenea (un solo tipo), reasigna el `type_id` del nodo Group al de ese tipo y fija `shape_override = SHAPE_SQUARE`. Si es heterogenea o vacia, conserva el visual generico. El subcomando `gx-cli group visual ` espejea exactamente esa SQL, asi estos tests verifican el contrato (homogeneo vs heterogeneo, type heredado y shape=square preservado) sin depender del binario. """ from __future__ import annotations import sqlite3 from test_gx_cli import OPS_SCHEMA, APP_SCHEMA, env_dirs, run_gx # noqa: F401 def _seed_group_with_children(ops_db, group_id: str, child_specs: list[tuple[str, str]]): """Inserta el contenedor Group + cada hijo (id, type_ref). `child_specs` = [(child_id, type_ref), ...]. Se anaden con group_id apuntando al contenedor. """ cn = sqlite3.connect(ops_db) try: cn.execute( "INSERT INTO entities(id, name, type_ref, status, source, " " metadata, created_at, updated_at) " "VALUES (?, ?, 'Group', 'active', 'manual', '{}', " " '2026-05-04T10:00:00.000Z', '2026-05-04T10:00:00.000Z')", (group_id, "test-group"), ) for i, (cid, type_ref) in enumerate(child_specs): cn.execute( "INSERT INTO entities(id, name, type_ref, status, source, " " metadata, group_id, " " created_at, updated_at) " "VALUES (?, ?, ?, 'active', 'manual', '{}', ?, ?, ?)", (cid, f"name-{i}", type_ref, group_id, f"2026-05-04T11:{i:02d}:00.000Z", f"2026-05-04T11:{i:02d}:00.000Z"), ) cn.commit() finally: cn.close() def test_group_inherits_visual_from_homogeneous_children(env_dirs): """5 Urls como hijos -> visual heredado a 'Url' (homogeneo).""" children = [(f"u_{i:02d}", "Url") for i in range(5)] _seed_group_with_children(env_dirs["ops"], "G_homogeneous", children) out = run_gx(env_dirs, "group", "visual", "G_homogeneous") assert out["homogeneous"] is True, out assert out["inherited"] == "Url", out assert out["child_types"] == ["Url"], out # La forma siempre se queda como square — distintivo de contenedor. assert out["shape"] == "square", out def test_group_falls_back_to_generic_for_heterogeneous(env_dirs): """Url + Email en el mismo Group -> visual generico Group.""" children = [ ("u_00", "Url"), ("u_01", "Url"), ("u_02", "Url"), ("e_00", "Email"), ("e_01", "Email"), ] _seed_group_with_children(env_dirs["ops"], "G_heterogeneous", children) out = run_gx(env_dirs, "group", "visual", "G_heterogeneous") assert out["homogeneous"] is False, out assert out["inherited"] == "Group", out # child_types ordenado alfabeticamente — verifica ambos presentes. assert out["child_types"] == ["Email", "Url"], out assert out["shape"] == "square", out def test_group_with_no_children_falls_back_to_generic(env_dirs): """Group vacio (sin hijos con group_id apuntando a el) -> generico.""" _seed_group_with_children(env_dirs["ops"], "G_empty", []) out = run_gx(env_dirs, "group", "visual", "G_empty") assert out["homogeneous"] is False, out assert out["inherited"] == "Group", out assert out["child_types"] == [], out def test_group_visual_ignores_nested_subgroups(env_dirs): """Subgrupos anidados (type_ref='Group') no cuentan — siguen scope fase 1.""" cn = sqlite3.connect(env_dirs["ops"]) try: cn.execute( "INSERT INTO entities(id, name, type_ref, status, source, " " metadata, created_at, updated_at) " "VALUES ('G_outer', 'outer', 'Group', 'active', 'manual', '{}', " " '2026-05-04T10:00:00.000Z', '2026-05-04T10:00:00.000Z')" ) for i in range(3): cn.execute( "INSERT INTO entities(id, name, type_ref, status, source, " " metadata, group_id, " " created_at, updated_at) " "VALUES (?, ?, 'Url', 'active', 'manual', '{}', 'G_outer', " " '2026-05-04T11:00:00.000Z', '2026-05-04T11:00:00.000Z')", (f"u_{i}", f"url-{i}"), ) # Subgrupo anidado — el resolver lo excluye via type_ref != 'Group'. cn.execute( "INSERT INTO entities(id, name, type_ref, status, source, " " metadata, group_id, " " created_at, updated_at) " "VALUES ('G_nested', 'nested', 'Group', 'active', 'manual', " " '{}', 'G_outer', " " '2026-05-04T11:00:00.000Z', '2026-05-04T11:00:00.000Z')" ) cn.commit() finally: cn.close() out = run_gx(env_dirs, "group", "visual", "G_outer") assert out["homogeneous"] is True, out assert out["inherited"] == "Url", out