from __future__ import annotations import io import sys import unittest from contextlib import redirect_stderr from html.parser import HTMLParser from types import SimpleNamespace from unittest.mock import Mock, patch from nest.cli import main from nest.server import create_app, run_server from tests.test_server import FASTAPI_AVAILABLE, FakeLifecycle, fake_application from uvm.errors import UvmError if FASTAPI_AVAILABLE: from fastapi.testclient import TestClient class NestCliTests(unittest.TestCase): def test_serve_launches_the_nest_workspace(self) -> None: application = fake_application(api_token="test-token") with ( patch("nest.cli.build_application", return_value=application), patch("nest.server.run_server") as launch, ): result = main(["--serve", "--host", "127.0.0.1", "--port", "8123"]) self.assertEqual(result, 0) launch.assert_called_once_with(application, host="127.0.0.1", port=8123) def test_invalid_invocations_do_not_build_host_services(self) -> None: for arguments in ( [], ["--serve", "list"], ["--serve", "--port", "0"], ["--serve", "--port", "65536"], ["--serve", "--port", "1.5"], ): with self.subTest(arguments=arguments), redirect_stderr(io.StringIO()): with patch("nest.cli.build_application") as build: with self.assertRaises(SystemExit) as raised: main(arguments) self.assertEqual(raised.exception.code, 2) build.assert_not_called() def test_startup_errors_are_reported_as_nest_errors(self) -> None: output = io.StringIO() with ( patch("nest.cli.build_application", side_effect=UvmError("invalid settings")), redirect_stderr(output), ): result = main(["--serve"]) self.assertEqual(result, 1) self.assertEqual(output.getvalue(), "nest: error: invalid settings\n") def test_factory_requires_an_explicit_host(self) -> None: with self.assertRaisesRegex(UvmError, "nest --serve"): create_app(fake_application(api_token="test-token")) def test_workspace_enforces_backend_token_and_tls_requirements(self) -> None: for token, host in ((None, "127.0.0.1"), ("test-token", "0.0.0.0")): for launch in (create_app, run_server): with self.subTest(token=token, host=host, launch=launch.__name__): options = {"port": 8000} if launch is run_server else {} with self.assertRaises(UvmError): launch(fake_application(api_token=token), host=host, **options) class _DashboardAssets(HTMLParser): def __init__(self) -> None: super().__init__() self.urls: list[str] = [] def handle_starttag(self, tag, attrs) -> None: attributes = dict(attrs) if tag == "link" and attributes.get("rel") in {"preload", "stylesheet"}: self.urls.append(attributes["href"]) if tag == "script" and attributes.get("src"): self.urls.append(attributes["src"]) @unittest.skipUnless(FASTAPI_AVAILABLE, "FastAPI and httpx are required for workspace tests") class NestWorkspaceTests(unittest.TestCase): def setUp(self) -> None: self.application = fake_application(api_token="test-token") self.application.lifecycle = FakeLifecycle() self.application.installer = SimpleNamespace(install=Mock()) self.headers = {"X-UVM-Token": "test-token"} self.api = create_app(self.application, host="127.0.0.1") self.client = TestClient(self.api) self.addCleanup(self.client.close) def test_nest_dashboard_and_its_linked_assets_are_public(self) -> None: root = self.client.get("/", follow_redirects=False) self.assertEqual(root.status_code, 307) self.assertEqual(root.headers["location"], "/dashboard") dashboard = self.client.get(root.headers["location"]) self.assertEqual(dashboard.status_code, 200) self.assertIn("