## Objectif Ce document liste les tests à prévoir pour un pilote du `Content Publication Engine`. Le but n'est pas seulement de vérifier que les JSON sont générés. Le but est de valider que l'architecture est fiable dans les cas réels: - export Louise/Mogador incomplet; - bug fournisseur; - produit retiré; - correction d'urgence; - vidéo JWP manquante; - cache CDN; - rollback; - run partiellement généré; - consommation par un site fournisseur. ## Principe du pilote Le pilote devrait être limité à une plateforme ou à un sous-ensemble. Options recommandées: ```text Option 1: ONFR - plus petit volume; - plus facile à valider; - bon premier test technique. Option 2: sous-ensemble TFO - plus proche des enjeux réels; - permet de tester collections/saisons/épisodes; - plus représentatif pour la production. ``` Le pilote ne devrait pas remplacer Directus ou la synchro actuelle immédiatement. Il devrait tourner en parallèle. ## Critères de réussite - Le CPE génère un run JSON complet et valide. - `manifest.json` pointe seulement vers un run validé. - Les sites peuvent consommer le feed via manifest + fichiers JSON. - Une correction d'urgence peut être publiée rapidement. - Un rollback peut être exécuté rapidement. - Un produit inchangé n'est pas écrasé par une donnée source suspecte. - Les anomalies sont visibles avant les plaintes de production. - Les performances sont suffisantes pour la plateforme pilote. - Le format JSON est validé par au moins un fournisseur/site consommateur. ## Tests d'ingestion source |Test|Résultat attendu| |---|---| |Export SQL reçu correctement|Le CPE détecte le nouvel export ou son statut d'import| |Export SQL absent|Alerte, aucun nouveau run publié| |Export SQL trop petit|Alerte, aucun run publié sans validation manuelle| |Export SQL identique au précédent|Run ignoré ou marqué sans changement| |Import MySQL réussi|Snapshot peut commencer| |Import MySQL échoué|Alerte, manifest inchangé| |Mogador Toolkit indisponible|Alerte, manifest inchangé| |Réponses API lentes|Retry/backoff, rapport de lenteur| |Réponses API partielles|Anomalie, pas d'écrasement massif| ## Tests de snapshot et diff |Test|Résultat attendu| |---|---| |Nouveau produit|Produit présent dans `latest.added` et fichier produit généré| |Produit modifié|Produit présent dans `latest.updated` et fichier produit régénéré| |Produit supprimé|Produit absent des indexes, présent dans `latest.removed`| |Programmation ajoutée|`today.json` ou `schedule/YYYY-MM-DD.json` mis à jour| |Programmation modifiée|`changed_fields` indique le changement| |Programmation supprimée|Programme absent du schedule concerné| |Image modifiée|Produit ou collection impactée régénérée| |Média modifié|Produit/collection impactée régénérée| |Aucun changement|Nouveau run optionnel, ou aucun run publié| ## Tests de publication incrémentale protégée |Test|Résultat attendu| |---|---| |Run 1 contient 8 000 produits, run 2 ajoute 10 produits|Le CPE traite les 10 nouveaux et conserve les fichiers inchangés| |Run 2 retire 5 produits|Les 5 produits disparaissent des indexes du run 2| |Produit inchangé dans le snapshot|Le dernier JSON valide est conservé| |Produit inchangé devient soudainement incomplet|Anomalie, ancien JSON valide conservé| |Bug source sur plusieurs anciens produits|Alerte globale, pas d'écrasement massif| |Trop d'anomalies dans un run|Run bloqué ou publié avec seuil contrôlé| |Diff incohérent|Manifest inchangé, rapport d'erreur| Point à ne pas oublier: il faut définir des seuils. Exemples: ```text Si plus de 5% des produits deviennent incomplets sans raison connue: -> bloquer le run Si 1 produit nouveau est incomplet: -> publier avec avertissement ou exclure selon règle métier Si un produit prévu aujourd'hui à 6h est incomplet: -> alerte critique ``` ## Tests de génération JSON |Test|Résultat attendu| |---|---| |`manifest.json` généré|Contient `schema_version`, `platform`, `run_id`, `base_path`, `published_at`| |`today.json` généré|Contient les programmes légers du jour| |`schedule/YYYY-MM-DD.json` généré|Contient les programmes de la date| |Schedule J+10 généré|Les 10 prochains jours sont disponibles| |`products/{product_key}.json` généré|Contient produit, programmations, média, images| |`collections/{biznumber}/full.json` généré|Contient collection -> saisons -> épisodes| |Produit multi-plateforme|Chaque plateforme a sa version contextualisée| |Champs obligatoires manquants|Validation échoue ou anomalie créée| |JSON invalide|Run non publié| |JSON Schema échoue|Run non publié| ## Tests de fichiers et chemins |Test|Résultat attendu| |---|---| |Chemins sans caractères problématiques|Aucun espace ou caractère ambigu dans les paths| |`product_key` numérique ou string|Convention documentée et stable| |`biznumber` avec zéros en début|Zéros conservés| |Produit absent|Retour HTTP attendu défini, ex. 404| |Ancien run consulté|Disponible pendant la rétention| |Run supprimé par rétention|Plus accessible après purge contrôlée| |Chemins de liens dans `today.json`|Pointent vers le bon `base_path`| ## Tests de publication atomique |Test|Résultat attendu| |---|---| |Run complet généré avec succès|Fichiers uploadés, validation OK, manifest mis à jour| |Run échoue pendant génération|Manifest reste sur ancien run| |Run échoue pendant upload|Manifest reste sur ancien run| |Run échoue après upload partiel|Manifest reste sur ancien run, fichiers incomplets nettoyés ou ignorés| |Manifest upload échoue|Ancien manifest reste actif| |Manifest pointe vers run inexistant|Détecté par validation pré-publication| |Deux runs simultanés|Lock empêche collision ou ordre déterministe| ## Tests de cache CDN |Test|Résultat attendu| |---|---| |`manifest.json` cache court|Nouveau run visible après TTL attendu| |Fichiers de run cache long|Fichiers immuables servis efficacement| |Rollback manifest|Site revient au run précédent après TTL/purge| |Purge manifest CDN|Nouveau manifest visible rapidement| |Fichier immutable modifié par erreur|Test doit échouer, les runs sont immuables| |Site garde un ancien manifest|Comportement acceptable documenté| Point facile à oublier: les sites doivent gérer le cas où le manifest change pendant une navigation. Recommandation: ```text Une page devrait utiliser un seul run_id pendant son rendu. Elle ne devrait pas mélanger des fichiers de deux runs différents dans la même réponse. ``` ## Tests de rollback |Test|Résultat attendu| |---|---| |Rollback vers run précédent|Manifest repointe vers le run précédent| |Rollback après bug source|Anciens JSON valides disponibles| |Rollback après mauvais override|Override désactivé, run précédent ou nouveau run correct publié| |Rollback audité|Qui, quand, pourquoi enregistré| |Rollback communiqué au site|Site détecte nouveau `run_id`| ## Tests de Publication Console |Test|Résultat attendu| |---|---| |Login édimestre|Accès autorisé seulement en interne| |Rôle read-only|Consultation seulement| |Rôle édimestre|Peut créer override autorisé| |Rôle admin|Peut approuver, rollback, republier| |Override avec expiration|Désactivation automatique après expiration| |Override sans raison|Refusé| |Override sans audit|Refusé| |Override produit|`publication_job` ciblé créé| |Override collection|Collection et indexes impactés régénérés| |Override supprimé|Nouveau run revient aux données source| |Conflit override vs nouveau snapshot|Rapport d'écart généré| Point facile à oublier: un override doit avoir une durée de vie. Sans expiration, la Publication Console devient un CMS parallèle et on recrée le problème de gouvernance. ## Tests Media Pipeline / JWP |Test|Résultat attendu| |---|---| |Vidéo prête|`media_assets.status = ready`, produit republié| |Vidéo manquante|`media_assets.status = missing`, alerte si critique| |Vidéo en encodage|`processing`, JSON indique un état contrôlé| |Vidéo échouée|`failed`, rapport visible| |Poster manquant|Anomalie non bloquante ou bloquante selon règle| |Sous-titres manquants|Anomalie selon règle d'accessibilité| |Media Pipeline down|CPE continue avec dernier `media_assets` connu| |JWP indisponible|Pas d'écrasement des anciens médias valides| |`publication_job media_ready`|Produit/collection impactés republiés| Point facile à oublier: le CPE ne devrait pas appeler JWP directement pour construire chaque JSON. Il doit lire l'état interne `media_assets`. ## Tests de sécurité et accès |Test|Résultat attendu| |---|---| |App interne non exposée Internet|Accès bloqué hors réseau/VPN/SSO| |Bucket par plateforme ou préfixe|Chaque fournisseur voit seulement son feed| |Secrets absents des JSON|Aucun token, URL signée privée ou donnée interne sensible| |`product_key` visible selon règle|Confirmer ce qui peut être exposé aux fournisseurs| |Logs sans secret|Aucun credential dans logs/rapports| |Accès écriture bucket limité|Seul le CPE peut publier| |Suppression accidentelle bucket|Protection IAM ou versioning bucket| Point facile à oublier: les JSON publics ne doivent pas contenir d'information réservée à l'équipe interne. ## Tests d'observabilité |Test|Résultat attendu| |---|---| |Rapport par run|Disponible après chaque tentative| |Rapport attendu vs publié|Écarts listés clairement| |Alerte export absent|Envoyée rapidement| |Alerte programme 6h absent|Critique avant l'heure de diffusion| |Alerte queue bloquée|Visible avant impact site| |Alerte trop d'anomalies|Run bloqué ou escaladé| |Dashboard dernier run|Plateforme, statut, durée, anomalies| |Logs corrélés par `run_id`|Diagnostic possible rapidement| ## Tests de performance |Test|Résultat attendu| |---|---| |Génération complète pilote|Durée mesurée| |Génération incrémentale 10 produits|Beaucoup plus rapide qu'une génération complète| |Publication 8 000 produits|Durée upload mesurée| |Publication 22 000 produits|Estimation IDELLO réaliste| |Lecture manifest CDN|Latence faible| |Lecture produit CDN|Latence faible| |Lecture collection full|Taille et latence acceptables| |Search index|Taille acceptable ou moteur dédié requis| Mesures à collecter: ```text durée extraction source durée diff durée génération JSON durée validation JSON Schema durée upload durée publication manifest nombre de fichiers taille totale nombre d'anomalies temps de rollback ``` ## Tests avec fournisseurs sites |Test|Résultat attendu| |---|---| |Fournisseur lit `manifest.json`|OK| |Fournisseur charge `today.json`|OK| |Fournisseur charge un produit via `links.product`|OK| |Fournisseur charge une collection complète|OK| |Fournisseur gère `latest.json`|OK ou besoin clarifié| |Fournisseur gère un produit retiré|OK| |Fournisseur gère un rollback|OK| |Fournisseur ne mélange pas deux runs|OK| |Champs manquants identifiés|Ajustement JSON v1 avant gel| ## Détails faciles à oublier Voici les points qui peuvent sembler petits, mais qui ont un gros impact: - `manifest.json` ne doit jamais pointer vers un run incomplet. - Les fichiers dans `/runs/{run_id}/` doivent être immuables. - Une page site doit utiliser un seul `run_id` pendant son rendu. - Les zéros au début des `biznumber` doivent être conservés. - Les dates doivent inclure la timezone. - `product_key` et `program_key` doivent avoir un type stable, idéalement string dans les JSON. - Les produits multi-plateformes doivent être contextualisés par plateforme. - Les overrides doivent expirer. - Les suppressions doivent être visibles dans `latest.json` et dans les rapports. - Les produits retirés ne doivent plus apparaître dans les indexes. - Les anciens fichiers retirés ne doivent pas rester découvrables via les indexes actifs. - Les fichiers lourds comme `collections/{biznumber}/full.json` doivent être mesurés. - Les champs internes ou sensibles ne doivent pas sortir dans les JSON publics. - Le CPE doit être idempotent: relancer un job ne doit pas casser l'état. - Les seuils d'anomalies doivent être définis avant le pilote. - Le run doit avoir un rapport lisible par humains, pas seulement des logs techniques. - Le rollback doit être testé avant le premier incident réel. - La rétention doit être automatisée. - Le bucket/CDN doit avoir une stratégie de purge du manifest. - Les sites doivent savoir quoi faire si un fichier lié retourne 404. - Les liens dans les JSON doivent être relatifs au `base_path` ou documentés clairement. - Les JSON Schema doivent être validés dans le pipeline. - Le feed JSON ne doit pas devenir une API dynamique cachée. ## Décision de fin de pilote À la fin du pilote, il faut pouvoir répondre clairement: - Est-ce que les sites peuvent consommer le feed JSON? - Est-ce que la génération est assez rapide? - Est-ce que le modèle protège contre les bugs source? - Est-ce que les rapports permettent d'être proactifs? - Est-ce que les corrections d'urgence sont assez rapides? - Est-ce que le rollback est simple? - Est-ce que le périmètre reste maîtrisé? - Est-ce qu'on peut généraliser à TFO, IDELLO, ONFR et linéaire?