|
6 | 6 | from pathlib import Path |
7 | 7 | from tempfile import TemporaryDirectory |
8 | 8 | from typing import TYPE_CHECKING, Any, cast |
| 9 | +from unittest.mock import Mock, patch |
9 | 10 |
|
10 | 11 | from marimo._server.api.deps import AppState |
11 | 12 | from marimo._server.api.endpoints.assets import _inject_service_worker |
@@ -217,3 +218,80 @@ def test_inject_service_worker() -> None: |
217 | 218 | "const notebookId = 'c%3A%5Cpath%5Cto%5Cnotebook.py';" |
218 | 219 | in _inject_service_worker("<body></body>", r"c:\path\to\notebook.py") |
219 | 220 | ) |
| 221 | + |
| 222 | + |
| 223 | +def test_index_with_missing_local_file_and_asset_url( |
| 224 | + client: TestClient, |
| 225 | +) -> None: |
| 226 | + """Test that index.html is fetched from asset_url when local file doesn't exist.""" |
| 227 | + app_state = AppState.from_app(cast(Any, client.app)) |
| 228 | + |
| 229 | + # Mock HTML content that would come from the CDN |
| 230 | + mock_html = """<!DOCTYPE html> |
| 231 | +<html> |
| 232 | +<head><title>{{ title }}</title></head> |
| 233 | +<body> |
| 234 | +<marimo-filename hidden>{{ filename }}</marimo-filename> |
| 235 | +'{{ mount_config }}' |
| 236 | +</body> |
| 237 | +</html>""" |
| 238 | + |
| 239 | + # Mock the requests.get to return our mock HTML |
| 240 | + mock_response = Mock() |
| 241 | + mock_response.text.return_value = mock_html |
| 242 | + mock_response.raise_for_status.return_value = None |
| 243 | + |
| 244 | + with ( |
| 245 | + patch("marimo._server.api.endpoints.assets.root") as mock_root, |
| 246 | + patch("marimo._utils.requests.get", return_value=mock_response), |
| 247 | + ): |
| 248 | + # Make local index.html appear to not exist |
| 249 | + mock_index_html = Mock() |
| 250 | + mock_index_html.exists.return_value = False |
| 251 | + mock_root.__truediv__.return_value = mock_index_html |
| 252 | + |
| 253 | + # Set asset_url on the app state |
| 254 | + client.app.state.asset_url = "https://cdn.example.com/assets/0.1.0" |
| 255 | + |
| 256 | + response = client.get("/", headers=token_header()) |
| 257 | + assert response.status_code == 200, response.text |
| 258 | + # The response should contain processed HTML |
| 259 | + assert "<title>" in response.text |
| 260 | + |
| 261 | + |
| 262 | +def test_index_with_missing_local_file_no_asset_url( |
| 263 | + client: TestClient, |
| 264 | +) -> None: |
| 265 | + """Test that error is raised when local file doesn't exist and no asset_url.""" |
| 266 | + with patch("marimo._server.api.endpoints.assets.root") as mock_root: |
| 267 | + # Make local index.html appear to not exist |
| 268 | + mock_index_html = Mock() |
| 269 | + mock_index_html.exists.return_value = False |
| 270 | + mock_root.__truediv__.return_value = mock_index_html |
| 271 | + |
| 272 | + # Ensure asset_url is None |
| 273 | + client.app.state.asset_url = None |
| 274 | + |
| 275 | + response = client.get("/", headers=token_header()) |
| 276 | + assert response.status_code == 500 |
| 277 | + assert "index.html not found" in response.json()["detail"] |
| 278 | + |
| 279 | + |
| 280 | +def test_index_prefers_local_file_over_asset_url(client: TestClient) -> None: |
| 281 | + """Test that local index.html is preferred even when asset_url is set.""" |
| 282 | + # Set asset_url on the app state |
| 283 | + client.app.state.asset_url = "https://cdn.example.com/assets/0.1.0" |
| 284 | + |
| 285 | + # Mock requests.get to track if it's called |
| 286 | + mock_response = Mock() |
| 287 | + with patch( |
| 288 | + "marimo._utils.requests.get", return_value=mock_response |
| 289 | + ) as mock_get: |
| 290 | + response = client.get("/", headers=token_header()) |
| 291 | + assert response.status_code == 200, response.text |
| 292 | + |
| 293 | + # requests.get should NOT be called since local file exists |
| 294 | + mock_get.assert_not_called() |
| 295 | + |
| 296 | + # Reset asset_url |
| 297 | + client.app.state.asset_url = None |
0 commit comments