context-map-ai 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- context_map/__init__.py +1 -0
- context_map/__tests__/conftest.py +33 -0
- context_map/__tests__/smoke.py +23 -0
- context_map/__tests__/test_checker_frescura.py +122 -0
- context_map/__tests__/test_cli.py +134 -0
- context_map/__tests__/test_conexiones.py +675 -0
- context_map/__tests__/test_cyclomatic.py +44 -0
- context_map/__tests__/test_diario_consolidado.py +76 -0
- context_map/__tests__/test_doctor.py +43 -0
- context_map/__tests__/test_ecosistema.py +448 -0
- context_map/__tests__/test_enterprise_features.py +338 -0
- context_map/__tests__/test_export.py +43 -0
- context_map/__tests__/test_hermes_clasificacion.py +73 -0
- context_map/__tests__/test_hook_memoria_viva.py +45 -0
- context_map/__tests__/test_hooks.py +35 -0
- context_map/__tests__/test_ingest.py +155 -0
- context_map/__tests__/test_mermaid.py +59 -0
- context_map/__tests__/test_metrica_memoria_viva.py +87 -0
- context_map/__tests__/test_parsing.py +65 -0
- context_map/__tests__/test_personal.py +489 -0
- context_map/__tests__/test_refresh_temporales.py +34 -0
- context_map/__tests__/test_reglas_catalogo.py +146 -0
- context_map/__tests__/test_scan_cache.py +30 -0
- context_map/__tests__/test_scanner.py +68 -0
- context_map/__tests__/test_security_scanner.py +40 -0
- context_map/__tests__/test_standardize.py +52 -0
- context_map/__tests__/test_tokenization.py +115 -0
- context_map/__tests__/test_topologia_arbol.py +307 -0
- context_map/__tests__/test_vault_consolidation.py +441 -0
- context_map/__tests__/test_watcher.py +29 -0
- context_map/__tests__/test_wrap.py +79 -0
- context_map/application/__init__.py +10 -0
- context_map/application/cli/__init__.py +8 -0
- context_map/application/cli/cli.py +139 -0
- context_map/application/cli/parser.py +204 -0
- context_map/application/commands/__init__.py +60 -0
- context_map/application/commands/_helpers.py +348 -0
- context_map/application/commands/adapt.py +85 -0
- context_map/application/commands/auto.py +56 -0
- context_map/application/commands/build.py +166 -0
- context_map/application/commands/doctor_cmd.py +56 -0
- context_map/application/commands/export.py +136 -0
- context_map/application/commands/hook.py +49 -0
- context_map/application/commands/importers.py +163 -0
- context_map/application/commands/ingest.py +133 -0
- context_map/application/commands/personal.py +555 -0
- context_map/application/commands/refresh.py +138 -0
- context_map/application/commands/scan.py +38 -0
- context_map/application/commands/sync.py +179 -0
- context_map/application/commands/tools.py +127 -0
- context_map/application/commands/update.py +144 -0
- context_map/application/commands/watch.py +27 -0
- context_map/application/commands/wrap.py +89 -0
- context_map/cli.py +26 -0
- context_map/core/__init__.py +68 -0
- context_map/core/generators/__init__.py +13 -0
- context_map/core/generators/generadores.py +186 -0
- context_map/core/generators/narrative_templates.py +264 -0
- context_map/core/logging_setup.py +71 -0
- context_map/core/models/__init__.py +7 -0
- context_map/core/models/config.py +17 -0
- context_map/core/models/models.py +172 -0
- context_map/core/normalization/__init__.py +27 -0
- context_map/core/normalization/cleaning.py +135 -0
- context_map/core/normalization/inference.py +209 -0
- context_map/core/normalization/mappings.py +116 -0
- context_map/core/normalization/standardize.py +145 -0
- context_map/core/parsing/__init__.py +19 -0
- context_map/core/parsing/cargadores.py +108 -0
- context_map/core/parsing/clasificacion.py +45 -0
- context_map/core/parsing/dedup.py +28 -0
- context_map/core/parsing/grafo.py +78 -0
- context_map/core/personal/__init__.py +23 -0
- context_map/core/personal/bd.py +527 -0
- context_map/core/storage/__init__.py +17 -0
- context_map/core/storage/config_loader.py +62 -0
- context_map/core/storage/store.py +214 -0
- context_map/core/tokenization.py +258 -0
- context_map/domain/__init__.py +49 -0
- context_map/domain/analysis/__init__.py +17 -0
- context_map/domain/analysis/checker.py +48 -0
- context_map/domain/analysis/complexity.py +134 -0
- context_map/domain/analysis/models.py +46 -0
- context_map/domain/analysis/reporting.py +95 -0
- context_map/domain/analysis/signals.py +411 -0
- context_map/domain/analyzers/cyclomatic.py +125 -0
- context_map/domain/ecosystem/__init__.py +27 -0
- context_map/domain/ecosystem/adaptador.py +229 -0
- context_map/domain/ecosystem/detector.py +436 -0
- context_map/domain/ecosystem/hooks.py +131 -0
- context_map/domain/ecosystem/rules_templates.py +396 -0
- context_map/domain/health/__init__.py +24 -0
- context_map/domain/health/doctor.py +321 -0
- context_map/domain/ingestion/__init__.py +19 -0
- context_map/domain/ingestion/ingest.py +292 -0
- context_map/domain/reglas/reglas.py +244 -0
- context_map/domain/reporting/__init__.py +13 -0
- context_map/domain/reporting/reporter.py +189 -0
- context_map/domain/scanning/__init__.py +13 -0
- context_map/domain/scanning/cache.py +116 -0
- context_map/domain/scanning/scanner.py +307 -0
- context_map/domain/scanning/security.py +79 -0
- context_map/domain/synchronization/__init__.py +7 -0
- context_map/domain/synchronization/relaciones.py +67 -0
- context_map/domain/synchronization/sync.py +249 -0
- context_map/domain/synchronization/watcher.py +142 -0
- context_map/infrastructure/__init__.py +4 -0
- context_map/infrastructure/analyzers/__init__.py +4 -0
- context_map/infrastructure/analyzers/content.py +182 -0
- context_map/infrastructure/analyzers/structure.py +194 -0
- context_map/infrastructure/integrations/__init__.py +4 -0
- context_map/infrastructure/integrations/antigravity.py +398 -0
- context_map/infrastructure/integrations/chat_export.py +217 -0
- context_map/infrastructure/integrations/git.py +147 -0
- context_map/infrastructure/integrations/hermes.py +304 -0
- context_map/infrastructure/mcp_server.py +279 -0
- context_map/infrastructure/version_check.py +192 -0
- context_map/presentation/__init__.py +20 -0
- context_map/presentation/briefs/__init__.py +9 -0
- context_map/presentation/briefs/agents.py +161 -0
- context_map/presentation/briefs/brief.py +107 -0
- context_map/presentation/briefs/extractors.py +167 -0
- context_map/presentation/briefs/sections.py +183 -0
- context_map/presentation/briefs/skill.py +224 -0
- context_map/presentation/vault/__init__.py +25 -0
- context_map/presentation/vault/atomic.py +420 -0
- context_map/presentation/vault/consolidated/__init__.py +22 -0
- context_map/presentation/vault/consolidated/canvas.py +281 -0
- context_map/presentation/vault/consolidated/common.py +611 -0
- context_map/presentation/vault/consolidated/consolidado.py +479 -0
- context_map/presentation/vault/consolidated/jerarquico.py +262 -0
- context_map/presentation/vault/consolidated/notas_ideas.py +232 -0
- context_map/presentation/vault/consolidated/rutas.py +149 -0
- context_map/presentation/vault/consolidated/secciones_backlog.py +131 -0
- context_map/presentation/vault/consolidated/secciones_estructura.py +248 -0
- context_map/presentation/vault/consolidated/secciones_historial.py +252 -0
- context_map/presentation/vault/consolidated/secciones_ideas.py +331 -0
- context_map/presentation/vault/consolidated/secciones_proposito.py +220 -0
- context_map/presentation/vault/consolidated/secciones_riesgos.py +187 -0
- context_map/presentation/vault/mermaid.py +150 -0
- context_map/presentation/vault/preservar.py +121 -0
- context_map/presentation/vault/templates.py +197 -0
- context_map/presentation/vault/writer.py +160 -0
- context_map_ai-2.0.0.dist-info/METADATA +270 -0
- context_map_ai-2.0.0.dist-info/RECORD +148 -0
- context_map_ai-2.0.0.dist-info/WHEEL +4 -0
- context_map_ai-2.0.0.dist-info/entry_points.txt +2 -0
- context_map_ai-2.0.0.dist-info/licenses/LICENSE +21 -0
context_map/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__all__ = ["build_map"]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Configuración compartida de pytest.
|
|
2
|
+
|
|
3
|
+
Aísla la base de datos personal de ContextMap durante los tests: cualquier
|
|
4
|
+
``do_sync`` (build/scan/refresh) que se ejecute en la suite consolida en una
|
|
5
|
+
BD temporal, jamás en la BD personal real del usuario (F: / ~/.context-map).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import contextlib
|
|
11
|
+
import os
|
|
12
|
+
import tempfile
|
|
13
|
+
from collections.abc import Generator
|
|
14
|
+
|
|
15
|
+
import pytest
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.fixture(autouse=True)
|
|
19
|
+
def _aislar_bd_personal(monkeypatch: pytest.MonkeyPatch) -> Generator[None, None, None]:
|
|
20
|
+
"""Redirige CTXMAP_PERSONAL_DB a una ruta temporal por test.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
monkeypatch: Fixture de pytest para parchear variables de entorno.
|
|
24
|
+
|
|
25
|
+
Yields:
|
|
26
|
+
None: cede el control al test con la variable ya parcheada.
|
|
27
|
+
"""
|
|
28
|
+
fd, ruta_tmp = tempfile.mkstemp(prefix="ctxmap_test_personal_", suffix=".db")
|
|
29
|
+
os.close(fd)
|
|
30
|
+
monkeypatch.setenv("CTXMAP_PERSONAL_DB", ruta_tmp)
|
|
31
|
+
yield
|
|
32
|
+
with contextlib.suppress(OSError):
|
|
33
|
+
os.remove(ruta_tmp)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from context_map.core.models import Edge, Node
|
|
4
|
+
from context_map.presentation.vault import render_active_map
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_smoke_render() -> None:
|
|
8
|
+
"""
|
|
9
|
+
Verifica que el writer genera el mapa esperado.
|
|
10
|
+
"""
|
|
11
|
+
nodes: list[Node] = [
|
|
12
|
+
Node(id="BASE-01", type="BASE", title="Demo", summary="demo", source="test")
|
|
13
|
+
]
|
|
14
|
+
edges: list[Edge] = [Edge(source="BASE-01", target="IDEA-01", kind="depends_on")]
|
|
15
|
+
md = render_active_map("Demo", nodes, edges)
|
|
16
|
+
assert "# Demo — Context Map" in md
|
|
17
|
+
assert "## Mapa mental de contexto" in md
|
|
18
|
+
assert "## Conexiones" in md
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
if __name__ == "__main__":
|
|
22
|
+
test_smoke_render()
|
|
23
|
+
print("smoke:ok")
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Tests de la señal de frescura del contexto (R1, auditoría 2026-08-14).
|
|
2
|
+
|
|
3
|
+
Verifica que ``check`` (y por tanto ``refresh``) detecta actividad posterior
|
|
4
|
+
al último build: commits git y sesiones de Hermes sin importar.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from types import SimpleNamespace
|
|
11
|
+
|
|
12
|
+
from context_map.domain.analysis.checker import (
|
|
13
|
+
analizar_readiness,
|
|
14
|
+
_sesiones_posteriores,
|
|
15
|
+
_ultima_actividad,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _hacer_repo(tmp_path, con_build: bool = True, con_git: bool = True) -> str:
|
|
20
|
+
"""Crea un mini proyecto con ``.context-map/state/last_build.json``.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
tmp_path: Fixture de pytest con directorio temporal.
|
|
24
|
+
con_build (bool): Si se crea el last_build.json.
|
|
25
|
+
con_git (bool): Si se crea la carpeta .git.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
str: Ruta del mini proyecto.
|
|
29
|
+
"""
|
|
30
|
+
ctx = tmp_path / ".context-map" / "state"
|
|
31
|
+
ctx.mkdir(parents=True)
|
|
32
|
+
if con_build:
|
|
33
|
+
(ctx / "last_build.json").write_text(
|
|
34
|
+
json.dumps({"clean": False, "manuales_preservadas": 0,
|
|
35
|
+
"timestamp": "2000-01-01T00:00:00"}),
|
|
36
|
+
encoding="utf-8",
|
|
37
|
+
)
|
|
38
|
+
if con_git:
|
|
39
|
+
(tmp_path / ".git").mkdir(exist_ok=True)
|
|
40
|
+
return str(tmp_path)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _sesion(fecha_inicio: str) -> SimpleNamespace:
|
|
44
|
+
"""Crea una sesión fake de Hermes con la fecha de inicio dada.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
fecha_inicio (str): Timestamp (epoch o ISO) de inicio de la sesión.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
SimpleNamespace: Sesión mínima compatible con el modelo.
|
|
51
|
+
"""
|
|
52
|
+
return SimpleNamespace(fecha_inicio=fecha_inicio)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_ultima_actividad_detecta_commit_posterior_al_build(tmp_path, monkeypatch):
|
|
56
|
+
"""Si hay un commit posterior al last_build, la señal avisa."""
|
|
57
|
+
ruta = _hacer_repo(tmp_path)
|
|
58
|
+
|
|
59
|
+
def _fake_git(_ruta, args): # noqa: ANN001
|
|
60
|
+
if args and args[0] == "log":
|
|
61
|
+
return "1800000000" # epoch posterior al build (2000-01-01)
|
|
62
|
+
return ""
|
|
63
|
+
|
|
64
|
+
monkeypatch.setattr("context_map.domain.analysis.checker._ejecutar_git", _fake_git)
|
|
65
|
+
monkeypatch.setattr("context_map.domain.analysis.checker.leer_sesiones", lambda *_: [])
|
|
66
|
+
actividad = _ultima_actividad(ruta)
|
|
67
|
+
assert actividad["commits_posteriores"] > 0
|
|
68
|
+
assert "commit" in actividad["aviso"].lower()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_check_avisa_con_sesiones_sin_importar(tmp_path, monkeypatch):
|
|
72
|
+
"""Si hay sesiones de Hermes posteriores al build, la señal avisa."""
|
|
73
|
+
ruta = _hacer_repo(tmp_path)
|
|
74
|
+
|
|
75
|
+
def _fake_git(_ruta, args): # noqa: ANN001
|
|
76
|
+
return ""
|
|
77
|
+
|
|
78
|
+
def _fake_leer(db_path=None, limite=None): # noqa: ANN001
|
|
79
|
+
return [_sesion("1800000000"), _sesion("1800000000"), _sesion("1800000000")]
|
|
80
|
+
|
|
81
|
+
monkeypatch.setattr("context_map.domain.analysis.checker._ejecutar_git", _fake_git)
|
|
82
|
+
monkeypatch.setattr("context_map.domain.analysis.checker.leer_sesiones", _fake_leer)
|
|
83
|
+
resultado = analizar_readiness(ruta)
|
|
84
|
+
assert any("sin importar" in s for s in resultado.sugerencias)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_check_sin_actividad_no_avisa(tmp_path, monkeypatch):
|
|
88
|
+
"""Sin commits ni sesiones posteriores, no hay falso aviso."""
|
|
89
|
+
ruta = _hacer_repo(tmp_path)
|
|
90
|
+
|
|
91
|
+
def _fake_git(_ruta, args): # noqa: ANN001
|
|
92
|
+
return ""
|
|
93
|
+
|
|
94
|
+
def _fake_leer(db_path=None, limite=None): # noqa: ANN001
|
|
95
|
+
return [_sesion("100")] # anterior al build
|
|
96
|
+
|
|
97
|
+
monkeypatch.setattr("context_map.domain.analysis.checker._ejecutar_git", _fake_git)
|
|
98
|
+
monkeypatch.setattr("context_map.domain.analysis.checker.leer_sesiones", _fake_leer)
|
|
99
|
+
resultado = analizar_readiness(ruta)
|
|
100
|
+
assert not any("sin importar" in s for s in resultado.sugerencias)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_sesiones_posteriores_cuenta_solo_las_recientes(tmp_path, monkeypatch):
|
|
104
|
+
"""Cuenta sesiones cuya fecha de inicio supera el timestamp del build."""
|
|
105
|
+
ruta = _hacer_repo(tmp_path)
|
|
106
|
+
|
|
107
|
+
def _fake_leer(db_path=None, limite=None): # noqa: ANN001
|
|
108
|
+
return [
|
|
109
|
+
_sesion("1800000000"), # posterior a 2000-01-01
|
|
110
|
+
_sesion("1800000000"),
|
|
111
|
+
_sesion("100"), # anterior al build
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
monkeypatch.setattr("context_map.domain.analysis.checker.leer_sesiones", _fake_leer)
|
|
115
|
+
assert _sesiones_posteriores(ruta) == 2
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_sin_build_previo_avisa_nunca_build(tmp_path):
|
|
119
|
+
"""Si no existe last_build.json, el aviso indica que nunca se ha construido."""
|
|
120
|
+
ruta = _hacer_repo(tmp_path, con_build=False)
|
|
121
|
+
actividad = _ultima_actividad(ruta)
|
|
122
|
+
assert "nunca" in actividad["aviso"].lower()
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Pruebas unitarias para el parser principal del CLI y despacho de comandos."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
from context_map.application.cli.parser import create_parser
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_cli_parser_defaults() -> None:
|
|
11
|
+
"""Verifica que el parser principal cree los subcomandos esperados."""
|
|
12
|
+
parser = create_parser()
|
|
13
|
+
assert isinstance(parser, argparse.ArgumentParser)
|
|
14
|
+
|
|
15
|
+
# Test parser para comando 'check'
|
|
16
|
+
args = parser.parse_args(["check", "."])
|
|
17
|
+
assert args.cmd == "check"
|
|
18
|
+
assert args.target == "."
|
|
19
|
+
|
|
20
|
+
# Test parser para comando 'build'
|
|
21
|
+
args_build = parser.parse_args(["build", "--clean", "--brief"])
|
|
22
|
+
assert args_build.cmd == "build"
|
|
23
|
+
assert args_build.clean is True
|
|
24
|
+
assert args_build.brief is True
|
|
25
|
+
|
|
26
|
+
# Test parser para comando 'scan'
|
|
27
|
+
args_scan = parser.parse_args(["scan", "."])
|
|
28
|
+
assert args_scan.cmd == "scan"
|
|
29
|
+
assert args_scan.target == "."
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_cli_parser_auto_mode() -> None:
|
|
33
|
+
"""Verifica los flags por defecto del comando auto."""
|
|
34
|
+
parser = create_parser()
|
|
35
|
+
args_auto = parser.parse_args(["auto", ".", "--project", "TestApp"])
|
|
36
|
+
assert args_auto.cmd == "auto"
|
|
37
|
+
assert args_auto.target == "."
|
|
38
|
+
assert args_auto.project == "TestApp"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_aviso_pre_actualizacion_contiene_comando(monkeypatch) -> None:
|
|
42
|
+
"""El aviso pre-refresh informa de la versión nueva y el comando de actualización."""
|
|
43
|
+
import context_map.infrastructure.version_check as vc
|
|
44
|
+
from context_map.infrastructure.version_check import aviso_pre_actualizacion
|
|
45
|
+
|
|
46
|
+
monkeypatch.setattr(vc, "hay_actualizacion", lambda force=False: (True, "1.4.0", "1.5.0"))
|
|
47
|
+
msg = aviso_pre_actualizacion()
|
|
48
|
+
assert "DESACTUALIZADO" in msg
|
|
49
|
+
assert "1.5.0" in msg
|
|
50
|
+
assert "uv tool install" in msg
|
|
51
|
+
assert "--with" in msg # incluye el SDK de MCP
|
|
52
|
+
|
|
53
|
+
monkeypatch.setattr(vc, "hay_actualizacion", lambda force=False: (False, "1.5.0", "1.5.0"))
|
|
54
|
+
assert aviso_pre_actualizacion() == ""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_refresh_imprime_aviso_pre_actualizacion(monkeypatch, capsys) -> None:
|
|
58
|
+
"""El refresh verifica la versión del PROGRAMA antes de actualizar el CONTEXTO."""
|
|
59
|
+
import context_map.infrastructure.version_check as vc
|
|
60
|
+
from context_map.application.commands import refresh as refresh_mod
|
|
61
|
+
from context_map.application.commands.refresh import cmd_refresh
|
|
62
|
+
|
|
63
|
+
# No ejecutar scan/build/check reales en el test
|
|
64
|
+
monkeypatch.setattr(refresh_mod, "cmd_scan", lambda args: None)
|
|
65
|
+
monkeypatch.setattr(refresh_mod, "cmd_build", lambda args: None)
|
|
66
|
+
monkeypatch.setattr(refresh_mod, "cmd_check", lambda args: None)
|
|
67
|
+
# Hay una actualización pendiente → debe avisarse al inicio
|
|
68
|
+
monkeypatch.setattr(vc, "hay_actualizacion", lambda force=False: (True, "1.4.0", "1.5.0"))
|
|
69
|
+
|
|
70
|
+
args = argparse.Namespace(target=".", project="Demo", quiet=False, mode="hierarchical")
|
|
71
|
+
cmd_refresh(args)
|
|
72
|
+
out = capsys.readouterr().out
|
|
73
|
+
|
|
74
|
+
assert "DESACTUALIZADO" in out, "El refresh no avisó de la actualización pendiente"
|
|
75
|
+
assert "1.5.0" in out
|
|
76
|
+
# El aviso aparece ANTES del resultado del refresh
|
|
77
|
+
assert out.index("DESACTUALIZADO") < out.index("[refresh] [OK]")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_refresh_importa_sesiones_automatico(monkeypatch, capsys) -> None:
|
|
81
|
+
"""El refresh importa las sesiones recientes de Hermes (memoria viva automática)."""
|
|
82
|
+
import argparse
|
|
83
|
+
|
|
84
|
+
from context_map.application.commands import refresh as refresh_mod
|
|
85
|
+
from context_map.application.commands.refresh import cmd_refresh
|
|
86
|
+
|
|
87
|
+
monkeypatch.setattr(refresh_mod, "cmd_scan", lambda args: None)
|
|
88
|
+
monkeypatch.setattr(refresh_mod, "cmd_build", lambda args: None)
|
|
89
|
+
monkeypatch.setattr(refresh_mod, "cmd_check", lambda args: None)
|
|
90
|
+
|
|
91
|
+
llamado: dict = {}
|
|
92
|
+
|
|
93
|
+
def _fake_importar_sesiones(**kwargs):
|
|
94
|
+
llamado.update(kwargs)
|
|
95
|
+
return 3
|
|
96
|
+
|
|
97
|
+
monkeypatch.setattr(
|
|
98
|
+
"context_map.infrastructure.integrations.hermes.importar_sesiones",
|
|
99
|
+
_fake_importar_sesiones,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
args = argparse.Namespace(target=".", project="Demo", quiet=False, mode="hierarchical")
|
|
103
|
+
cmd_refresh(args)
|
|
104
|
+
out = capsys.readouterr().out
|
|
105
|
+
|
|
106
|
+
# Se importan las 5 sesiones más recientes y los eventos van al raw del proyecto
|
|
107
|
+
assert llamado.get("limite") == 5
|
|
108
|
+
assert "events.jsonl" in llamado.get("output_path", "")
|
|
109
|
+
assert "3 evento(s) de sesiones importados" in out
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_refresh_importar_sesiones_tolerante(monkeypatch, capsys) -> None:
|
|
113
|
+
"""Si la BD de sesiones no existe, el refresh NO se rompe (aviso y sigue)."""
|
|
114
|
+
import argparse
|
|
115
|
+
|
|
116
|
+
from context_map.application.commands import refresh as refresh_mod
|
|
117
|
+
from context_map.application.commands.refresh import cmd_refresh
|
|
118
|
+
|
|
119
|
+
monkeypatch.setattr(refresh_mod, "cmd_scan", lambda args: None)
|
|
120
|
+
monkeypatch.setattr(refresh_mod, "cmd_build", lambda args: None)
|
|
121
|
+
monkeypatch.setattr(refresh_mod, "cmd_check", lambda args: None)
|
|
122
|
+
|
|
123
|
+
def _fake_importar_sesiones(**kwargs):
|
|
124
|
+
raise FileNotFoundError("sin BD de sesiones")
|
|
125
|
+
|
|
126
|
+
monkeypatch.setattr(
|
|
127
|
+
"context_map.infrastructure.integrations.hermes.importar_sesiones",
|
|
128
|
+
_fake_importar_sesiones,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
args = argparse.Namespace(target=".", project="Demo", quiet=False, mode="hierarchical")
|
|
132
|
+
cmd_refresh(args) # no debe lanzar excepción
|
|
133
|
+
out = capsys.readouterr().out
|
|
134
|
+
assert "[refresh] [OK]" in out, "El refresh se rompió por la importación de sesiones"
|