"""Teste F5: `_hits` nu creste monoton pe viata procesului. Cheile ale caror timestamp-uri au expirat complet trebuie sterse din `_hits` (nu doar golite), altfel dictionarul acumuleaza o intrare per IP vazut vreodata. Semantica ferestrei glisante (permite/blocheaza) trebuie sa ramana neschimbata. """ from __future__ import annotations import pytest from app.web import ratelimit from app.web.ratelimit import check_rate_limit @pytest.fixture(autouse=True) def _clean_hits(): ratelimit._hits.clear() yield ratelimit._hits.clear() def test_cheie_blocata_fara_timestamp_uri_valide_e_stearsa(monkeypatch): """max_hits=0 -> orice cerere e blocata; daca fereastra a expirat complet (filtrarea produce o lista goala), cheia nu trebuie sa ramana in `_hits`.""" now = [1000.0] monkeypatch.setattr(ratelimit.time, "monotonic", lambda: now[0]) key = "ip_blocat_gol" assert check_rate_limit(key, max_hits=0, window_s=60) is False # Nimic adaugat (max_hits=0 blocheaza tot) si lista era goala -> cheia disparuta. assert key not in ratelimit._hits def test_cheie_activa_ramane_dupa_permis(monkeypatch): """O cerere permisa isi adauga timestamp-ul -> cheia activa ramane in `_hits`.""" now = [1000.0] monkeypatch.setattr(ratelimit.time, "monotonic", lambda: now[0]) key = "ip_activ" assert check_rate_limit(key, max_hits=2, window_s=60) is True assert key in ratelimit._hits assert ratelimit._hits[key] == [1000.0] def test_semantica_max_hits_si_fereastra_neschimbata(monkeypatch): """max_hits atinse -> False; dupa expirarea ferestrei -> True din nou.""" now = [1000.0] monkeypatch.setattr(ratelimit.time, "monotonic", lambda: now[0]) key = "ip_fereastra" assert check_rate_limit(key, max_hits=2, window_s=10) is True assert check_rate_limit(key, max_hits=2, window_s=10) is True # A treia cerere in aceeasi fereastra -> blocata. assert check_rate_limit(key, max_hits=2, window_s=10) is False # Trece timpul peste fereastra -> toate timestamp-urile expira. now[0] += 11 assert check_rate_limit(key, max_hits=2, window_s=10) is True # Doar noua cerere ramane in lista (cele vechi, expirate, au fost curatate). assert ratelimit._hits[key] == [1011.0] def test_cheie_dispare_dupa_expirare_completa_si_reblocare(monkeypatch): """O cheie blocata, ramasa fara timestamp-uri valide dupa expirarea ferestrei (fara sa mai apara alte cereri intre timp), nu trebuie sa ramana in `_hits` la urmatorul apel care o gaseste goala si tot o blocheaza (max_hits=0).""" now = [1000.0] monkeypatch.setattr(ratelimit.time, "monotonic", lambda: now[0]) key = "ip_reblocat" check_rate_limit(key, max_hits=0, window_s=5) assert key not in ratelimit._hits now[0] += 100 assert check_rate_limit(key, max_hits=0, window_s=5) is False assert key not in ratelimit._hits