import contextlib import io import tempfile import unittest from pathlib import Path from ampg.cli import main from ampg.config import load_config from ampg.health import blocked_health_checks, health_plan def _write_config(root: Path, protocol_block: str) -> Path: source = root / "site" source.mkdir() (source / "index.html").write_text("

Hello

", encoding="utf-8") config_path = root / "gateway.toml" config_path.write_text( f""" [profiles.mobile-i2p] protocols = ["i2p"] [[site]] id = "example" domain = "example.test" [site.source] kind = "static-html" path = "./site" canonical_url = "https://example.test" [site.outputs] root = "./out" plan_root = "./plan" {protocol_block} """, encoding="utf-8", ) return config_path def _mark_output(root: Path, protocol: str) -> None: output = root / f"out/{protocol}" output.mkdir(parents=True) (output / ".ampg-output").write_text("generated by AMPG\n", encoding="utf-8") class HealthPlanTest(unittest.TestCase): def test_published_health_plan_reviews_placeholder_i2p_address(self): with tempfile.TemporaryDirectory() as tmp: root = Path(tmp) config = load_config( _write_config( root, """ [site.protocols.i2p] enabled = true renderer = "privacy-html" daemon = "i2pd" daemon_policy = "auto" """, ) ) _mark_output(root, "i2p") checks = health_plan(config) self.assertEqual(1, len(checks)) self.assertEqual([], blocked_health_checks(checks)) self.assertEqual("review", checks[0].status) self.assertEqual("configure-address", checks[0].action) self.assertEqual("placeholder", checks[0].address_status) self.assertIn("127.0.0.1:4444", checks[0].command) def test_configured_i2p_health_plan_is_planned(self): with tempfile.TemporaryDirectory() as tmp: root = Path(tmp) config = load_config( _write_config( root, """ [site.protocols.i2p] enabled = true renderer = "privacy-html" i2p_url = "site.i2p" """, ) ) _mark_output(root, "i2p") checks = health_plan(config) self.assertEqual("planned", checks[0].status) self.assertEqual("configured", checks[0].address_status) self.assertEqual("http://site.i2p/", checks[0].url) def test_missing_output_blocks_health_plan(self): with tempfile.TemporaryDirectory() as tmp: config = load_config( _write_config( Path(tmp), """ [site.protocols.tor] enabled = true renderer = "privacy-html" """, ) ) checks = health_plan(config) self.assertEqual(1, len(blocked_health_checks(checks))) self.assertEqual("build-output", checks[0].action) def test_preview_health_plan_uses_loopback_urls(self): with tempfile.TemporaryDirectory() as tmp: root = Path(tmp) config = load_config( _write_config( root, """ [site.protocols.tor] enabled = true renderer = "privacy-html" """, ) ) _mark_output(root, "tor") checks = health_plan(config, mode="preview", base_port=19900) self.assertEqual(1, len(checks)) self.assertEqual("planned", checks[0].status) self.assertEqual("preview", checks[0].address_status) self.assertEqual("http://127.0.0.1:19900/", checks[0].url) self.assertEqual("clearnet", checks[0].transport) def test_health_plan_cli_uses_profile_scope(self): with tempfile.TemporaryDirectory() as tmp: root = Path(tmp) config_path = _write_config( root, """ [site.protocols.clearnet] enabled = true renderer = "clearnet" [site.protocols.i2p] enabled = true renderer = "privacy-html" i2p_url = "site.i2p" """, ) _mark_output(root, "i2p") stdout = io.StringIO() with contextlib.redirect_stdout(stdout): status = main( [ "--config", str(config_path), "health-plan", "--profile", "mobile-i2p", ] ) output = stdout.getvalue() self.assertEqual(0, status) self.assertIn("protocol=i2p", output) self.assertIn("planned=1", output) self.assertNotIn("protocol=clearnet", output) def test_health_plan_cli_blocks_missing_output(self): with tempfile.TemporaryDirectory() as tmp: config_path = _write_config( Path(tmp), """ [site.protocols.i2p] enabled = true renderer = "privacy-html" """, ) stdout = io.StringIO() with contextlib.redirect_stdout(stdout): status = main(["--config", str(config_path), "health-plan"]) output = stdout.getvalue() self.assertEqual(1, status) self.assertIn("blocked=1", output) if __name__ == "__main__": unittest.main()