docs: enrichir ARCHITECTURE, DEVELOPMENT_PRINCIPLES, GOLDEN_DATASET, TESTS_AUTOMATISES, TEST_ENVIRONMENT

This commit is contained in:
Hermann_Kitio 2026-04-17 18:35:50 +03:00
parent 52b8e9d011
commit f343fb4696
5 changed files with 1649 additions and 1476 deletions

View file

@ -1,365 +1,260 @@
# TEST_ENVIRONMENT.md — Expria / Coach TCF Canada
# TEST_ENVIRONMENT.md — Expria Frontend
> **Document de référence — Version 1.0**
> Ce document décrit comment créer et réinitialiser l'environnement de test.
> Les comptes de test permettent de rejouer tous les parcours utilisateur
> sans créer de vrais abonnements ni passer par Stripe.
> Ce document décrit comment configurer et utiliser l'environnement de test frontend.
> Complément au `TEST_ENVIRONMENT.md` backend (qui décrit la base de données Supabase et les comptes de test).
---
## 1. Principe
L'environnement de test est une configuration connue et reproductible de la base de données.
Il consiste en 4 comptes Supabase préconfigurés, un par situation critique.
L'environnement de test frontend permet de faire tourner le code React localement, connecté soit au backend de production (`api.expria.app`), soit à une instance backend locale (pour tests isolés).
**Règles absolues :**
- Ces comptes n'existent que dans l'environnement de développement / staging
- Jamais en production
- Les emails se terminent par `@expria.local` — bloqués à l'inscription dans le code
- Les mots de passe sont documentés ici — ne jamais les utiliser pour de vrais comptes
- Ne jamais utiliser de comptes réels (utilisateurs payants) pour les tests
- Utiliser exclusivement les 4 comptes `@gmail.com` documentés dans le backend
- Ne jamais committer les fichiers `.env` (présents dans `.gitignore`)
---
## 2. Les 4 comptes de test
## 2. Prérequis
| Compte | Plan | simulations_used | Cas testé |
| Outil | Version minimum | Vérification |
|---|---|---|
| Node.js | 20.x LTS | `node --version` |
| npm | 10.x | `npm --version` |
| Git | 2.x | `git --version` |
| Navigateur moderne | Chrome 120+, Firefox 120+, Safari 17+ | — |
Recommandé :
- VS Code (ou Cursor) avec extensions : ESLint, Prettier, Tailwind CSS IntelliSense, Vitest
- DevTools React (extension navigateur)
---
## 3. Configuration — mode dev connecté au backend de production
C'est le mode par défaut. Le frontend local appelle `https://api.expria.app`.
### Fichier `.env` à créer à la racine du projet
```
# URL du backend de production
VITE_API_URL=https://api.expria.app
# Supabase (clé publique uniquement)
VITE_SUPABASE_URL=https://<project>.supabase.co
VITE_SUPABASE_ANON_KEY=<clé publique à obtenir auprès de Hermann>
# Flags de features (à ajuster selon le sprint)
VITE_ENABLE_T2_LIVE=false
# Monitoring (optionnel)
VITE_SENTRY_DSN=
```
**Les vraies valeurs** sont à récupérer dans :
- Le coffre-fort de Hermann (1Password / Bitwarden)
- Ou auprès de Hermann directement
Après modification du `.env`, redémarrer le dev server (Vite charge les env au démarrage seulement).
### Lancement
```bash
npm install
npm run dev
```
Le frontend est disponible sur `http://localhost:5173` (port Vite par défaut).
---
## 4. Configuration — mode dev connecté au backend local
Utile quand on veut :
- Tester une modification backend avant déploiement
- Déboguer un flux complet frontend + backend
- Travailler sans connexion internet stable
### Prérequis
Le backend doit tourner localement dans un autre terminal :
```bash
cd D:\expria-backend
npm run dev
# Backend disponible sur http://localhost:3000 (vérifier le port exact)
```
### Fichier `.env` (ajustement)
```
VITE_API_URL=http://localhost:3000
# Le reste reste identique
```
### Points d'attention
- **CORS** : le backend doit autoriser `http://localhost:5173` dans sa configuration CORS. À vérifier dans `expria-backend/src/index.ts`. Si CORS bloque les requêtes, voir SEC-02 dans `SECURITY.md`.
- **WebSocket T2** : devient `ws://localhost:3000/t2/live` (non sécurisé en dev local). Acceptable pour le dev.
- **Supabase** : reste en production même en dev local (Supabase n'a pas de mode local simple). Les comptes de test `@gmail.com` fonctionnent identiquement.
---
## 5. Comptes de test (rappel)
Identiques aux comptes documentés dans `expria-backend/docs/TEST_ENVIRONMENT.md` :
| Email | Plan | Simulations utilisées | Cas testé |
|---|---|---|---|
| test.free@expria.local | free | 0 | Parcours Free normal |
| test.standard@expria.local | standard | 12 | Parcours Standard complet |
| test.premium@expria.local | premium | 28 | Parcours Premium complet |
| test.quota@expria.local | free | 5 | Blocage quota Free |
| test.free@gmail.com | free | 0 | Parcours Free normal |
| test.standard@gmail.com | standard | 12 | Parcours Standard complet |
| test.premium@gmail.com | premium | 28 | Parcours Premium complet |
| test.quota@gmail.com | free | 5 | Blocage quota Free |
**Mot de passe pour tous les comptes de test :** `Expria2025!test`
**Mot de passe commun :** `Expria2025!test`
Les comptes sont créés par le script SQL de `expria-backend/docs/TEST_ENVIRONMENT.md` §3. Si un compte manque ou est corrompu, demander à Hermann de rejouer le script côté Supabase.
---
## 3. Script de création — à exécuter dans Supabase SQL Editor
## 6. Scénarios de test spécifiques
> ⚠️ À exécuter UNE SEULE FOIS dans l'environnement de développement.
> Ne jamais exécuter en production.
### 6.1 Simuler un utilisateur Free qui atteint le quota
```sql
-- =============================================================
-- EXPRIA — Création des comptes de test
-- Environnement : développement / staging uniquement
-- =============================================================
1. Se connecter avec `test.free@gmail.com`
2. Utiliser `test.quota@gmail.com` à la place (qui a déjà 5/5 utilisées)
3. Tenter de lancer une simulation → modal de blocage doit apparaître
-- Étape 1 : Créer les utilisateurs dans auth.users
-- (Supabase gère le hash du mot de passe automatiquement)
**Ne pas** essayer de forcer le compteur en soumettant 5 simulations réelles — ça consomme des appels à DeepSeek (payant) et pollue la base.
INSERT INTO auth.users (
id,
email,
encrypted_password,
email_confirmed_at,
created_at,
updated_at,
raw_app_meta_data,
raw_user_meta_data,
is_super_admin,
role
) VALUES
(
'00000000-0000-0000-0000-000000000001',
'test.free@expria.local',
crypt('Expria2025!test', gen_salt('bf')),
NOW(), NOW(), NOW(),
'{"provider":"email","providers":["email"]}',
'{}', false, 'authenticated'
),
(
'00000000-0000-0000-0000-000000000002',
'test.standard@expria.local',
crypt('Expria2025!test', gen_salt('bf')),
NOW(), NOW(), NOW(),
'{"provider":"email","providers":["email"]}',
'{}', false, 'authenticated'
),
(
'00000000-0000-0000-0000-000000000003',
'test.premium@expria.local',
crypt('Expria2025!test', gen_salt('bf')),
NOW(), NOW(), NOW(),
'{"provider":"email","providers":["email"]}',
'{}', false, 'authenticated'
),
(
'00000000-0000-0000-0000-000000000004',
'test.quota@expria.local',
crypt('Expria2025!test', gen_salt('bf')),
NOW(), NOW(), NOW(),
'{"provider":"email","providers":["email"]}',
'{}', false, 'authenticated'
)
ON CONFLICT (id) DO NOTHING;
### 6.2 Simuler un changement de plan
-- Étape 2 : Créer les profils dans la table profiles
INSERT INTO profiles (
id,
email,
plan,
simulations_used,
stripe_customer_id,
stripe_subscription_id,
plan_expires_at,
created_at,
updated_at
) VALUES
(
'00000000-0000-0000-0000-000000000001',
'test.free@expria.local',
'free', 0, NULL, NULL, NULL,
NOW(), NOW()
),
(
'00000000-0000-0000-0000-000000000002',
'test.standard@expria.local',
'standard', 12, 'cus_test_standard', 'sub_test_standard',
NOW() + INTERVAL '14 days',
NOW(), NOW()
),
(
'00000000-0000-0000-0000-000000000003',
'test.premium@expria.local',
'premium', 28, 'cus_test_premium', 'sub_test_premium',
NOW() + INTERVAL '21 days',
NOW(), NOW()
),
(
'00000000-0000-0000-0000-000000000004',
'test.quota@expria.local',
'free', 5, NULL, NULL, NULL,
NOW(), NOW()
)
ON CONFLICT (id) DO UPDATE SET
plan = EXCLUDED.plan,
simulations_used = EXCLUDED.simulations_used,
stripe_customer_id = EXCLUDED.stripe_customer_id,
stripe_subscription_id = EXCLUDED.stripe_subscription_id,
plan_expires_at = EXCLUDED.plan_expires_at,
updated_at = NOW();
Pour tester le flux d'upgrade sans vraiment payer :
1. Aller sur `/pricing` avec `test.free@gmail.com`
2. Cliquer "Choisir Standard"
3. Sur la page Stripe Checkout, utiliser la carte de test `4242 4242 4242 4242`
4. Date d'expiration : n'importe laquelle dans le futur
5. CVC : n'importe lequel (3 chiffres)
-- Étape 3 : Insérer des productions de test pour les comptes Standard et Premium
-- (nécessaire pour tester le dashboard, l'historique, et l'analyse des patterns)
Le webhook Stripe met à jour `plan = 'standard'` dans Supabase. Pour revenir à l'état initial après test, Hermann peut rejouer le script de reset de `expria-backend/docs/TEST_ENVIRONMENT.md` §5.
INSERT INTO productions (
id, user_id, tache, mode, contenu, score, nclc, rapport, created_at
) VALUES
-- 5 productions pour test.standard (active l'indice de préparation)
(gen_random_uuid(), '00000000-0000-0000-0000-000000000002',
'EE_T1', 'entrainement',
'Texte de production test EE T1 — compte standard',
14.5, 8,
'{"criteres":[{"nom":"Cohérence","score":3},{"nom":"Lexique","score":4}],"erreurs":["Connecteurs logiques insuffisants"],"exercices":["Exercice connecteurs"]}',
NOW() - INTERVAL '10 days'),
(gen_random_uuid(), '00000000-0000-0000-0000-000000000002',
'EE_T2', 'entrainement',
'Texte de production test EE T2 — compte standard',
15.0, 8,
'{"criteres":[{"nom":"Cohérence","score":4},{"nom":"Lexique","score":3}],"erreurs":["Vocabulaire limité"],"exercices":["Exercice vocabulaire"]}',
NOW() - INTERVAL '8 days'),
(gen_random_uuid(), '00000000-0000-0000-0000-000000000002',
'EE_T3', 'entrainement',
'Texte de production test EE T3 — compte standard',
13.5, 7,
'{"criteres":[{"nom":"Cohérence","score":3},{"nom":"Lexique","score":3}],"erreurs":["Structure argumentative faible"],"exercices":["Exercice argumentation"]}',
NOW() - INTERVAL '6 days'),
(gen_random_uuid(), '00000000-0000-0000-0000-000000000002',
'EO_T1', 'entrainement',
NULL, 14.0, 8,
'{"criteres":[{"nom":"Phonologie","score":4},{"nom":"Lexique","score":3}],"erreurs":["Liaisons manquantes"],"exercices":["Exercice liaisons"]}',
NOW() - INTERVAL '4 days'),
(gen_random_uuid(), '00000000-0000-0000-0000-000000000002',
'EO_T3', 'entrainement',
NULL, 15.5, 9,
'{"criteres":[{"nom":"Phonologie","score":4},{"nom":"Lexique","score":4}],"erreurs":[],"exercices":[]}',
NOW() - INTERVAL '2 days'),
### 6.3 Simuler un paiement refusé
-- 7 productions pour test.premium (active patterns + indice)
(gen_random_uuid(), '00000000-0000-0000-0000-000000000003',
'EE_T1', 'entrainement',
'Texte production EE T1 — premium',
16.0, 9,
'{"criteres":[{"nom":"Cohérence","score":4},{"nom":"Lexique","score":4}],"erreurs":["Connecteurs logiques"],"exercices":["Connecteurs"]}',
NOW() - INTERVAL '20 days'),
(gen_random_uuid(), '00000000-0000-0000-0000-000000000003',
'EE_T2', 'entrainement',
'Texte production EE T2 — premium',
15.5, 9,
'{"criteres":[{"nom":"Cohérence","score":4},{"nom":"Lexique","score":4}],"erreurs":["Connecteurs logiques"],"exercices":["Connecteurs"]}',
NOW() - INTERVAL '16 days'),
(gen_random_uuid(), '00000000-0000-0000-0000-000000000003',
'EE_T3', 'examen',
'Texte production EE T3 — premium examen',
17.0, 10,
'{"criteres":[{"nom":"Cohérence","score":5},{"nom":"Lexique","score":4}],"erreurs":[],"exercices":[]}',
NOW() - INTERVAL '12 days'),
(gen_random_uuid(), '00000000-0000-0000-0000-000000000003',
'EO_T1', 'entrainement',
NULL, 16.5, 9,
'{"criteres":[{"nom":"Phonologie","score":4},{"nom":"Lexique","score":4}],"erreurs":["Connecteurs logiques"],"exercices":["Connecteurs oraux"]}',
NOW() - INTERVAL '9 days'),
(gen_random_uuid(), '00000000-0000-0000-0000-000000000003',
'EO_T2_LIVE', 'entrainement',
NULL, 15.0, 8,
'{"criteres":[{"nom":"Interaction","score":4},{"nom":"Phonologie","score":3}],"erreurs":["Hésitations fréquentes"],"exercices":["Fluidité orale"]}',
NOW() - INTERVAL '6 days'),
(gen_random_uuid(), '00000000-0000-0000-0000-000000000003',
'EO_T3', 'entrainement',
NULL, 16.0, 9,
'{"criteres":[{"nom":"Phonologie","score":4},{"nom":"Lexique","score":4}],"erreurs":["Connecteurs logiques"],"exercices":["Connecteurs oraux"]}',
NOW() - INTERVAL '3 days'),
(gen_random_uuid(), '00000000-0000-0000-0000-000000000003',
'EE_T1', 'examen',
'Texte production EE T1 examen — premium',
17.5, 10,
'{"criteres":[{"nom":"Cohérence","score":5},{"nom":"Lexique","score":4}],"erreurs":[],"exercices":[]}',
NOW() - INTERVAL '1 day')
ON CONFLICT DO NOTHING;
Même flux que 6.2 mais avec la carte `4000 0000 0000 0002`. Stripe retourne "paiement refusé". Le plan reste inchangé, le message d'erreur doit s'afficher clairement.
### 6.4 Simuler une session expirée
1. Se connecter normalement
2. Ouvrir DevTools → Application → Local Storage
3. Trouver la clé contenant le JWT Supabase (`sb-<project>-auth-token`)
4. Modifier la valeur pour casser le token (changer 1 caractère)
5. Rafraîchir la page
6. Le frontend doit détecter le JWT invalide → message "Session expirée" + redirect `/login`
Ce test vérifie SEC-06 (gestion des sessions expirées).
### 6.5 Simuler T2 Live sans avoir Premium
1. Se connecter avec `test.free@gmail.com` ou `test.standard@gmail.com`
2. Accéder à l'URL `/t2-live` directement
3. Le frontend doit afficher un PaywallModal ou rediriger
4. Si malgré tout une requête WebSocket est envoyée, le backend doit fermer la connexion avec close code 4003 (`PLAN_INSUFFICIENT`)
---
## 7. Matrice de compatibilité navigateurs
Ces navigateurs sont ceux que Claude Code doit considérer comme supportés. Tester au minimum sur les deux premiers.
| Navigateur | Version minimum | Priorité de test | Notes |
|---|---|---|---|
| Chrome (desktop) | 120 | 🔴 obligatoire | Majoritaire chez les utilisateurs |
| Chrome Mobile (Android) | 120 | 🔴 obligatoire | Audience Afrique = mobile-first |
| Safari Mobile (iOS) | 17 | 🟡 recommandé | Audience Canada = iPhone courant |
| Firefox (desktop) | 120 | 🟢 optionnel | Usage faible |
| Safari Desktop | 17 | 🟢 optionnel | Niche |
**Attention particulière pour mobile Android (Afrique) :**
- Connexions 3G/4G instables → vérifier que retry dans `api-client.ts` gère bien
- RAM limitée → éviter les listes illimitées, paginer
- Clavier virtuel qui masque les inputs → vérifier scroll automatique
Test mobile rapide via DevTools : Chrome → F12 → Toggle device toolbar → sélectionner "iPhone 12" ou "Galaxy S20".
Test mobile réel : idéal avant chaque release production, via un vrai téléphone ou un service comme BrowserStack (payant).
---
## 8. Simulation de conditions réseau dégradées
Dans Chrome DevTools → Network tab → Throttling dropdown :
- **Fast 3G** : simule une connexion 3G typique (Afrique)
- **Slow 3G** : simule une connexion 2G (zones rurales)
- **Offline** : simule une perte réseau
Tests à effectuer en mode "Fast 3G" :
- Login : doit répondre en < 10s
- Submit simulation : timeout à 30s doit fonctionner
- T2 Live : doit tenir la connexion WebSocket ou échouer gracieusement
---
## 9. Procédure avant chaque session Claude Code
```
[ ] D:\expria-frontend existe et contient docs/ à jour
[ ] .env est configuré (demander à Hermann si nécessaire)
[ ] npm install a été exécuté récemment (dernière modification package.json)
[ ] npm run dev démarre sans erreur
[ ] Login test.free@gmail.com fonctionne
[ ] Un commit Git propre existe avant de commencer
[ ] Les documents de référence ont été lus (cf. ONBOARDING.md §7)
```
---
## 4. Script de vérification — confirmer que les comptes existent
## 10. Procédure après chaque session Claude Code
```sql
-- Vérifier les profils créés
SELECT
id,
email,
plan,
simulations_used,
plan_expires_at
FROM profiles
WHERE email LIKE '%@expria.local'
ORDER BY email;
-- Vérifier les productions créées
SELECT
p.email,
prod.tache,
prod.mode,
prod.score,
prod.created_at
FROM productions prod
JOIN profiles p ON p.id = prod.user_id
WHERE p.email LIKE '%@expria.local'
ORDER BY p.email, prod.created_at;
```
**Résultat attendu :** 4 profils, 12 productions au total.
---
## 5. Script de réinitialisation — remettre l'environnement à zéro
> À utiliser quand les comptes de test ont été modifiés par des sessions de test
> et qu'on veut repartir d'un état propre.
```sql
-- Supprimer les productions de test
DELETE FROM productions
WHERE user_id IN (
SELECT id FROM profiles WHERE email LIKE '%@expria.local'
);
-- Remettre les profils à leur état initial
UPDATE profiles SET
plan = 'free',
simulations_used = 0,
stripe_customer_id = NULL,
stripe_subscription_id = NULL,
plan_expires_at = NULL,
updated_at = NOW()
WHERE email = 'test.free@expria.local';
UPDATE profiles SET
plan = 'standard',
simulations_used = 12,
stripe_customer_id = 'cus_test_standard',
stripe_subscription_id = 'sub_test_standard',
plan_expires_at = NOW() + INTERVAL '14 days',
updated_at = NOW()
WHERE email = 'test.standard@expria.local';
UPDATE profiles SET
plan = 'premium',
simulations_used = 28,
stripe_customer_id = 'cus_test_premium',
stripe_subscription_id = 'sub_test_premium',
plan_expires_at = NOW() + INTERVAL '21 days',
updated_at = NOW()
WHERE email = 'test.premium@expria.local';
UPDATE profiles SET
plan = 'free',
simulations_used = 5,
stripe_customer_id = NULL,
stripe_subscription_id = NULL,
plan_expires_at = NULL,
updated_at = NOW()
WHERE email = 'test.quota@expria.local';
-- Réinsérer les productions (copier-coller le bloc INSERT de la section 3)
[ ] npm run typecheck : 0 erreur
[ ] npm run test : 0 échec
[ ] Les tests du Golden Dataset concernés ont été rejoués (cf. GOLDEN_DATASET.md)
[ ] Si modifications d'état (profils de test modifiés par des actions de test),
rejouer le script de reset backend
[ ] Un commit Git a été fait avec un message clair
[ ] CHANGELOG.md mis à jour
```
---
## 6. Bloquer les inscriptions @expria.local en production
## 11. Debugging
Ajouter cette validation dans le backend (middleware d'inscription) :
### Erreur "CORS blocked" au démarrage
- Vérifier que le backend autorise `http://localhost:5173`
- Vérifier que `VITE_API_URL` dans `.env` pointe vers la bonne URL backend
- Voir SEC-02 dans `SECURITY.md`
```typescript
// src/middleware/auth.ts — backend Hono
### Erreur "Invalid JWT" après login
- Le JWT expire au bout de ~1h, se déconnecter et se reconnecter
- Vider le localStorage (DevTools → Application → Clear site data)
- Vérifier que `VITE_SUPABASE_URL` correspond bien au projet Supabase utilisé par le backend
const BLOCKED_EMAIL_DOMAINS = ['@expria.local']
### Les variables d'environnement ne sont pas prises en compte
- Vite charge `.env` au démarrage uniquement — redémarrer `npm run dev` après modification
- Les variables doivent commencer par `VITE_` pour être exposées côté client
- Vérifier qu'il n'y a pas d'espace autour du `=` dans `.env`
export function validateEmail(email: string): boolean {
const isBlocked = BLOCKED_EMAIL_DOMAINS.some(domain =>
email.toLowerCase().endsWith(domain)
)
if (isBlocked) return false
return true
}
```
Et dans la route d'inscription :
```typescript
// src/routes/auth.ts
app.post('/auth/register', async (c) => {
const { email, password } = await c.req.json()
if (!validateEmail(email)) {
return c.json({ error: 'Email non autorisé' }, 400)
}
// ... suite de l'inscription
})
```
### WebSocket T2 Live ne se connecte pas
- Vérifier `VITE_ENABLE_T2_LIVE=true` dans `.env`
- Vérifier que l'utilisateur est bien Premium
- Ouvrir DevTools → Network → filtrer par "WS" pour voir le handshake WebSocket
- Vérifier les close codes : 4001 = auth, 4003 = plan insuffisant
---
## 7. Procédure complète — première mise en place
## 12. Historique
```
Étape 1 : Ouvrir Supabase Dashboard → SQL Editor
Étape 2 : Copier-coller le script de la section 3
Étape 3 : Exécuter
Étape 4 : Copier-coller le script de vérification (section 4)
Étape 5 : Vérifier : 4 profils + 12 productions affichés
Étape 6 : Tester une connexion avec test.free@expria.local
dans l'application (mot de passe : Expria2025!test)
Étape 7 : Vérifier que le dashboard Free s'affiche correctement
```
---
## 8. Procédure — avant chaque session Golden Dataset
```
Étape 1 : Exécuter le script de réinitialisation (section 5)
Étape 2 : Exécuter le script de vérification (section 4)
Étape 3 : Confirmer que les 4 profils sont dans l'état attendu
Étape 4 : Lancer les tests du Golden Dataset
```
| Version | Date | Changements |
|---|---|---|
| 1.0 | 2026-04-17 | Création initiale |