expria-backend/docs/TEST_ENVIRONMENT.md

365 lines
12 KiB
Markdown

# TEST_ENVIRONMENT.md — Expria / Coach TCF Canada
> **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.
---
## 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.
**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
---
## 2. Les 4 comptes de test
| Compte | Plan | simulations_used | 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 |
**Mot de passe pour tous les comptes de test :** `Expria2025!test`
---
## 3. Script de création — à exécuter dans Supabase SQL Editor
> ⚠️ À exécuter UNE SEULE FOIS dans l'environnement de développement.
> Ne jamais exécuter en production.
```sql
-- =============================================================
-- EXPRIA — Création des comptes de test
-- Environnement : développement / staging uniquement
-- =============================================================
-- Étape 1 : Créer les utilisateurs dans auth.users
-- (Supabase gère le hash du mot de passe automatiquement)
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;
-- É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();
-- É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)
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'),
-- 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;
```
---
## 4. Script de vérification — confirmer que les comptes existent
```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)
```
---
## 6. Bloquer les inscriptions @expria.local en production
Ajouter cette validation dans le backend (middleware d'inscription) :
```typescript
// src/middleware/auth.ts — backend Hono
const BLOCKED_EMAIL_DOMAINS = ['@expria.local']
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
})
```
---
## 7. Procédure complète — première mise en place
```
É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
```