feat: add repository mesh reconciliation round
This commit is contained in:
162
tests/test_repository_mesh_runtime.py
Normal file
162
tests/test_repository_mesh_runtime.py
Normal file
@@ -0,0 +1,162 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user