fix: A30 + A37 + A39 + A40 + A41 — fin du P2/P3 XS/S (v7.4.0)
- A30 — `require_scope()` câblé : 69 sites stricts de api_v2.py passent par la factory (Bearer + scope en 1 appel, contrôle manuel supprimé) ; sémantique alignée sur celle des handlers (pas de default "read" → 0 changement de comportement) ; 12 top-level morts supprimés (0 ref app ET tests) : unsync_block, find_referring, _b64url, strip_markdown, format_number, get_auto_property_value, get_next_unique_id, local_date_in_tz, verify_device_token, _get_dynamic_groups, _require_user_gitea, validate_upload_request - A37 — CORS sans `*` : origines = app_base_url + allow_origin_regex (localhost/dev, origines d'extension pour le Web Clipper), méthodes et entêtes minutées, allow_credentials explicite + test test_cors_no_star - A39 — htmx : décision « rien » documentée (32 attributs hx-* réels sur 6 templates, conversion = refonte du view-switching sans test E2E) - A40 — version d'assets à source unique : ENV.globals["asset_version"] lu au boot depuis le fichier VERSION ; littéraux `?v=` de base.html éliminés ; test test_asset_version_single_source - A41 — app.css : 91 règles mortes purgées (-10 274 octets, 121 618 → 111 344), scan templates/JS/CSS/Python à 0 référence suite **1031/1031** · `ruff check app tests` OK · OpenAPI 511 chemins / 7.4.0 docs (ROADMAP/CHANGELOG/WORKLOAD/VERSION) à jour
This commit is contained in:
@@ -65,6 +65,35 @@ def test_rate_limit_key_and_prune():
|
||||
assert "old" not in mw._store and "fresh" in mw._store
|
||||
|
||||
|
||||
def test_cors_no_star(client):
|
||||
"""A37 : plus de `*` — origine refusée n'a pas d'ACAO, origine autorisée oui."""
|
||||
r = client.get("/api/health", headers={"Origin": "https://evil.example"})
|
||||
assert "access-control-allow-origin" not in r.headers
|
||||
r2 = client.get("/api/health", headers={"Origin": "http://localhost:8080"})
|
||||
assert r2.headers.get("access-control-allow-origin") == "http://localhost:8080"
|
||||
|
||||
|
||||
def test_asset_version_single_source():
|
||||
"""A40 : une seule source de version d'assets = le fichier VERSION."""
|
||||
import re as _re
|
||||
from pathlib import Path
|
||||
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
version = (root / "VERSION").read_text(encoding="utf-8").strip()
|
||||
from app.templating import ASSET_VERSION, ENV
|
||||
|
||||
assert ASSET_VERSION == version
|
||||
assert ENV.globals["asset_version"] == version
|
||||
src = (root / "app/templates/base.html").read_text(encoding="utf-8")
|
||||
assert "app.css?v={{ asset_version }}" in src
|
||||
assert "app.js?v={{ asset_version }}" in src
|
||||
# plus aucun littéral de version première main dans les templates
|
||||
literals = _re.findall(
|
||||
r"(?:app|design-tokens|components|offline|flowdeck)\.(?:css|js)\?v=\d", src
|
||||
)
|
||||
assert literals == [], literals
|
||||
|
||||
|
||||
def test_no_duplicate_routes():
|
||||
"""A24 : deux routes même méthode+chemin → l'une écrase silencieusement l'autre."""
|
||||
from app.main import app
|
||||
|
||||
Reference in New Issue
Block a user