120 lines
4.1 KiB
Python
120 lines
4.1 KiB
Python
"""Prüft Assets und Inhalte: Lizenzdokumentation vollständig, Verweise gültig,
|
||
SVGs harmlos, keine Namen aus anderen Spielen oder Filmen."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import re
|
||
import xml.etree.ElementTree as ET
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
|
||
from app.config import PROJECT_ROOT
|
||
|
||
STATIC = PROJECT_ROOT / "app" / "static"
|
||
ASSETS = STATIC / "assets"
|
||
ASSETS_DOC = PROJECT_ROOT / "docs" / "ASSETS.md"
|
||
JS_ROOT = STATIC / "js"
|
||
|
||
#: Begriffe aus Helldivers bzw. Starship Troopers, die im Spiel nicht vorkommen dürfen
|
||
#: (Groß-/Kleinschreibung egal). Der allgemeine Tonfall darf inspiriert sein, Namen nicht.
|
||
FORBIDDEN_TERMS = [
|
||
"helldiver",
|
||
"super earth",
|
||
"super-erde",
|
||
"supererde",
|
||
"stratagem",
|
||
"hellpod",
|
||
"terminid",
|
||
"automaton",
|
||
"illuminate",
|
||
"managed democracy",
|
||
"gelenkte demokratie",
|
||
"liber-tea",
|
||
"would you like to know more",
|
||
"möchten sie mehr erfahren",
|
||
"mobile infantry",
|
||
"arachnid",
|
||
"federation",
|
||
"föderation",
|
||
"service guarantees citizenship",
|
||
"rico",
|
||
"klendathu",
|
||
]
|
||
|
||
|
||
def _asset_files() -> list[Path]:
|
||
return sorted(p for p in ASSETS.rglob("*") if p.is_file())
|
||
|
||
|
||
def test_asset_folder_is_not_empty() -> None:
|
||
assert _asset_files(), "Keine Assets gefunden"
|
||
|
||
|
||
@pytest.mark.parametrize("path", _asset_files(), ids=lambda p: p.relative_to(ASSETS).as_posix())
|
||
def test_every_asset_is_documented(path: Path) -> None:
|
||
documented = ASSETS_DOC.read_text(encoding="utf-8")
|
||
relative = path.relative_to(ASSETS).as_posix()
|
||
assert relative in documented, f"{relative} fehlt in docs/ASSETS.md"
|
||
|
||
|
||
def test_referenced_asset_files_exist() -> None:
|
||
manifest = (JS_ROOT / "systems" / "Assets.js").read_text(encoding="utf-8")
|
||
referenced = re.findall(r"file: '([^']+)'", manifest)
|
||
assert len(referenced) >= 15
|
||
missing = [f for f in referenced if not (ASSETS / f).is_file()]
|
||
assert missing == []
|
||
|
||
|
||
@pytest.mark.parametrize("path", sorted(ASSETS.rglob("*.svg")), ids=lambda p: p.name)
|
||
def test_svgs_are_well_formed_and_self_contained(path: Path) -> None:
|
||
source = path.read_text(encoding="utf-8")
|
||
root = ET.fromstring(source) # noqa: S314 – eigene, versionierte Dateien
|
||
assert root.tag.endswith("svg")
|
||
lowered = source.lower()
|
||
# Keine Skripte, keine externen Verweise: SVGs sollen reine Grafik sein.
|
||
assert "<script" not in lowered
|
||
assert "http://" not in lowered.replace("http://www.w3.org/2000/svg", "")
|
||
assert "https://" not in lowered
|
||
assert "<image" not in lowered
|
||
|
||
|
||
def test_phaser_is_vendored_locally() -> None:
|
||
phaser = JS_ROOT / "lib" / "phaser.min.js"
|
||
assert phaser.is_file()
|
||
assert phaser.stat().st_size > 500_000
|
||
head = phaser.read_text(encoding="utf-8", errors="ignore")[:100_000]
|
||
assert '"3.90.0"' in head or "3.90.0" in head
|
||
assert (JS_ROOT / "lib" / "PHASER_LICENSE.md").is_file()
|
||
|
||
|
||
def test_no_external_urls_in_page_or_scripts() -> None:
|
||
"""Das Spiel lädt nichts von fremden Servern (kein CDN, kein Tracking)."""
|
||
offenders = []
|
||
for path in [*JS_ROOT.rglob("*.js"), *(PROJECT_ROOT / "app" / "templates").rglob("*.html")]:
|
||
if "lib" in path.relative_to(STATIC if path.is_relative_to(STATIC) else PROJECT_ROOT).parts:
|
||
continue
|
||
text = path.read_text(encoding="utf-8")
|
||
for match in re.findall(r"""(?:src|href)=["'](https?://[^"']+)""", text):
|
||
offenders.append(f"{path.name}: {match}")
|
||
if re.search(r"""\b(?:import|fetch)\s*\(?\s*["']https?://""", text):
|
||
offenders.append(f"{path.name}: externer Import/Fetch")
|
||
assert offenders == []
|
||
|
||
|
||
def test_no_names_from_other_games_or_films() -> None:
|
||
sources = [
|
||
*JS_ROOT.rglob("*.js"),
|
||
*(PROJECT_ROOT / "app" / "templates").rglob("*.html"),
|
||
*ASSETS.rglob("*.svg"),
|
||
]
|
||
hits = []
|
||
for path in sources:
|
||
if "lib" in path.parts:
|
||
continue
|
||
text = path.read_text(encoding="utf-8").lower()
|
||
for term in FORBIDDEN_TERMS:
|
||
if re.search(rf"\b{re.escape(term)}\b", text):
|
||
hits.append(f"{path.relative_to(PROJECT_ROOT).as_posix()}: {term}")
|
||
assert hits == []
|