Files
mrleo1nidandClaude Sonnet 5.5 bbbcc6a5d8 chore: add CI, lint config, login rate limit and split large modules
- Rate-limit failed logins (8 per 15 min per IP or username, 429 + Retry-After)
- Add ruff/mypy config and GitHub Actions workflow (ruff, pytest, tsc, tests, build)
- Apply ruff autofixes (import order, pyupgrade)
- Extract rpy_helpers.py, engine-util.ts, TranslationParts.tsx and
  translation-types.ts from oversized modules
- Add dependency-free React UI tests
- Refresh README and ARCHITECTURE (Naninovel/Tomie, status and plans)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 11:23:22 +03:00

26 lines
841 B
Python

from app.ratelimit import FailureLimiter
def test_blocks_after_max_failures_and_resets():
limiter = FailureLimiter(max_failures=3, window_seconds=60)
for _ in range(2):
limiter.fail("ip:1", "user:a")
assert limiter.retry_after("ip:1", "user:a") == 0
limiter.fail("ip:1", "user:a")
assert limiter.retry_after("ip:1", "user:a") > 0
assert limiter.retry_after("ip:2", "user:b") == 0
limiter.reset("ip:1", "user:a")
assert limiter.retry_after("ip:1", "user:a") == 0
def test_window_expires(monkeypatch):
import app.ratelimit as rl
now = [1000.0]
monkeypatch.setattr(rl.time, "monotonic", lambda: now[0])
limiter = FailureLimiter(max_failures=1, window_seconds=10)
limiter.fail("k")
assert limiter.retry_after("k") > 0
now[0] += 11
assert limiter.retry_after("k") == 0