from __future__ import annotations import json import os import unittest from datetime import datetime, timezone from pathlib import Path from mais_humana.repository_mesh import MeshEnvironment, MeshEnvironmentKind, RepositoryTarget, build_mesh_report from mais_humana.repository_mesh_reconciliation import build_reconciliation_plan from mais_humana.repository_mesh_runtime import ( RuntimeCommandStatus, RuntimeLockStatus, acquire_lock, build_runtime_cycle, commands_from_report_and_plan, cron_scheduler_spec, lock_is_stale, release_lock, runtime_csv, runtime_jsonl, runtime_markdown, scheduler_markdown, scheduler_payload, windows_scheduler_spec, write_runtime_artifacts, ) from tests.helpers import make_tmp from tests.test_repository_mesh import FakeGit, make_repo def target() -> RepositoryTarget: return RepositoryTarget("alpha", "admin/alpha", "alpha", "01_alpha") def env(root: Path) -> MeshEnvironment: return MeshEnvironment("primary", MeshEnvironmentKind.WINDOWS_PRIMARY, str(root), "test") def clean_report(root: Path): repo = make_repo(root, "alpha") fake = FakeGit() fake.set_repo(repo, remote="https://git.ami.app.br/admin/alpha.git") return build_mesh_report(root, targets=(target(),), environments=(env(root),), runner=fake) class RepositoryMeshRuntimeTests(unittest.TestCase): def test_lock_acquire_busy_release_cycle(self) -> None: tmp = make_tmp() lock_path = tmp / "mesh.lock.json" first = acquire_lock(lock_path, owner="one", expires_after_seconds=600) self.assertEqual(first.status, RuntimeLockStatus.ACQUIRED) second = acquire_lock(lock_path, owner="two", expires_after_seconds=600) self.assertEqual(second.status, RuntimeLockStatus.BUSY) released = release_lock(first) self.assertEqual(released.status, RuntimeLockStatus.RELEASED) third = acquire_lock(lock_path, owner="three", expires_after_seconds=600) self.assertEqual(third.status, RuntimeLockStatus.ACQUIRED) release_lock(third) def test_lock_stale_detection_accepts_malformed_payload_as_recoverable(self) -> None: self.assertTrue(lock_is_stale({"createdAt": "bad", "expiresAfterSeconds": 600})) self.assertTrue(lock_is_stale({"createdAt": "2026-01-01T00:00:00+00:00", "expiresAfterSeconds": 0})) self.assertTrue( lock_is_stale( { "createdAt": datetime.now(timezone.utc).isoformat(), "expiresAfterSeconds": 600, "pid": 99999999 if os.name == "nt" else 999999, } ) ) def test_commands_from_clean_report_allow_only_safe_fetch_like_commands(self) -> None: tmp = make_tmp() report = clean_report(tmp) plan = build_reconciliation_plan(report) commands = commands_from_report_and_plan(report, plan) self.assertTrue(commands) self.assertTrue(any(command.status == RuntimeCommandStatus.ALLOWED for command in commands)) self.assertFalse(any("reset" in command.command for command in commands)) def test_dirty_report_blocks_runtime_commands(self) -> None: tmp = make_tmp() repo = make_repo(tmp, "alpha") fake = FakeGit() fake.set_repo(repo, remote="https://git.ami.app.br/admin/alpha.git", status=(" M README.md",)) report = build_mesh_report(tmp, targets=(target(),), environments=(env(tmp),), runner=fake) plan = build_reconciliation_plan(report) commands = commands_from_report_and_plan(report, plan) self.assertTrue(any(command.status == RuntimeCommandStatus.BLOCKED for command in commands)) self.assertTrue(any("destrutivo" in " ".join(command.blocked_reasons) or "manual" in " ".join(command.blocked_reasons) for command in commands)) def test_runtime_cycle_blocks_everything_when_lock_not_acquired(self) -> None: tmp = make_tmp() report = clean_report(tmp) plan = build_reconciliation_plan(report) first = acquire_lock(tmp / "lock.json", owner="one") busy = acquire_lock(tmp / "lock.json", owner="two") cycle = build_runtime_cycle(report, plan, lock=busy) self.assertEqual(cycle.lock.status, RuntimeLockStatus.BUSY) self.assertTrue(all(result.status == RuntimeCommandStatus.BLOCKED for result in cycle.results)) release_lock(first) def test_runtime_outputs_csv_jsonl_and_markdown(self) -> None: tmp = make_tmp() report = clean_report(tmp) plan = build_reconciliation_plan(report) lock = acquire_lock(tmp / "lock.json", owner="one") cycle = build_runtime_cycle(report, plan, lock=lock) self.assertIn("command_id,target_name,status", runtime_csv(cycle)) self.assertIn("Repository Mesh Runtime Cycle", runtime_markdown(cycle)) first_line = runtime_jsonl(cycle).splitlines()[0] self.assertIn("command", json.loads(first_line)) release_lock(lock) def test_scheduler_specs_include_five_minute_commands(self) -> None: tmp = make_tmp() win = windows_scheduler_spec( python_exe="python.exe", project_root=tmp, ecosystem_root=tmp / "eco", central_platform_folder=tmp / "central", ) cron = cron_scheduler_spec( python_exe="python", project_root=tmp, ecosystem_root=tmp / "eco", central_platform_folder=tmp / "central", ) payload = scheduler_payload((win, cron)) md = scheduler_markdown((win, cron)) self.assertIn("windows_task", payload["kinds"]) self.assertIn("cron", payload["kinds"]) self.assertIn("New-ScheduledTaskAction", md) self.assertIn("crontab", md) def test_write_runtime_artifacts_records_project_and_central_files(self) -> None: tmp = make_tmp() project = tmp / "project" central = tmp / "central" project.mkdir() central.mkdir() report = clean_report(tmp / "eco") plan = build_reconciliation_plan(report) lock = acquire_lock(project / "dados" / "lock.json", owner="one") cycle = build_runtime_cycle(report, plan, lock=lock) specs = ( windows_scheduler_spec(python_exe="python.exe", project_root=project, ecosystem_root=tmp / "eco", central_platform_folder=central), cron_scheduler_spec(python_exe="python", project_root=project, ecosystem_root=tmp / "eco", central_platform_folder=central), ) records = write_runtime_artifacts(cycle, specs, project, central_platform_folder=central) self.assertTrue((project / "dados" / "repository-mesh-runtime-cycle.json").exists()) self.assertTrue((project / "dados" / "repository-mesh-runtime.jsonl").exists()) self.assertTrue((project / "ecossistema" / "REPOSITORY-MESH-SCHEDULERS.md").exists()) self.assertTrue((central / "reports" / "EXECUTADO__repository-mesh-runtime.md").exists()) self.assertGreaterEqual(len(records), 6) release_lock(lock) if __name__ == "__main__": unittest.main()