Statut : stable (snapshot daté à recouper avec le code) Public cible : contributeur Dernière vérification : 2026-05-01 Sources de vérité :
app/controllers/,config/routes.rb,models.md,../internal/todo.md.À vérifier :
- Recroiser la liste des contrôleurs avec
app/controllers/après chaque ajout admin.
Statut interne:
Date: 2025-01-31
Objectif: Audit complet des contrôleurs pour préparer l'implémentation de tests TDD
Base: Classification Zones (models.md)
Statut actuel:
Total contrôleurs: 33
Zone 1 (Critiques): 8 contrôleurs ✅ 100% testé
Zone 2 (En cours): 10 contrôleurs
Zone 3 (Non prioritaire): 15 contrôleurs
Architecture actuelle (2025-01): ✅ Tous les controllers admin utilisent des services (voir services.md)
Approche: Tests d'intégration (request specs) qui testent les invariants métier et contrats d'interface.
Ce qu'on teste:
- ✅ Autorisation (admin/authentifié/anonyme)
- ✅ Workflows métier complets (CRUD + logique métier)
- ✅ Edge cases critiques (validations, erreurs)
- ✅ Réponses HTTP correctes
- ❌ Pas de détails d'implémentation
Approche: Attendre que la logique se stabilise, puis ajouter tests.
Approche: Pas de tests ou tests minimaux pour régression.
Complexité: ⭐⭐⭐⭐⭐ (Très élevée - 505 lignes)
Actions à tester:
index- Liste avec filtres, recherche, paginationshow- Affichage Person/User (support formatperson_123)new- Création avec/withoutperson_idcreate- UtiliseAdmin::UserCreationFormedit- Édition User/Personupdate- Mise à jour User + Person séparémentdestroy- Suppression viaUserManagement::UserDeleterrestore- Restauration User soft-deletededit_person- Édition Person directement
Invariants métier à tester:
- ✅ Seuls admin/super_admin peuvent accéder
- ✅ Pas de suppression User avec privilèges ≥ current_user
- ✅ Création User → Person créée automatiquement
- ✅ Update Person → skip_membership_validation activé
- ✅ Destruction Person → User soft-deleted si existe
Services utilisés:
Admin::UserCreationFormUserManagement::UserDeleter(à conserver pour suppression/archivage sécurisée)PersonQueryPeople::Register
Estimation: 25-30 request specs
Complexité: ⭐⭐⭐⭐ (Élevée - 273 lignes)
Actions à tester:
index- Liste avec filtres viaAdmin::PaymentsServicenew- Nouveau paiement (optionneluser_id)create- UtilisePeople::PaymentCreatoredit- Édition inline (turbo_stream)update- UtilisePeople::PaymentUpdaterdestroy- UtilisePeople::PaymentCancellershow- Redirection vers indexrestore- UtilisePeople::PaymentRestorer
Invariants métier à tester:
- ✅ Seuls admin/super_admin peuvent accéder
- ✅ Création paiement →
recorded_by_id = Current.user.id - ✅ Montant converti euros → centimes
- ✅ Destruction → soft-delete avec audit
- ✅ Totaux recalculés après modifications
Services utilisés:
Admin::PaymentsServicePeople::PaymentCreatorPeople::PaymentUpdaterPeople::PaymentCancellerPeople::PaymentRestorer
Estimation: 20-25 request specs
Complexité: ⭐⭐⭐⭐ (Élevée - 167 lignes)
Actions à tester:
index- Liste des personnes avec adhésionsshow- Affichage adhésion actuellenew- Nouvelle adhésion (optionnelupgrade=true)create- Création/Upgrade via services- Upgrade:
People::MembershipUpgrader - Création:
People::MembershipCreator
- Upgrade:
edit- Édition adhésionupdate- Mise à jour dates/type viaPeople::MembershipUpdaterdestroy- Désactivation (status: inactive) viaPeople::MembershipDeactivator
Invariants métier à tester:
- ✅ Seuls admin/super_admin peuvent accéder
- ✅ Upgrade Basic → Circus uniquement
- ✅ Upgrade calcule différence de prix correctement
- ✅ Création adhésion → Payment créé automatiquement
- ✅ Destroy → Status inactive (pas de suppression)
Services utilisés:
People::MembershipCreatorPeople::MembershipUpgraderPeople::MembershipUpdaterPeople::MembershipDeactivator
Estimation: 18-22 request specs
Complexité: ⭐⭐⭐ (Moyenne - 76 lignes)
Actions à tester:
index- Liste tous les événementsnew- Formulaire créationcreate/update- CRUD inline : le contrôleur instancieEvent.new/Event.finddirectement (vérifié dansapp/controllers/admin/events_controller.rb).destroy- Suppression événement
Invariants métier à tester:
- ✅ Seuls admin/super_admin peuvent accéder
- ✅ Création →
creator_id = current_user.id - ✅ Category par défaut: 'other'
Services utilisés:
- (aucun) — les classes
EventManagement::EventCreator/Updater/Deleterexistent dansapp/services/event_management/mais ne sont plus appelées depuisapp/. Voir l'item « CleanupEventManagement::*orphelins » dans../internal/todo.md.
Estimation: 12-15 request specs
Complexité: ⭐ (Simple - 10 lignes)
Actions à tester:
index- Affichage dashboard avec cache
Invariants métier à tester:
- ✅ Seuls admin/super_admin peuvent accéder
- ✅ Cache notepad et opening_hours
Estimation: 3-5 request specs
Complexité: ⭐⭐ (Simple - 25 lignes)
Actions à tester:
new- Formulaire login (redirection si déjà connecté)create- Authentification + rate limitingdestroy- Déconnexion
Invariants métier à tester:
- ✅ Accessible sans authentification (
allow_unauthenticated_access) - ✅ Rate limiting: 10 tentatives / 3 minutes
- ✅ Login réussi → Session créée + cookie
- ✅ Login échoué → Redirection avec erreur
- ✅ Logout → Session détruite + cookie supprimé
Services utilisés:
User.authenticate_bystart_new_session_for
Estimation: 8-10 request specs
Complexité: ⭐⭐⭐ (Moyenne - 58 lignes)
Actions à tester:
new- Formulaire inscription (redirection si connecté)create- UtiliseWeb::UserRegistration
Invariants métier à tester:
- ✅ Accessible sans authentification
- ✅ Création User → Person + Newsletter si demandé
- ✅ Email existant → Message avec lien "Mot de passe oublié"
- ✅ Person existante sans User → Message "Récupérer mon compte"
- ✅ CGU + Privacy Policy requis
- ✅ Inscription réussie → Email welcome envoyé
Services utilisés:
Web::UserRegistration
Estimation: 10-12 request specs
Complexité: ⭐⭐⭐ (Moyenne - 65 lignes)
Actions à tester:
create- Création session Stripesuccess- Callback Stripe + création EventAttendeecancel- Annulation paiement
Invariants métier à tester:
- ✅ Requiert authentification
- ✅ Création session Stripe avec metadata correct
- ✅ Success → EventAttendee créé si paiement réussi
- ✅ Cancel → Redirection avec message
Services utilisés:
- Stripe API
Estimation: 8-10 request specs (avec mocks Stripe)
| Controller | Complexité | Raison Zone 2 | Tests Quand? |
|---|---|---|---|
AccountClaimsController |
⭐⭐ | Workflow à finaliser | Après validation business |
PasswordsController |
⭐⭐ | Feature à stabiliser | Après validation business |
Admin::ContributionFormulasController |
⭐⭐⭐ | Flux achat/upgrade via People::Contribution* |
Après stabilisation |
Admin::MemberNumbersController |
⭐⭐ | Admin access | Après stabilisation |
Admin::MembershipTypesController |
⭐⭐⭐ | CRUD standard | Après stabilisation |
Admin::DonationsController |
⭐⭐ | CRUD simple | Après stabilisation |
Admin::ExportsController |
⭐⭐ | Export data | Après stabilisation |
Admin::OpeningHoursController |
⭐ | CRUD simple | Après stabilisation |
Admin::NotepadsController |
⭐ | CRUD simple | Après stabilisation |
Admin::BlogsController |
⭐⭐ | CMS non critique | Après stabilisation |
| Controller | Raison |
|---|---|
HomeController |
Affichage simple |
PagesController |
Pages statiques |
EventsController (public) |
Affichage liste |
BlogsController (public) |
Affichage blog |
ContactsController |
Formulaire simple |
SettingsController |
Self-service non critique |
UsersController (public) |
Profil utilisateur |
EventInterestsController |
Feature simple |
CookiesController |
Configuration cookies |
Admin::PriceCatalogController |
CMS non critique |
Admin::AttendanceListsController |
Logique en exploration |
Admin::AttendancesController |
Logique en exploration |
Admin::SessionsController |
Duplicate SessionsController |
| Autres admin controllers | CRUD non critiques |
Objectif: Tests pour 8 contrôleurs Zone 1
Ordre de priorité:
- ✅
SessionsController- Auth critique - ✅
RegistrationsController- Signup critique - ✅
Admin::PaymentsController- Financier critique - ✅
Admin::MembershipsController- Business critique - ✅
Admin::UsersController- Gestion critique - ✅
CheckoutController- Paiement critique - ✅
Admin::EventsController- CRUD critique - ✅
Admin::DashboardController- Home admin
Estimation totale: ~100-120 request specs
Métriques attendues:
- Coverage controllers Zone 1: 80%+
- Tests passent à 100%
- CI bloquant pour régressions
Objectif: Edge cases + intégrations
- Tests Turbo Stream pour actions AJAX
- Tests formats JSON
- Tests edge cases (permissions, validations)
- Tests intégration avec services
Estimation: +30-40 request specs
Objectif: Stabiliser Zone 2 → Zone 1
- Quand logique stabilisée, ajouter tests
- Migration progressive Zone 2 → Zone 1
# spec/requests/admin/users_spec.rb
RSpec.describe "Admin::Users", type: :request do
let(:admin_user) { create(:user, system_role: :admin) }
let(:regular_user) { create(:user, system_role: :web_visitor) }
before { sign_in_as admin_user }
describe "GET /admin/users" do
context "when authenticated as admin" do
it "returns success" do
get admin_users_path
expect(response).to have_http_status(:success)
end
it "filters by active membership" do
create(:person, :with_active_membership)
create(:person, :without_membership)
get admin_users_path, params: { filter: "with_active_membership" }
expect(assigns(:people).count).to eq(1)
end
end
context "when not authenticated" do
it "redirects to login" do
sign_out
get admin_users_path
expect(response).to redirect_to(new_session_path)
end
end
end
describe "POST /admin/users" do
context "with valid params" do
let(:valid_params) do
{
user: {
first_name: "John",
last_name: "Doe",
email: "john@example.com",
create_web_account: "1",
email_address: "john@example.com",
system_role: "web_visitor"
}
}
end
it "creates a new user and person" do
expect {
post admin_users_path, params: valid_params
}.to change(Person, :count).by(1)
.and change(User, :count).by(1)
end
it "redirects to user show page" do
post admin_users_path, params: valid_params
person = Person.last
expect(response).to redirect_to(admin_user_path("person_#{person.id}"))
end
end
end
endcontext "authorization" do
it "allows admin access" do
sign_in_as create(:user, system_role: :admin)
get admin_users_path
expect(response).to have_http_status(:success)
end
it "allows super_admin access" do
sign_in_as create(:user, system_role: :super_admin)
get admin_users_path
expect(response).to have_http_status(:success)
end
it "denies volunteer access" do
sign_in_as create(:user, system_role: :volunteer)
get admin_users_path
expect(response).to redirect_to(root_path)
end
it "denies unauthenticated access" do
get admin_users_path
expect(response).to redirect_to(new_session_path)
end
endcontext "authorization" do
it "allows unauthenticated access to new" do
get new_registration_path
expect(response).to have_http_status(:success)
end
it "allows authenticated access" do
sign_in_as create(:user)
get new_registration_path
expect(response).to redirect_to(root_path)
end
end- ✅ Un User ne peut pas être supprimé par un User avec privilèges ≤
- ✅ Création User → Person créée automatiquement
- ✅ Person sans User peut exister (prospects, newsletter)
- ✅ Montant toujours en centimes en DB
- ✅
recorded_by_idtoujours défini (audit trail) - ✅ Destruction → soft-delete (pas de hard delete)
- ✅ Upgrade Basic → Circus uniquement
- ✅ Pas de downgrade Circus → Basic
- ✅ Création adhésion → Payment créé automatiquement
- ✅ Rate limiting: 10 tentatives / 3 minutes
- ✅ Session créée avec cookie httponly
- ✅ Logout → Session détruite
- ✅ 8 contrôleurs Zone 1 testés
- ✅ 100-120 request specs écrits
- ✅ Coverage controllers Zone 1: 80%+
- ✅ Tous tests passent à 100%
- ✅ Edge cases testés
- ✅ Intégrations services testées
- ✅ Coverage controllers Zone 1: 90%+
- ✅ Zone 2 progressive → Zone 1
- ✅ Coverage globale: 60%+
# spec/support/request_helpers.rb
module RequestHelpers
def sign_in_as(user)
# Créer session + cookie
session = user.sessions.create!(
user_agent: "Test Agent",
ip_address: "127.0.0.1"
)
cookies.signed[:session_id] = session.id
Current.user = user
Current.session = session
end
def sign_out
Current.session&.destroy
cookies.delete(:session_id)
Current.user = nil
Current.session = nil
end
end# spec/factories/people.rb
FactoryBot.define do
factory :person do
# ...
trait :with_active_membership do
after(:create) do |person|
create(:membership, person: person, status: :active)
end
end
trait :without_user_account do
user { nil }
end
end
end- Lire
docs/domain/business_logic.md(Zone 1) - Comprendre architecture Person-Based
- Setup helpers request spec
- Créer factories nécessaires
- Identifier actions critiques
- Identifier invariants métier
- Identifier services utilisés
- Écrire tests authorization d'abord
- Écrire tests happy path
- Écrire tests edge cases
- Vérifier coverage > 80%
- Business Logic:
../domain/business_logic.md - Zones Classification:
models.md
Prochaine Étape: Commencer Phase 1 avec SessionsController (plus simple) pour établir patterns et helpers.