[DTP-Worker 20260731_023000] Auto exec · session 20260731_023000
CI / Contraintes NON-NÉGOCIABLES (CLAUDE.md) (push) Has been cancelled
CI / Validation JSON (schémas Faisabilité) (push) Has been cancelled
CI / Qualité documentaire (liens + 4Big) (push) Has been cancelled
CI / Reproductibilité des artefacts out/ (build == commité) (push) Has been cancelled
CI / Fraîcheur matrice de régression (run == commité) (push) Has been cancelled
CI / Intégrité du câblage CI (gate agrège tout · gates statiques verrouillés) (push) Has been cancelled
CI / Publiciste · parser + schéma + generator (unittest) (push) Has been cancelled
CI / RBAC · 50 rôles + schéma (unittest) (push) Has been cancelled
CI / Faisabilité · générateur 4 volets + round-trip (unittest) (push) Has been cancelled
CI / RBAC · fixtures ERPNext (Role + Custom DocPerm) (push) Has been cancelled
CI / RBAC · plan User Permission (row-level) (push) Has been cancelled
CI / RBAC · Role Profile (bundles par portail) (push) Has been cancelled
CI / RBAC · run-book d'application unifié (agrégat 3 volets) (push) Has been cancelled
CI / Faisabilité · dossier bancable trilingue FR/EN/ES (push) Has been cancelled
CI / CRM · workflow vente ERPNext (lead → CONFOTUR) (push) Has been cancelled
CI / CRM · DocType porteur OTO Dossier Vente (push) Has been cancelled
CI / CRM · barème commissions vendeurs (push) Has been cancelled
CI / Fiscal · e-CF DGII (Compupar) (push) Has been cancelled
CI / Frontend · Workspaces 5 portails rôle (push) Has been cancelled
CI / Legal · DocType CONFOTUR Application (push) Has been cancelled
CI / QA · Audit 5D conformité (push) Has been cancelled
CI / SEO · mots-clés trilingues + schema.org + hreflang (push) Has been cancelled
CI / Chat OTOIA · montage par portail (Custom Block) (push) Has been cancelled
CI / QA · Audit 4Big (95+/100 sur 100% deliverables) (push) Has been cancelled
CI / Démo · Scénarios (run-sheet P07 banquier / P05 client) (push) Has been cancelled
CI / QA · Matrice de régression exhaustive (Sprint 8) (push) Has been cancelled
CI / DevOps · Run-book de déploiement VPS unifié (Sprint 8) (push) Has been cancelled
CI / QA · Matrice d'acceptation / traçabilité MVP (Sprint 8) (push) Has been cancelled
CI / Mobile · config app Expo/EAS (navigation par rôle) (push) Has been cancelled
CI / E2E baseline Playwright (manuel) (push) Has been cancelled
CI / Gate qualité (agrégat) (push) Has been cancelled
CI / Contraintes NON-NÉGOCIABLES (CLAUDE.md) (push) Has been cancelled
CI / Validation JSON (schémas Faisabilité) (push) Has been cancelled
CI / Qualité documentaire (liens + 4Big) (push) Has been cancelled
CI / Reproductibilité des artefacts out/ (build == commité) (push) Has been cancelled
CI / Fraîcheur matrice de régression (run == commité) (push) Has been cancelled
CI / Intégrité du câblage CI (gate agrège tout · gates statiques verrouillés) (push) Has been cancelled
CI / Publiciste · parser + schéma + generator (unittest) (push) Has been cancelled
CI / RBAC · 50 rôles + schéma (unittest) (push) Has been cancelled
CI / Faisabilité · générateur 4 volets + round-trip (unittest) (push) Has been cancelled
CI / RBAC · fixtures ERPNext (Role + Custom DocPerm) (push) Has been cancelled
CI / RBAC · plan User Permission (row-level) (push) Has been cancelled
CI / RBAC · Role Profile (bundles par portail) (push) Has been cancelled
CI / RBAC · run-book d'application unifié (agrégat 3 volets) (push) Has been cancelled
CI / Faisabilité · dossier bancable trilingue FR/EN/ES (push) Has been cancelled
CI / CRM · workflow vente ERPNext (lead → CONFOTUR) (push) Has been cancelled
CI / CRM · DocType porteur OTO Dossier Vente (push) Has been cancelled
CI / CRM · barème commissions vendeurs (push) Has been cancelled
CI / Fiscal · e-CF DGII (Compupar) (push) Has been cancelled
CI / Frontend · Workspaces 5 portails rôle (push) Has been cancelled
CI / Legal · DocType CONFOTUR Application (push) Has been cancelled
CI / QA · Audit 5D conformité (push) Has been cancelled
CI / SEO · mots-clés trilingues + schema.org + hreflang (push) Has been cancelled
CI / Chat OTOIA · montage par portail (Custom Block) (push) Has been cancelled
CI / QA · Audit 4Big (95+/100 sur 100% deliverables) (push) Has been cancelled
CI / Démo · Scénarios (run-sheet P07 banquier / P05 client) (push) Has been cancelled
CI / QA · Matrice de régression exhaustive (Sprint 8) (push) Has been cancelled
CI / DevOps · Run-book de déploiement VPS unifié (Sprint 8) (push) Has been cancelled
CI / QA · Matrice d'acceptation / traçabilité MVP (Sprint 8) (push) Has been cancelled
CI / Mobile · config app Expo/EAS (navigation par rôle) (push) Has been cancelled
CI / E2E baseline Playwright (manuel) (push) Has been cancelled
CI / Gate qualité (agrégat) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
"""Tests du générateur de config app mobile (Sprint 5 · Mobile).
|
||||
|
||||
Stdlib pur (unittest) — aucune dépendance pip. Couvre :
|
||||
- conformité au schéma + les 12 invariants du CLI (chemin nominal) ;
|
||||
- la cross-cohérence RBAC ↔ portails ↔ seo (onglets, rôles, labels/icônes, langues) ;
|
||||
- l'anti-invention (#6) par injections négatives (identifiant de store fabriqué,
|
||||
rôles désynchronisés de la surface RBAC, langue inventée, token de marque altéré).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
_HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
_ROOT = os.path.normpath(os.path.join(_HERE, ".."))
|
||||
sys.path.insert(0, _ROOT)
|
||||
|
||||
import app_config_gen as gen # noqa: E402
|
||||
from mobilelib import builder, deps # noqa: E402
|
||||
|
||||
|
||||
def build():
|
||||
return gen._build_bundle()
|
||||
|
||||
|
||||
class TestNominal(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.bundle = build()
|
||||
|
||||
def test_build_valide(self):
|
||||
self.assertEqual(gen._validate_bundle(self.bundle), [])
|
||||
|
||||
def test_schema_seul(self):
|
||||
schema = deps.load_json(deps.SCHEMA_PATH)
|
||||
self.assertEqual(deps.validate(self.bundle, schema), [])
|
||||
|
||||
def test_onglets_portails_metier_dans_ordre(self):
|
||||
contract = deps.load_contract()
|
||||
spec = deps.load_spec()
|
||||
keys = [n["portail"] for n in self.bundle["role_navigation"]]
|
||||
self.assertEqual(keys, spec["portail_order"])
|
||||
self.assertEqual(set(keys), set(contract["portails_business"]))
|
||||
|
||||
def test_roles_allowed_egal_surface_rbac(self):
|
||||
"""Ancrage sécurité : un onglet ne cible QUE les rôles RBAC de son portail."""
|
||||
contract = deps.load_contract()
|
||||
roles_map = deps.roles_by_portail(contract)
|
||||
for n in self.bundle["role_navigation"]:
|
||||
self.assertEqual(n["roles_allowed"], sorted(roles_map[n["portail"]]))
|
||||
self.assertEqual(n["n_roles"], len(n["roles_allowed"]))
|
||||
self.assertTrue(n["roles_allowed"], f"{n['portail']} sans rôle")
|
||||
|
||||
def test_labels_icones_depuis_portails_spec(self):
|
||||
pspec = deps.load_portails_spec()
|
||||
labels = {p["key"]: p["label"] for p in pspec["portails"]}
|
||||
icons = {p["key"]: p["icon"] for p in pspec["portails"]}
|
||||
for n in self.bundle["role_navigation"]:
|
||||
self.assertEqual(n["label"], labels[n["portail"]])
|
||||
self.assertEqual(n["icon"], icons[n["portail"]])
|
||||
|
||||
def test_langues_depuis_seo(self):
|
||||
langs, default_lang = deps.load_langs()
|
||||
app = self.bundle["app_config"]["expo"]
|
||||
self.assertEqual(sorted(app["locales"].keys()), sorted(langs))
|
||||
self.assertEqual(app["extra"]["defaultLocale"], default_lang)
|
||||
self.assertEqual(self.bundle["manifest"]["langues"]["liste"], list(langs))
|
||||
|
||||
def test_tokens_marque_4(self):
|
||||
app = self.bundle["app_config"]["expo"]
|
||||
self.assertEqual(app["backgroundColor"], deps.branding.COLOR_BG)
|
||||
self.assertEqual(app["splash"]["backgroundColor"], deps.branding.COLOR_BG)
|
||||
self.assertEqual(app["primaryColor"], deps.branding.COLOR_ACCENT)
|
||||
self.assertEqual(app["userInterfaceStyle"], "dark")
|
||||
|
||||
def test_devises_usd_dop(self):
|
||||
app = self.bundle["app_config"]["expo"]
|
||||
self.assertEqual(
|
||||
app["extra"]["devises"],
|
||||
[deps.branding.DEVISE_PRIMAIRE, deps.branding.DEVISE_SECONDAIRE],
|
||||
)
|
||||
|
||||
def test_identifiants_store_null(self):
|
||||
"""Anti-invention : chaque champ a_confirmer reste null (jamais fabriqué)."""
|
||||
spec = deps.load_spec()
|
||||
for entry in spec["a_confirmer"]:
|
||||
found, value = gen._resolve_path(self.bundle, entry["champ"])
|
||||
self.assertTrue(found, f"champ absent: {entry['champ']}")
|
||||
self.assertIsNone(value, f"{entry['champ']} devrait être null")
|
||||
self.assertEqual(self.bundle["manifest"]["identifiants_statut"], "a_confirmer")
|
||||
|
||||
def test_expo_sdk_54(self):
|
||||
spec = deps.load_spec()
|
||||
self.assertEqual(
|
||||
self.bundle["app_config"]["expo"]["extra"]["expoSdkMajor"],
|
||||
spec["app"]["expo_sdk_major"],
|
||||
)
|
||||
|
||||
def test_eas_profils_et_submit_null(self):
|
||||
spec = deps.load_spec()
|
||||
eas = self.bundle["eas_build"]
|
||||
self.assertEqual(sorted(eas["build"].keys()), sorted(spec["eas"]["build_profiles"]))
|
||||
sub = eas["submit"]["production"]
|
||||
self.assertIsNone(sub["ios"]["appleId"])
|
||||
self.assertIsNone(sub["android"]["serviceAccountKeyPath"])
|
||||
|
||||
def test_determinisme(self):
|
||||
a = json.dumps(build(), sort_keys=True, ensure_ascii=False)
|
||||
b = json.dumps(build(), sort_keys=True, ensure_ascii=False)
|
||||
self.assertEqual(a, b)
|
||||
|
||||
|
||||
class TestAntiInvention(unittest.TestCase):
|
||||
"""Injections négatives : chaque garde-fou doit MORDRE (invariant → erreur)."""
|
||||
|
||||
def setUp(self):
|
||||
self.bundle = build()
|
||||
|
||||
def test_identifiant_fabrique_detecte(self):
|
||||
bad = copy.deepcopy(self.bundle)
|
||||
bad["app_config"]["expo"]["ios"]["bundleIdentifier"] = "com.oto.inventé"
|
||||
errs = gen._validate_bundle(bad)
|
||||
self.assertTrue(any("null" in e for e in errs), errs)
|
||||
|
||||
def test_roles_desync_detecte(self):
|
||||
bad = copy.deepcopy(self.bundle)
|
||||
bad["role_navigation"][0]["roles_allowed"] = ["OTO Rôle Inventé"]
|
||||
errs = gen._validate_bundle(bad)
|
||||
self.assertTrue(any("roles_allowed" in e for e in errs), errs)
|
||||
|
||||
def test_langue_inventee_detecte(self):
|
||||
bad = copy.deepcopy(self.bundle)
|
||||
bad["app_config"]["expo"]["locales"]["de"] = None
|
||||
errs = gen._validate_bundle(bad)
|
||||
self.assertTrue(any("locales" in e for e in errs), errs)
|
||||
|
||||
def test_token_marque_altere_detecte(self):
|
||||
bad = copy.deepcopy(self.bundle)
|
||||
bad["app_config"]["expo"]["primaryColor"] = "#ffffff"
|
||||
errs = gen._validate_bundle(bad)
|
||||
self.assertTrue(any("primaryColor" in e for e in errs), errs)
|
||||
|
||||
def test_ordre_onglets_altere_detecte(self):
|
||||
bad = copy.deepcopy(self.bundle)
|
||||
bad["role_navigation"].reverse()
|
||||
errs = gen._validate_bundle(bad)
|
||||
self.assertTrue(any("ordre des onglets" in e for e in errs), errs)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user