Testare & calitate
Ce garantează corectitudinea codului: tipuri stricte, validare la runtime și o suită automată de teste rulată la fiecare schimbare.
Corectitudinea codului Recash este garantată pe mai multe straturi, de la compilare până la integrare continuă:
- tipuri stricte, verificate la compilare, pe tot fluxul de date
- validare la runtime pentru orice date venite de la client
- teste unitare pentru logica din
lib/ - teste de integrare pentru rutele API critice
- teste end-to-end pentru interfața publică și pentru fluxul complet al platformei
- teste de încărcare care măsoară comportamentul sub trafic concurent
- integrare continuă care rulează teste la fiecare schimbare
TypeScript
Limbajul TypeScript a fost ales pentru a asigura calitatea. Fiecare model din baza de date generează tipuri exacte, folosite din backend până în componentele de interfață. Un câmp lipsă sau un tip greșit de date devine eroare de compilare, descoperită înainte ca vreun utilizator să ajungă la ea.
Validare la runtime
TypeScript verifică doar la compilare — un client HTTP poate trimite, tehnic, orice. De aceea fiecare payload care ajunge la o rută API trece printr-o schemă Zod înainte să atingă baza de date: numărul de sticle este limitat la un interval rezonabil, descrierea nu poate conține date de contact ascunse, valoarea garanției este verificată să fie pozitivă și plauzibilă. Practic, un set de verificări de contract rulate live, pe fiecare cerere primită.
Teste unitare
Logica de business stă izolată în funcții din lib/, deci poate fi testată direct, fără mock-uri și fără baza de date. Suita de teste unitare (Vitest) acoperă exact aceste funcții:
- calculul câștigurilor — valoarea garanției SGR de 0,5 RON pe sticlă, rotunjirea la 0,5 RON și împărțirea între poster și colector
- schemele de validare — aceleași scheme Zod care păzesc rutele API, verificate pe payload-uri valide și invalide
- badge-urile și reputația — ce insigne se acordă la fiecare prag și cum se cumulează
- programul de disponibilitate — dacă o postare este activă acum, evaluat în fusul Europe/Bucharest
- utilitare geografice — distanța haversine între două puncte și validarea identificatorilor din baza de date

Teste de integrare
Peste testele unitare, un al doilea nivel verifică rutele API critice ale fluxului de tranzacție — creare, revendicare, aprobare, finalizare — la nivelul handler-ului. Aici testăm regulile de business complete: cine are voie să facă fiecare acțiune, în ce stare trebuie să fie postarea, ce se întâmplă la o cerere invalidă.
Baza de date, Redis, autentificarea și notificările sunt înlocuite cu mock-uri, așa că testele rulează rapid și determinist, fără infrastructură externă. Ele confirmă că logica rutei ia deciziile corecte — nu că MongoDB răspunde, ci că handler-ul reacționează corect la ce primește de la baza de date.
Teste end-to-end
Fluxurile sunt verificate și din exterior, prin browser și prin API, cu Playwright. Suita e2e are două configurații, cu scopuri diferite.
Interfața publică, rulată la fiecare schimbare, acoperă paginile care nu cer autentificare — pagina principală, harta. Nu are nevoie de backend: paginile publice se randează pe server și degradează grațios când baza de date lipsește, iar paginile care cer date le primesc din intercepții deterministe. Se verifică inclusiv ca rutele protejate să redirecționeaze un vizitator neautentificat.
Fluxul complet autentificat — creare → revendicare → aprobare → finalizare cu cod — rulează pe un backend efemer, complet izolat: o replică MongoDB în memorie și un Redis în proces, pornite și oprite automat de test. Sesiunile celor doi utilizatori (poster și colector) sunt generate direct din secretul de autentificare, fără un ocol prin Google OAuth. Este cel mai apropiat test de o tranzacție reală, de la un capăt la altul.

Teste de încărcare
Un ultim strat măsoară cum se comportă aplicația sub trafic concurent și, mai important, cum cedează atunci când este împinsă dincolo de limite: se apără singură — răspunde cu 429 și scapă de excesul de cereri — sau se prăbușește, cu erori 5xx și timeout-uri. Testele sunt scrise pentru k6 și rulează întotdeauna pe o versiune de producție, nu pe serverul de dezvoltare, unde cifrele nu ar însemna nimic.
Scenariile
Suita acoperă o gamă de tipare de trafic, fiecare răspunzând la o întrebare diferită:
- smoke — funcționează endpoint-urile la un trafic minim? Verificarea de funcționare rulată prima
- load — rămâne aplicația funcțională la scara de trafic așteptată, cu latența sub prag?
- stress — unde este punctul de rupere? Traficul urcă în trepte până când sistemul începe să cedeze
- spike — supraviețuiește unui vârf brusc de trafic și, mai ales, își revine după el?
- ratelimit — inundat de cereri de la un singur IP, aplicația scapă de exces controlat, nu cedează?
Criteriul de trecere
Distincția pe care o trag toate scenariile este una singură: 429 înseamnă că aplicația se protejează (bine), iar 5xx sau timeout înseamnă că cedează (rău). De aceea pragurile pică rularea doar la eșecuri reale — erori de server, latență peste limită, cereri pierdute — niciodată la un 429, care este exact răspunsul dorit sub abuz.
Toate scenariile din suita implicită sunt read-only: lovesc doar rutele de citire, fără să modifice date, deci pot fi rulate în siguranță chiar și împotriva unei baze de date populate.
Integrare continuă
Totul se leagă printr-un flux de integrare continuă (GitHub Actions) care rulează la fiecare push pe main și la fiecare pull request, în trei sarcini paralele:
- testele unitare și de integrare (Vitest)
- interfața publică (Playwright), pe o versiune de producție construită în același pas
- fluxul complet autentificat (Playwright), pe backend-ul efemer izolat
Rapoartele Playwright sunt salvate ca artefacte la fiecare rulare, iar rulările vechi pentru aceeași ramură sunt anulate automat când apare una nouă. Testele de încărcare, fiind costisitoare și țintind un mediu real, se pornesc manual, la cerere, cu alegerea scenariului și a țintei.

