from pathlib import Path from fastapi.testclient import TestClient from reyna_cli.web import WebSettings, create_app, normalize_args, RunRequest, run_web_server from reyna_cli.tts import TTSError, _custom_tts def test_web_health_without_auth_config(): app = create_app( WebSettings( auth_enabled=True, dev_auth=False, session_secret="", authentik_issuer="https://auth.reynafamily.com/application/o/reyna-cli-web/", client_id=None, client_secret=None, basic_user=None, basic_password=None, public_url=None, command_timeout=5, output_limit=1000, workdir=Path.cwd(), ) ) client = TestClient(app) response = client.get("/healthz") assert response.status_code == 200 assert response.json()["auth_enabled"] is True assert response.json()["oidc_configured"] is False def test_web_index_renders_with_dev_auth(): app = create_app( WebSettings( auth_enabled=True, dev_auth=True, session_secret="dev-secret", authentik_issuer="https://auth.reynafamily.com/application/o/reyna-cli-web/", client_id=None, client_secret=None, basic_user=None, basic_password=None, public_url=None, command_timeout=5, output_limit=1000, workdir=Path.cwd(), ) ) client = TestClient(app) response = client.get("/") assert response.status_code == 200 assert "Reyna CLI Web" in response.text assert "doctor --json" in response.text def test_web_refuses_dev_auth_on_lan_bind(monkeypatch, tmp_path): monkeypatch.setenv("HERMES_ENV_PATH", str(tmp_path / "missing.env")) monkeypatch.setenv("REYNA_WEB_DEV_AUTH", "1") monkeypatch.delenv("REYNA_WEB_ALLOW_INSECURE_LAN", raising=False) try: run_web_server(host="0.0.0.0", port=8765) except SystemExit as exc: assert "Refusing to run" in str(exc) else: raise AssertionError("LAN dev-auth server was allowed") def test_web_refuses_lan_bind_without_oidc_config(monkeypatch, tmp_path): monkeypatch.setenv("HERMES_ENV_PATH", str(tmp_path / "missing.env")) monkeypatch.delenv("REYNA_WEB_DEV_AUTH", raising=False) monkeypatch.delenv("REYNA_WEB_AUTH_CLIENT_ID", raising=False) monkeypatch.delenv("REYNA_WEB_AUTH_CLIENT_SECRET", raising=False) monkeypatch.delenv("REYNA_WEB_BASIC_USER", raising=False) monkeypatch.delenv("REYNA_WEB_BASIC_PASSWORD", raising=False) monkeypatch.delenv("REYNA_WEB_SESSION_SECRET", raising=False) try: run_web_server(host="0.0.0.0", port=8765) except SystemExit as exc: assert "without configured authentication" in str(exc) else: raise AssertionError("LAN server without OIDC config was allowed") def test_web_basic_auth_protects_index(): app = create_app( WebSettings( auth_enabled=True, dev_auth=False, session_secret="basic-secret", authentik_issuer="https://auth.reynafamily.com/application/o/reyna-cli-web/", client_id=None, client_secret=None, basic_user="adolfo", basic_password="correct-password", public_url=None, command_timeout=5, output_limit=1000, workdir=Path.cwd(), ) ) client = TestClient(app) unauthenticated = client.get("/") assert unauthenticated.status_code == 401 assert unauthenticated.headers["www-authenticate"] == 'Basic realm="Reyna CLI Web"' authenticated = client.get("/", auth=("adolfo", "correct-password")) assert authenticated.status_code == 200 assert "HTTP Basic auth" in authenticated.text def test_web_allows_lan_bind_with_basic_auth(monkeypatch, tmp_path): monkeypatch.setenv("HERMES_ENV_PATH", str(tmp_path / "missing.env")) called = {} def fake_run(app_path, host, port, reload): called.update({"app_path": app_path, "host": host, "port": port, "reload": reload}) monkeypatch.delenv("REYNA_WEB_DEV_AUTH", raising=False) monkeypatch.delenv("REYNA_WEB_AUTH_CLIENT_ID", raising=False) monkeypatch.delenv("REYNA_WEB_AUTH_CLIENT_SECRET", raising=False) monkeypatch.setenv("REYNA_WEB_BASIC_USER", "adolfo") monkeypatch.setenv("REYNA_WEB_BASIC_PASSWORD", "correct-password") monkeypatch.setattr("uvicorn.run", fake_run) run_web_server(host="0.0.0.0", port=8765) assert called["host"] == "0.0.0.0" def test_web_api_run_uses_argument_vector(monkeypatch): app = create_app( WebSettings( auth_enabled=False, dev_auth=False, session_secret="dev-secret", authentik_issuer="https://auth.reynafamily.com/application/o/reyna-cli-web/", client_id=None, client_secret=None, basic_user=None, basic_password=None, public_url=None, command_timeout=5, output_limit=1000, workdir=Path.cwd(), ) ) def fake_run(args, settings): return {"ok": True, "args": args, "exit_code": 0, "stdout": "done", "stderr": ""} monkeypatch.setattr("reyna_cli.web.run_reyna_cli", fake_run) client = TestClient(app) response = client.post("/api/run", json={"command": "doctor --json"}) assert response.status_code == 200 payload = response.json() assert payload["ok"] is True assert payload["args"] == ["doctor", "--json"] assert payload["stdout"] == "done" def test_normalize_args_refuses_nested_server(): try: normalize_args(RunRequest(command="--help web")) except Exception as exc: assert getattr(exc, "status_code", None) == 400 else: raise AssertionError("nested web command was not rejected") def test_custom_tts_rejects_raw_text_placeholder(tmp_path): try: _custom_tts("hello", tmp_path / "audio", "printf {raw_text}", None) except TTSError as exc: assert "raw_text" in str(exc) else: raise AssertionError("raw_text placeholder was not rejected")