Files
fn_registry/python/functions/infra/mark_claude_parent_test.py
T
egutierrez 753e16b84c feat(infra): mark_claude_parent — escribe parent_orchestrator en goal.json (PID->sessionId)
Helper py analogo a mark_claude_role: resuelve el sessionId de un Claude recien
arrancado por su PID (sondeando sessions/<pid>.json) y escribe SOLO la clave
parent_orchestrator en su goal.json, preservando el resto. Lo consume
spawn_fleet_agent --parent para que el watcher de fleetview rutee los avisos del
ejecutor a su orquestador padre. Tests: escribe+preserva, goal inexistente,
parent vacio (ValueError), timeout sin crash.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 13:27:47 +02:00

122 lines
3.9 KiB
Python

"""Tests para mark_claude_parent."""
import json
import os
import pytest
from mark_claude_parent import mark_claude_parent
def _write_session(sessions_dir, pid, session_id):
"""Escribe un sessions/<pid>.json con el sessionId dado."""
os.makedirs(sessions_dir, exist_ok=True)
path = os.path.join(sessions_dir, f"{pid}.json")
with open(path, "w", encoding="utf-8") as fh:
json.dump({"sessionId": session_id, "cwd": "/tmp/whatever"}, fh)
return path
def _write_goal(goals_dir, session_id, goal):
"""Escribe un goal/<sessionId>.json con el dict dado."""
os.makedirs(goals_dir, exist_ok=True)
path = os.path.join(goals_dir, f"{session_id}.json")
with open(path, "w", encoding="utf-8") as fh:
json.dump(goal, fh)
return path
def _read_goal(goals_dir, session_id):
path = os.path.join(goals_dir, f"{session_id}.json")
with open(path, "r", encoding="utf-8") as fh:
return json.load(fh)
def test_sessions_presente_resuelve_y_escribe_parent_preservando_otros_campos(tmp_path):
sessions_dir = str(tmp_path / "sessions")
goals_dir = str(tmp_path / "goals")
pid = 4242
sid = "executor-abc-123"
parent = "orchestrator-xyz-789"
_write_session(sessions_dir, pid, sid)
# Goal preexistente con campos que NO deben perderse (incluido role).
_write_goal(
goals_dir,
sid,
{
"goal": "implementar fase 3",
"phase": "trabajando",
"role": "executor",
"dod_contract": {"capa1": "mecanica"},
},
)
res = mark_claude_parent(pid, parent, wait_s=2.0,
sessions_dir=sessions_dir, goals_dir=goals_dir)
assert res["ok"] is True
assert res["pid"] == pid
assert res["session_id"] == sid
assert res["parent_orchestrator"] == parent
assert res["path"] == os.path.join(goals_dir, f"{sid}.json")
goal = _read_goal(goals_dir, sid)
assert goal["parent_orchestrator"] == parent
# Todos los demas campos del goal se preservan intactos.
assert goal["goal"] == "implementar fase 3"
assert goal["phase"] == "trabajando"
assert goal["role"] == "executor"
assert goal["dod_contract"] == {"capa1": "mecanica"}
def test_parent_vacio_lanza_value_error_sin_escribir(tmp_path):
sessions_dir = str(tmp_path / "sessions")
goals_dir = str(tmp_path / "goals")
pid = 4242
sid = "executor-abc-123"
_write_session(sessions_dir, pid, sid)
with pytest.raises(ValueError):
mark_claude_parent(pid, " ", wait_s=2.0,
sessions_dir=sessions_dir, goals_dir=goals_dir)
# No escribio nada: el goals_dir ni siquiera deberia existir.
assert not os.path.exists(goals_dir)
def test_sessions_ausente_devuelve_timeout_sin_crash(tmp_path):
sessions_dir = str(tmp_path / "sessions")
goals_dir = str(tmp_path / "goals")
pid = 9999 # sin sessions/<pid>.json escrito
res = mark_claude_parent(pid, "orchestrator-xyz-789", wait_s=0.5,
sessions_dir=sessions_dir, goals_dir=goals_dir)
assert res["ok"] is False
assert res["pid"] == pid
assert "timeout" in res["error"]
assert f"{pid}.json" in res["error"]
# No se escribio ningun goal.
assert not os.path.exists(goals_dir)
def test_goal_inexistente_se_crea_con_solo_parent(tmp_path):
sessions_dir = str(tmp_path / "sessions")
goals_dir = str(tmp_path / "goals")
pid = 7
sid = "fresh-executor-uuid"
parent = "orchestrator-xyz-789"
_write_session(sessions_dir, pid, sid)
# No existe goal previo para esta sesion.
res = mark_claude_parent(pid, parent, wait_s=2.0,
sessions_dir=sessions_dir, goals_dir=goals_dir)
assert res["ok"] is True
assert res["session_id"] == sid
assert res["parent_orchestrator"] == parent
goal = _read_goal(goals_dir, sid)
assert goal == {"parent_orchestrator": parent}