import { describe, it, expect } from 'vitest'; import { computeGaugeState } from '@domain/services/GaugeCalculator'; import type { GaugeRecharge } from '@domain/services/GaugeCalculator'; import { gainedIn, timeToGain } from '@domain/services/GaugeCalculator'; import { xpForLevel, levelFromXp } from '@domain/value-objects/XpTable'; /** * Tests de régression pour la fonctionnalité de recharge de jauge en cours de session. * * Scénario typique : le joueur recharge la mangeoire pendant le timer car elle s'est vidée. * Sans recharge prise en compte, le countdown reste bloqué à ∞ ou 0 au lieu de recalculer. */ describe('Régression: recharge de jauge en cours de timer', () => { describe('Mangeoire rechargée une fois (scénario XP principal)', () => { // Contexte : // - Mangeoire départ = 50 000 (tier 2, rate 20/tick) // - DD level 1, cible level 50 // - Après 1h (3600 sec), la mangeoire est vide → XP gagnée jusqu'ici // - Le joueur recharge à 80 000 (tier 3) // - On simule 1h supplémentaire après recharge const startGl = 50000; const xpCible = xpForLevel(50) - xpForLevel(1); // 34 365 XP it('sans recharge : XP limité par la jauge qui se vide', () => { // 50 000 pts de jauge → max 50 000 XP disponible (tier 2 → tier 1) // Mais XP cible = 34 365 → la jauge suffira-t-elle ? // timeToGain(50000, 34365) : // tier2 (40000-50000) : 10000 pts, s += ceil(10000/20)*10=5000, rem=24365 // tier1 (0-40000) : 40000 pts, s += ceil(24365/10)*10=24370, rem=0 // total = 29370 sec ≈ 8h 9m const sec = timeToGain(startGl, xpCible); expect(sec).toBe(29370); }); it('avec recharge à t=5000 sec : XP total = seg1 + seg2', () => { // Seg1 : 50000 (tier2→tier1), 5000 sec → gainedIn(50000, 5000) = ? // tier2 (40000-50000) : a=10000, m=500, u=500 ticks → 10000 pts, tl=0 // → gained seg1 = 10000 // Recharge à 80000 à t=5000 // Seg2 : 80000 (tier3), 5000 sec → gainedIn(80000, 5000) = ? // tier3 (70000-80000) : a=10000, m=333, u=333 ticks → 9990, tl=167 // tier2 (40000-70000) : a=30000, m=1500, u=167 ticks → 3340 // → gained seg2 = 13330 // Total = 23330 XP const recharges: GaugeRecharge[] = [{ atSec: 5000, level: 80000 }]; const { gained } = computeGaugeState(startGl, recharges, Infinity, 10000); const expectedSeg1 = gainedIn(50000, 5000); const expectedSeg2 = gainedIn(80000, 5000); expect(gained).toBe(expectedSeg1 + expectedSeg2); }); it('la recharge fait redémarrer le countdown vers la cible', () => { // Après recharge : XP restante = xpCible - gained_so_far // Le countdown doit utiliser curGl (niveau après recharge et dépletion seg2) const rechargeEl = 5000; // 5000 sec = quand le joueur recharge const totalEl = 10000; const recharges: GaugeRecharge[] = [{ atSec: rechargeEl, level: 80000 }]; const { gained: gainedSoFar, curGl } = computeGaugeState(startGl, recharges, Infinity, totalEl); const xpRestante = Math.max(0, xpCible - gainedSoFar); // curGl doit refléter la jauge après seg2 (pas celle d'origine à 50000) expect(curGl).toBeGreaterThan(0); expect(curGl).toBeLessThan(80000); // la jauge a déjà décru depuis la recharge // timeToGain depuis curGl doit être < timeToGain depuis startGl (grâce à la recharge) const secAvecRecharge = timeToGain(curGl, xpRestante); const secSansRecharge = timeToGain(gainedIn(startGl, 5000) > 0 ? 0 : startGl, xpRestante); expect(secAvecRecharge).toBeLessThan(secSansRecharge === Infinity ? Infinity : secSansRecharge + 1); }); }); describe('Baffeur rechargé (sérenité)', () => { // Baffeur 70000 (tier2 → cap à -5000 séren.) // Séren. départ = 0, cap absolu = 5000 pts à gagner // À t=3000 sec, cap atteint → gel. Recharge à t=4000 → ne change rien (déjà gelé). const startGl = 70000; const ptsToAbsCap = 5000; // 0 - (-5000) it('gel au cap absolu même avec recharge après le gel', () => { const recharges: GaugeRecharge[] = [{ atSec: 4000, level: 90000 }]; // recharge après gel const { gained, effectiveEl } = computeGaugeState(startGl, recharges, ptsToAbsCap, 9999); // Gel = timeToGain(70000, 5000) : // tier2 (40000-70000) : 5000 pts → ceil(5000/20)*10 = 2500 sec expect(gained).toBe(5000); expect(effectiveEl).toBe(2500); // La recharge à t=4000 est APRÈS le gel à t=2500 → ignorée }); it('recharge AVANT le gel allonge le temps de traitement', () => { // Recharge à t=1000 (avant gel naturel à t=2500) // Seg1 : 70000, 1000 sec → gainedIn(70000, 1000) = 100 ticks // tier3 (70000-90000): a=0 (70000 non > 70000... wait 70000>70000=false) // Hmm: 70000 > 70000 = false, donc tier3 skip // tier2 (40000-70000): a=30000, m=1500, u=min(1500,100)=100, out=2000 // gained seg1 = 2000, jauge = 70000 - 100*20 = 68000 // Recharge à 90000 à t=1000 // Seg2 : 90000, besoin encore 3000 pts, timeToGain(90000,3000) : // tier3 (70000-90000): 20000 pts, 3000/30=100 ticks=1000 sec // effectiveEl = 1000 + 1000 = 2000 const recharges: GaugeRecharge[] = [{ atSec: 1000, level: 90000 }]; const { gained, effectiveEl } = computeGaugeState(70000, recharges, 5000, 9999); expect(gained).toBe(5000); expect(effectiveEl).toBe(2000); // plus rapide qu'à 2500 grâce au tier3 }); }); describe('Deux recharges successives (mangeoire longue session)', () => { it('accumule correctement trois segments', () => { // Seg1 : 30000 (tier1), 500 sec → 50 ticks × 10 = 500 pts // Recharge 1 à t=500 → 80000 // Seg2 : 80000 (tier2→tier3), 500 sec → gainedIn(80000, 500) // Recharge 2 à t=1000 → 95000 // Seg3 : 95000 (tier4), 500 sec → gainedIn(95000, 500) const recharges: GaugeRecharge[] = [ { atSec: 500, level: 80000 }, { atSec: 1000, level: 95000 }, ]; const { gained } = computeGaugeState(30000, recharges, Infinity, 1500); const expected = gainedIn(30000, 500) + gainedIn(80000, 500) + gainedIn(95000, 500); expect(gained).toBe(expected); }); it('le niveau XP estimé est cohérent avec les points accumulés', () => { const startXp = 1; // level 1 const startGl = 30000; const recharges: GaugeRecharge[] = [ { atSec: 500, level: 80000 }, { atSec: 1000, level: 95000 }, ]; const { gained } = computeGaugeState(startGl, recharges, Infinity, 1500); const estLevel = levelFromXp(xpForLevel(startXp) + gained); // Avec 3 segments : ~500 + ~gainedIn(80000,500) + ~gainedIn(95000,500) pts d'XP // ça devrait faire progresser significativement le niveau expect(estLevel).toBeGreaterThan(startXp); }); }); });