365 lines
12 KiB
Markdown
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
|
|
```
|