from __future__ import annotations import logging import os import tempfile import unittest from pathlib import Path from fastapi import FastAPI from fastapi.testclient import TestClient from backend.frontend_assets import ( build_frontend_asset_version, register_frontend_assets, resolve_frontend_assets, ) class FrontendAssetsTests(unittest.TestCase): def _build_frontend_fixture(self, project_root: Path) -> None: frontend_root = project_root / "frontend" frontend_root.mkdir(parents=True, exist_ok=True) files: dict[str, str] = { "index.html": "v=__FRONTEND_ASSET_VERSION__", "app.js": "console.log('app');", "forecast-models.js": "console.log('forecast-models');", "workspace.js": "console.log('workspace');", "workspace.css": "body { color: #fff; }", "favicon.svg": "", } for file_name, content in files.items(): (frontend_root / file_name).write_text(content, encoding="utf-8") (frontend_root / "AIBG.png").write_bytes(b"\x89PNG\r\n\x1a\n") def test_asset_version_changes_when_forecast_model_registry_changes(self) -> None: with tempfile.TemporaryDirectory() as temp_dir: project_root = Path(temp_dir) self._build_frontend_fixture(project_root) assets = resolve_frontend_assets(project_root) initial_version = build_frontend_asset_version( assets=assets, app_version="1.0.0", cache_version="v1", ) assets.forecast_models_js.write_text("console.log('updated');", encoding="utf-8") new_mtime = assets.forecast_models_js.stat().st_mtime + 5 os.utime(assets.forecast_models_js, (new_mtime, new_mtime)) updated_version = build_frontend_asset_version( assets=assets, app_version="1.0.0", cache_version="v1", ) self.assertNotEqual(initial_version, updated_version) def test_register_frontend_assets_serves_forecast_registry_with_no_cache_headers(self) -> None: with tempfile.TemporaryDirectory() as temp_dir: project_root = Path(temp_dir) self._build_frontend_fixture(project_root) app = FastAPI() register_frontend_assets( app, project_root=project_root, app_version="1.0.0", cache_version="v1", logger=logging.getLogger("test-frontend-assets"), ) client = TestClient(app) response = client.get("/forecast-models.js") self.assertEqual(response.status_code, 200) self.assertIn("forecast-models", response.text) self.assertEqual( response.headers["Cache-Control"], "no-store, no-cache, must-revalidate, max-age=0", ) def test_register_frontend_assets_replaces_version_placeholder_in_index(self) -> None: with tempfile.TemporaryDirectory() as temp_dir: project_root = Path(temp_dir) self._build_frontend_fixture(project_root) app = FastAPI() register_frontend_assets( app, project_root=project_root, app_version="2.0.0", cache_version="v9", logger=logging.getLogger("test-frontend-assets"), ) client = TestClient(app) response = client.get("/") self.assertEqual(response.status_code, 200) self.assertNotIn("__FRONTEND_ASSET_VERSION__", response.text) self.assertIn("2.0.0-v9-", response.text) if __name__ == "__main__": unittest.main()