| 16 | |
| 17 | |
| 18 | class ThemeAssetTests(SettingsOverrideMixin, unittest.TestCase): |
| 19 | def get_theme_index_asset(self, theme: str) -> str: |
| 20 | assets = sorted((BASE_DIR / theme / "assets").glob("index-*.js")) |
| 21 | self.assertTrue(assets, f"{theme} 缺少 index JS 资源") |
| 22 | return assets[0].name |
| 23 | |
| 24 | def test_resolves_assets_from_current_theme(self): |
| 25 | settings.themesSelect = "themes/2023" |
| 26 | theme_2023_asset = resolve_theme_file( |
| 27 | "assets", self.get_theme_index_asset("themes/2023") |
| 28 | ) |
| 29 | |
| 30 | settings.themesSelect = "themes/2024" |
| 31 | theme_2024_asset = resolve_theme_file( |
| 32 | "assets", self.get_theme_index_asset("themes/2024") |
| 33 | ) |
| 34 | |
| 35 | self.assertIn("themes/2023/assets", str(theme_2023_asset)) |
| 36 | self.assertIn("themes/2024/assets", str(theme_2024_asset)) |
| 37 | |
| 38 | def test_rejects_theme_asset_path_traversal(self): |
| 39 | settings.themesSelect = "themes/2024" |
| 40 | |
| 41 | with self.assertRaises(HTTPException) as error: |
| 42 | resolve_theme_file("assets", "..", "..", "core", "settings.py") |
| 43 | |
| 44 | self.assertEqual(error.exception.status_code, 404) |
| 45 | |
| 46 | def test_index_keeps_absolute_asset_urls(self): |
| 47 | settings.themesSelect = "themes/2023" |
| 48 | |
| 49 | response = asyncio.run(index()) |
| 50 | html = response.body.decode("utf-8") |
| 51 | |
| 52 | self.assertIn('src="/assets/', html) |
| 53 | self.assertIn('href="/assets/', html) |
nothing calls this directly
no outgoing calls
no test coverage detected