"""Tests para preregister_hypothesis (pre-registro inmutable, anti-HARKing). Importa el modulo hoja directamente (`preregister_hypothesis`) para no depender de que el paquete reexporte la funcion en su __init__ (lo integra el orquestador al cerrar el grupo papers). El pytest del repo resuelve el modulo hoja por su nombre directo. Todos los tests son hermeticos y deterministas: usan el fixture `tmp_path` de pytest; NUNCA escriben en `papers/`. """ from preregister_hypothesis import preregister_hypothesis def _parse_frontmatter(text: str) -> dict: parts = text.split("---", 2) fm = {} for line in parts[1].splitlines(): line = line.strip() if not line or ":" not in line: continue key, _, value = line.partition(":") fm[key.strip()] = value.strip() return fm HYP = {"h0": "no hay diferencia entre A y B", "h1": "el grupo A > grupo B"} PLAN = { "test": "welch_t_test", "effect_size_metric": "cohens_d", "decision_rule": "rechazar H0 si p<0.05 tras Holm y |d|>=0.5", "planned_n": 100, "multiple_correction": "holm", } def test_golden_congela_y_escribe_archivo(tmp_path): paper = tmp_path / "0001-x" paper.mkdir() res = preregister_hypothesis(str(paper), HYP, PLAN) assert res["status"] == "frozen" pre = paper / "preregistration.md" assert pre.exists() text = pre.read_text(encoding="utf-8") fm = _parse_frontmatter(text) assert fm["status"] == "frozen" assert fm["paper_slug"] == "0001-x" assert fm["content_hash"] # no vacio assert fm["frozen_at"] # no vacio assert res["content_hash"] == fm["content_hash"] assert res["frozen_at"] == fm["frozen_at"] def test_idempotente_mismo_input_no_reescribe(tmp_path): paper = tmp_path / "0001-x" paper.mkdir() pre = paper / "preregistration.md" first = preregister_hypothesis(str(paper), HYP, PLAN) assert first["status"] == "frozen" bytes_before = pre.read_bytes() second = preregister_hypothesis(str(paper), HYP, PLAN) assert second["status"] == "unchanged" # Mismo hash y frozen_at original preservado. assert second["content_hash"] == first["content_hash"] assert second["frozen_at"] == first["frozen_at"] # El archivo NO cambio byte a byte (incl. frozen_at). assert pre.read_bytes() == bytes_before def test_inmutabilidad_anti_harking_rechaza_contenido_distinto(tmp_path): paper = tmp_path / "0001-x" paper.mkdir() pre = paper / "preregistration.md" preregister_hypothesis(str(paper), HYP, PLAN) bytes_frozen = pre.read_bytes() # Intento de re-congelar con una hipotesis DISTINTA (HARKing) -> rechazado. hyp_tramposo = {"h0": "no hay diferencia", "h1": "el grupo B > grupo A (cambiado tras ver datos)"} res = preregister_hypothesis(str(paper), hyp_tramposo, PLAN) assert res["status"] == "error" # Asercion mas importante: el archivo en disco SIGUE siendo el original. assert pre.read_bytes() == bytes_frozen def test_error_paper_dir_inexistente_no_crash_no_crea(tmp_path): missing = tmp_path / "no-existe" res = preregister_hypothesis(str(missing), HYP, PLAN) assert res["status"] == "error" # No se creo el directorio ni el archivo. assert not missing.exists() assert not (missing / "preregistration.md").exists()