Files
graph_explorer/tests/test_extract_links.py
egutierrez 7a94160fd2 feat: catch-up de decisiones previas (Webpage→Url, anti-bot, UI 2-col, tests cross-platform)
Bloque de cambios revisados y validados con el usuario en sesiones
previas que no habian aterrizado en commits propios. Lista por tema:

* enrichers: web_search ahora usa lite.duckduckgo.com como endpoint
  primario (mas tolerante con bot detection desde IP residencial),
  con fallback al endpoint html. Detecta pagina captcha y emite
  error claro si ambos fallan. Anyade _DDGLiteParser para el formato
  lite + auto-pick de parser por contenido.

* enrichers: tipo Webpage unificado en Url (campos de cuerpo
  cacheado viven en metadata del Url). Manifests actualizados
  (applies_to: [Url]). fetch_webpage ya no convierte Url->Webpage.

* enrichers/manifest: campo `params` parseado a EnricherSpec.params
  (name, type, default_value, description). UI puede renderizar
  dialog de configuracion.

* jobs: fix de path conversion para Python embebido nativo Windows
  (no convertir a /mnt/c/... cuando el subproceso es Windows-native;
  solo cuando es bash o python via WSL).

* main.cpp: ventana ImGui (no modal) "Run enricher" con layout
  2-col (label izq, input der). Inserta job con JSON tipado. Layout
  clustering apretado: hijos del mismo anchor en un solo anillo
  alrededor del padre, sin desperdigar por anillos crecientes.

* views: inspector con layout 2-col via BeginTable (Identity,
  Schema fields, Extras). Description full-width debajo de su label.

* tests: portable conftest (auto-detecta REGISTRY_ROOT, PYTHON_BIN,
  ENRICHERS_DIR para WSL y Windows portable). _runner.py trampoline
  inyecta stub via sys.path porque embedded Python ignora PYTHONPATH.
  Tests bash-only (vendor_script, freeze, dispatcher bash, resolver
  Linux-binary) skipean en Windows. Tests existentes adaptados a
  Webpage->Url.

Resultado actual: 32 passed WSL, 21 passed + 11 skipped Windows.
2026-05-03 14:41:28 +02:00

64 lines
2.3 KiB
Python

"""Tests del enricher extract_links — sin red, lee markdown del cache."""
from __future__ import annotations
from pathlib import Path
from conftest import (
base_ctx, list_entities, list_relations, make_node, run_enricher,
)
SAMPLE_MD = """# Pagina demo
Aqui hay [un enlace](https://example.com/articulo) interesante y
otro [duplicado](https://example.com/articulo) que no debe contar
dos veces.
Tambien una URL pelada: https://otra.example/path?q=1
y https://tercera.example/
Y un email que NO debe extraer como Url: contact@no.example
"""
def test_extract_links_creates_url_nodes(ops_db, app_dir, registry_root):
# 1) Crear el cache con el markdown.
md_dir = Path(app_dir) / "cache" / "ab"
md_dir.mkdir(parents=True, exist_ok=True)
md_path = md_dir / "abc.md"
md_path.write_text(SAMPLE_MD, encoding="utf-8")
rel = md_path.relative_to(app_dir)
# 2) Crear Webpage con metadata.markdown_path apuntando al cache.
make_node(ops_db, node_id="w1", name="demo",
type_ref="Url", metadata={"markdown_path": str(rel)})
ctx = base_ctx(ops_db=ops_db, app_dir=app_dir, registry_root=registry_root,
node_id="w1", node_name="demo", node_type="Url",
metadata={"markdown_path": str(rel)})
rc, out, err = run_enricher("extract_links", ctx)
assert rc == 0, err
assert out is not None, err
assert out["entities_added"] >= 3, out
urls = [e["name"] for e in list_entities(ops_db, type_ref="Url")]
assert "https://example.com/articulo" in urls
assert "https://otra.example/path?q=1" in urls
rels = list_relations(ops_db, name="LINKS_TO")
assert len(rels) >= 3
assert all(r["from_entity"] == "w1" for r in rels)
def test_extract_links_without_markdown_path_errors(ops_db, app_dir,
registry_root):
make_node(ops_db, node_id="w1", name="demo",
type_ref="Url", metadata={})
ctx = base_ctx(ops_db=ops_db, app_dir=app_dir, registry_root=registry_root,
node_id="w1", node_name="demo", node_type="Url")
rc, out, err = run_enricher("extract_links", ctx)
assert rc != 0, "deberia fallar sin markdown_path"
assert out is not None
assert "missing markdown_path" in (out.get("error") or "")