From ecb478e10cbed8a5ecc891e52149da44d8d14aab Mon Sep 17 00:00:00 2001 From: Hermann_Kitio Date: Tue, 21 Apr 2026 02:01:13 +0300 Subject: [PATCH] =?UTF-8?q?fix(simulations):=20d=C3=A9placer=20incr=C3=A9m?= =?UTF-8?q?ent=20simulations=5Fused=20apr=C3=A8s=20correction=20r=C3=A9uss?= =?UTF-8?q?ie=20(Option=20B)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/simulationController.test.ts | 17 +++------------ src/controllers/correctionController.ts | 21 +++++++++++++++++-- src/controllers/simulationController.ts | 12 +---------- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/controllers/__tests__/simulationController.test.ts b/src/controllers/__tests__/simulationController.test.ts index 1d5b99d..8142013 100644 --- a/src/controllers/__tests__/simulationController.test.ts +++ b/src/controllers/__tests__/simulationController.test.ts @@ -55,15 +55,6 @@ function mockInsert(returnData: { id: string; tache: string; mode: string; creat } as any) } -/** Mock from('profiles').update(...).eq(...) */ -function mockUpdate() { - vi.mocked(supabase.from).mockReturnValueOnce({ - update: vi.fn(() => ({ - eq: vi.fn(() => ({ data: null, error: null })), - })), - } as any) -} - /** Mock from('productions').select(...).eq(...).single() pour getById */ function mockProductionSelect(data: unknown, error: unknown = null) { vi.mocked(supabase.from).mockReturnValueOnce({ @@ -123,12 +114,11 @@ describe('POST /simulations', () => { vi.clearAllMocks() }) - it('free + 4 simulations utilisées → création OK, simulations_used incrémenté', async () => { + it('free + 4 simulations utilisées → création OK, pas d\'incrément (il a lieu après correction)', async () => { const profile = buildProfile({ plan: 'free', simulations_used: 4 }) mockAuth(profile) mockInsert({ id: 'prod-1', tache: 'EE_T1', mode: 'entrainement', created_at: '2024-01-01T00:00:00Z' }) mockSujets([MOCK_SUJET_EE_T1]) - mockUpdate() const app = createApp() const res = await app.request('/simulations', { @@ -143,10 +133,9 @@ describe('POST /simulations', () => { expect(body.tache).toBe('EE_T1') expect(body.mode).toBe('entrainement') expect(body.sujet).toEqual(MOCK_SUJET_EE_T1) - // 4 appels from : profiles (auth) + productions (insert) + sujets (select) + profiles (update) - expect(vi.mocked(supabase.from).mock.calls).toHaveLength(4) + // 3 appels from : profiles (auth) + productions (insert) + sujets (select) + expect(vi.mocked(supabase.from).mock.calls).toHaveLength(3) expect(vi.mocked(supabase.from).mock.calls[2][0]).toBe('sujets') - expect(vi.mocked(supabase.from).mock.calls[3][0]).toBe('profiles') }) it('free + 5 simulations utilisées → QUOTA_REACHED', async () => { diff --git a/src/controllers/correctionController.ts b/src/controllers/correctionController.ts index d8182e4..8a26c80 100644 --- a/src/controllers/correctionController.ts +++ b/src/controllers/correctionController.ts @@ -1,5 +1,6 @@ import { supabase } from '../lib/supabase.js' import { correctEE as deepseekCorrectEE, correctEO as deepseekCorrectEO } from '../lib/deepseek.js' +import { PLANS, type Plan } from '../lib/access.js' import type { EERapport, EORapport } from '../lib/deepseek.js' import type { AuthProfile } from '../middleware/auth.js' @@ -73,7 +74,15 @@ export async function correctEE( } } - // 4. Retourner le rapport complet enrichi avec simulation_id + // 4. Incrémenter simulations_used si le plan a une limite (non bloquant). + if (PLANS[profile.plan as Plan].simulations_lifetime !== null) { + await supabase + .from('profiles') + .update({ simulations_used: profile.simulations_used + 1 }) + .eq('id', profile.id) + } + + // 5. Retourner le rapport complet enrichi avec simulation_id return { data: { ...rapport, simulation_id: simulationId } } } @@ -141,6 +150,14 @@ export async function correctEO( } } - // 4. Retourner le rapport complet enrichi avec simulation_id + // 4. Incrémenter simulations_used si le plan a une limite (non bloquant). + if (PLANS[profile.plan as Plan].simulations_lifetime !== null) { + await supabase + .from('profiles') + .update({ simulations_used: profile.simulations_used + 1 }) + .eq('id', profile.id) + } + + // 5. Retourner le rapport complet enrichi avec simulation_id return { data: { ...rapport, simulation_id: simulationId } } } diff --git a/src/controllers/simulationController.ts b/src/controllers/simulationController.ts index 42f6fd3..c3d4401 100644 --- a/src/controllers/simulationController.ts +++ b/src/controllers/simulationController.ts @@ -1,6 +1,5 @@ import { supabase } from '../lib/supabase.js' -import { canUserSimulate, getPlanPermissions } from '../lib/access.js' -import type { Plan } from '../lib/access.js' +import { canUserSimulate } from '../lib/access.js' import type { EERapport } from '../lib/deepseek.js' import type { AuthProfile } from '../middleware/auth.js' @@ -114,15 +113,6 @@ export async function create( } } - // 4. Incrémenter simulations_used si le plan a une limite (via access.ts — Règle D) - const perms = getPlanPermissions(profile.plan as Plan) - if (perms.simulations_lifetime !== null) { - await supabase - .from('profiles') - .update({ simulations_used: profile.simulations_used + 1 }) - .eq('id', profile.id) - } - return { data: { id: data.id,