Newer
Older
navi-1 / tests / unit / test_deployment_flags.py
"""Deployment flags: NAVI_WEBCLIENT_ENABLED gating and navi-server settings.

The webclient gating in navi/main.py runs at import time, so these tests
launch a subprocess with the flag in the environment and inspect the
registered routes — monkeypatching settings in-process would not re-gate.
"""

from __future__ import annotations

import json
import os
import subprocess
import sys

from navi.config import Settings

ROUTES_SCRIPT = """
import json
import navi.main as m
print(json.dumps(sorted({r.path for r in m.app.routes})))
"""


def _app_routes(extra_env: dict[str, str]) -> list[str]:
    env = {k: v for k, v in os.environ.items() if k != "NAVI_WEBCLIENT_ENABLED"}
    env.update(extra_env)
    out = subprocess.run(
        [sys.executable, "-c", ROUTES_SCRIPT],
        capture_output=True, text=True, env=env, cwd=os.path.dirname(os.path.dirname(os.path.dirname(__file__))),
        timeout=120,
    )
    assert out.returncode == 0, out.stderr[-2000:]
    # Import-time logging (profile loading etc.) shares stdout — the JSON
    # payload is the last line printed by the script itself.
    return json.loads(out.stdout.strip().splitlines()[-1])


def test_settings_defaults():
    s = Settings(database_url="postgresql://x")
    assert s.navi_webclient_enabled is True
    assert s.navi_host == "127.0.0.1"
    assert s.navi_port == 8000


def test_webclient_enabled_default_registers_ui_routes():
    routes = _app_routes({"NAVI_WEBCLIENT_ENABLED": "true"})
    # Web panel, static mounts and the debug tools are all present.
    assert "/" in routes
    assert "/admin" in routes
    assert "/health" in routes
    assert "/ws/sessions/{session_id}" in routes


def test_webclient_disabled_drops_ui_routes_only():
    routes = _app_routes({"NAVI_WEBCLIENT_ENABLED": "false"})
    # Everything web-facing is gone …
    assert "/" not in routes
    assert "/admin" not in routes
    assert "/debug" not in routes
    assert not any(r.startswith(("/assets", "/images", "/content")) for r in routes)
    # … but the API the terminal client uses is fully intact.
    assert "/health" in routes
    assert "/sessions" in routes
    assert "/ws/sessions/{session_id}" in routes
    assert "/api/sessions/{session_id}/files/{filename}" in routes
    # The /admin/* JSON API (profile/memory/sessions management) stays —
    # only the HTML panel is web UI. require_admin still gates it.
    assert "/admin/profiles" in routes