# 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 `@gmail.com` — 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@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` --- ## 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@gmail.com', crypt('Expria2025!test', gen_salt('bf')), NOW(), NOW(), NOW(), '{"provider":"email","providers":["email"]}', '{}', false, 'authenticated' ), ( '00000000-0000-0000-0000-000000000002', 'test.standard@gmail.com', crypt('Expria2025!test', gen_salt('bf')), NOW(), NOW(), NOW(), '{"provider":"email","providers":["email"]}', '{}', false, 'authenticated' ), ( '00000000-0000-0000-0000-000000000003', 'test.premium@gmail.com', crypt('Expria2025!test', gen_salt('bf')), NOW(), NOW(), NOW(), '{"provider":"email","providers":["email"]}', '{}', false, 'authenticated' ), ( '00000000-0000-0000-0000-000000000004', 'test.quota@gmail.com', 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@gmail.com', 'free', 0, NULL, NULL, NULL, NOW(), NOW() ), ( '00000000-0000-0000-0000-000000000002', 'test.standard@gmail.com', 'standard', 12, 'cus_test_standard', 'sub_test_standard', NOW() + INTERVAL '14 days', NOW(), NOW() ), ( '00000000-0000-0000-0000-000000000003', 'test.premium@gmail.com', 'premium', 28, 'cus_test_premium', 'sub_test_premium', NOW() + INTERVAL '21 days', NOW(), NOW() ), ( '00000000-0000-0000-0000-000000000004', 'test.quota@gmail.com', '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 '%@gmail.com' 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 '%@gmail.com' 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 '%@gmail.com' ); -- 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@gmail.com'; 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@gmail.com'; 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@gmail.com'; 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@gmail.com'; -- Réinsérer les productions (copier-coller le bloc INSERT de la section 3) ``` --- ## 6. Bloquer les inscriptions @gmail.com en production Ajouter cette validation dans le backend (middleware d'inscription) : ```typescript // src/middleware/auth.ts — backend Hono const BLOCKED_EMAIL_DOMAINS = ['@gmail.com'] 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@gmail.com 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 ```