Harden browser docs and transport recovery

This commit is contained in:
jwinterm
2026-07-11 08:59:21 -04:00
parent 97a43f8572
commit 4379b99245
17 changed files with 354 additions and 39 deletions
+49
View File
@@ -126,6 +126,55 @@ class CliOpenTest(unittest.TestCase):
helper.assert_called_once()
self.assertEqual(9876, helper.call_args.kwargs["watch_pid"])
def test_transport_command_prints_setup_hint(self) -> None:
result = ManagedTransportResult(
transport="i2p",
provider="-",
status="missing-provider",
endpoint="http://127.0.0.1:4444",
owned=False,
pid=0,
state_dir=".ampb/transports/i2p",
command=(),
message="I2P provider not found.",
setup_hint="Install i2pd with Homebrew: brew install i2pd.",
install_command=("brew", "install", "i2pd"),
)
with patch("ampbrowser.cli.transport_status", return_value=result):
with patch.object(sys, "stdout", new_callable=io.StringIO) as stdout:
code = main(["transport", "status", "i2p"])
self.assertEqual(0, code)
output = stdout.getvalue()
self.assertIn('install_command="brew install i2pd"', output)
self.assertIn('setup_hint="Install i2pd with Homebrew: brew install i2pd."', output)
def test_transport_repair_command_prints_result(self) -> None:
result = ManagedTransportResult(
transport="tor",
provider="arti",
status="repaired",
endpoint="socks5://127.0.0.1:9050",
owned=True,
pid=1234,
state_dir=".ampb/transports/tor",
command=("/tmp/arti", "proxy"),
message="removed AMPB-owned tor runtime state",
provider_source="bundled-sidecar",
)
with patch("ampbrowser.cli.repair_managed_transport", return_value=result) as repair:
with patch.object(sys, "stdout", new_callable=io.StringIO) as stdout:
code = main(["transport", "repair", "tor"])
self.assertEqual(0, code)
repair.assert_called_once()
output = stdout.getvalue()
self.assertIn("status=repaired", output)
self.assertIn("provider=arti", output)
self.assertIn('message="removed AMPB-owned tor runtime state"', output)
if __name__ == "__main__":
unittest.main()
+2
View File
@@ -180,10 +180,12 @@ class PrepareOpenTest(unittest.TestCase):
background = (extension / "background.js").read_text(encoding="utf-8")
setup = (extension / "setup.html").read_text(encoding="utf-8")
self.assertIn("webNavigation", manifest)
self.assertIn("install_command", background)
self.assertIn("http://127.0.0.1:", background)
self.assertNotIn("__AMPB_ROUTE_HELPER_URL__", background)
self.assertNotIn("__AMPB_ROUTE_HELPER_TOKEN__", background)
self.assertIn("Set Up Transport", setup)
self.assertIn("install-command", setup)
def test_execute_open_blocks_when_runtime_is_missing(self) -> None:
config = AppConfig(runtime_path="/missing/ampb/firefox", transport_modes={})
+32
View File
@@ -0,0 +1,32 @@
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parents[1]
PRIVATE_TARGET_TERMS = (
"wowrace",
"wowngeon",
"smirk-monorepo",
"medusa-multi-tenant-platform",
"ai-gen-bot",
)
class PublicPrivateSplitTest(unittest.TestCase):
def test_private_target_names_do_not_appear_in_public_docs(self) -> None:
failures = []
for path in sorted(ROOT.rglob("*.md")):
rel = path.relative_to(ROOT)
if rel.parts[0] in {".git", "docs"} and "private" in rel.parts:
continue
if "docs" in rel.parts and "generated" in rel.parts:
continue
text = path.read_text(encoding="utf-8").lower()
for term in PRIVATE_TARGET_TERMS:
if term in text:
failures.append(f"{rel}: contains private target term {term!r}")
self.assertEqual([], failures)
if __name__ == "__main__":
unittest.main()
+4
View File
@@ -51,6 +51,8 @@ class RouteHelperTest(unittest.TestCase):
command=("/opt/i2pd",),
message="started managed i2pd transport",
provider_source="system-package",
setup_hint="Install i2pd with Homebrew: brew install i2pd.",
install_command=("brew", "install", "i2pd"),
)
with tempfile.TemporaryDirectory() as tmp:
@@ -64,6 +66,8 @@ class RouteHelperTest(unittest.TestCase):
self.assertTrue(response["ok"])
self.assertTrue(response["ready"])
self.assertEqual("started", response["status"])
self.assertEqual("Install i2pd with Homebrew: brew install i2pd.", response["setup_hint"])
self.assertEqual(["brew", "install", "i2pd"], response["install_command"])
ensure.assert_called_once()
def test_blocks_unsupported_routes(self) -> None:
+52 -5
View File
@@ -6,7 +6,7 @@ import unittest
from unittest.mock import patch
from ampbrowser.config import AppConfig
from ampbrowser.transport_manager import ensure_transport_ready, stop_managed_transport, transport_status
from ampbrowser.transport_manager import ensure_transport_ready, repair_managed_transport, stop_managed_transport, transport_status
from ampbrowser.transports import TransportStatus
@@ -181,6 +181,51 @@ class TransportManagerTest(unittest.TestCase):
self.assertEqual("arti", result.provider)
self.assertFalse(state_path.exists())
def test_repair_managed_transport_removes_runtime_state(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
state_dir = root / ".ampb/transports/tor"
profile_dir = root / ".ampb/profiles/tor"
state_dir.mkdir(parents=True)
profile_dir.mkdir(parents=True)
state_path = state_dir / "ampb-owned.json"
state_path.write_text('{"pid": 1234, "provider": "arti", "endpoint": "socks5://127.0.0.1:9050"}\n', encoding="utf-8")
(state_dir / "arti.log").write_text("warn\n", encoding="utf-8")
(profile_dir / "user.js").write_text("prefs\n", encoding="utf-8")
live = [True]
def fake_alive(pid: int) -> bool:
return live[0]
def fake_kill(pid: int, sig: int) -> None:
live[0] = False
with patch("ampbrowser.transport_manager._pid_alive", side_effect=fake_alive):
with patch("ampbrowser.transport_manager.os.kill", side_effect=fake_kill):
result = repair_managed_transport("tor", config=AppConfig(transport_modes={}), root=root)
self.assertEqual("repaired", result.status)
self.assertFalse(state_dir.exists())
self.assertTrue((profile_dir / "user.js").exists())
self.assertIn("runtime state", result.message)
def test_repair_managed_transport_blocks_when_stop_fails(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
state_dir = root / ".ampb/transports/tor"
state_dir.mkdir(parents=True)
state_path = state_dir / "ampb-owned.json"
state_path.write_text('{"pid": 1234, "provider": "arti", "endpoint": "socks5://127.0.0.1:9050"}\n', encoding="utf-8")
with patch("ampbrowser.transport_manager._pid_alive", return_value=True):
with patch("ampbrowser.transport_manager.os.kill", side_effect=OSError("nope")):
result = repair_managed_transport("tor", config=AppConfig(transport_modes={}), root=root)
self.assertEqual("repair-blocked", result.status)
self.assertTrue(state_path.exists())
self.assertIn("could not repair", result.message)
def test_starts_configured_classic_tor_provider_with_ampb_state(self) -> None:
status = TransportStatus(
transport="tor",
@@ -270,14 +315,16 @@ class TransportManagerTest(unittest.TestCase):
)
with tempfile.TemporaryDirectory() as tmp:
with patch("ampbrowser.transport_manager.shutil.which", return_value=None):
with patch("ampbrowser.transport_manager._bundled_i2pd_path", return_value=""):
result = ensure_transport_ready("i2p", config=AppConfig(transport_modes={}), root=Path(tmp), status=status)
with patch("ampbrowser.transport_manager.sys.platform", "darwin"):
with patch("ampbrowser.transport_manager.shutil.which", return_value=None):
with patch("ampbrowser.transport_manager._bundled_i2pd_path", return_value=""):
result = ensure_transport_ready("i2p", config=AppConfig(transport_modes={}), root=Path(tmp), status=status)
self.assertEqual("missing-provider", result.status)
self.assertFalse(result.ready)
self.assertIn("I2P provider not found", result.message)
self.assertIn("brew install i2pd", result.message)
self.assertIn("brew install i2pd", result.setup_hint)
self.assertEqual(("brew", "install", "i2pd"), result.install_command)
def test_finds_homebrew_i2pd_provider_when_not_on_path(self) -> None:
status = TransportStatus(