test: add test suite with 65 tests, 73% coverage

- Unit tests: password hashing, notification providers, webhook field parsing
- Integration tests: auth routes (register/login/confirm-email/logout),
  invite flow, Evotor webhooks (/user/create, /user/verify, /user/token),
  admin panel (access control, activate/suspend/delete/reset-password)
- conftest: SQLite in-memory engine, transactional sessions, factory-boy
  factories (UserFactory with UserRoleEnum variants)
- Fix bcrypt: replace passlib (broken on Python 3.14 + bcrypt 5.x) with
  direct bcrypt calls; drop passlib from requirements.txt
- Fix datetime.utcnow() deprecation across routes and tests
- Fix Jinja2 TemplateResponse signature (request as first positional arg)
- Add coverage config to pyproject.toml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mguschin
2026-04-28 12:27:42 +03:00
parent 5ead89e0cf
commit fc65e591b3
18 changed files with 882 additions and 31 deletions

View File

@@ -8,7 +8,7 @@ POST /user/token — Evotor sends us its own API token for the user.
import json
import logging
import secrets
from datetime import datetime, timedelta
from datetime import datetime, timezone, timedelta
from typing import Any
from fastapi import APIRouter, Depends, Request
@@ -64,7 +64,7 @@ def _upsert_evotor_connection(
conn = db.query(EvotorConnection).filter(
EvotorConnection.evotor_user_id == evotor_user_id
).first()
now = datetime.utcnow()
now = datetime.now(timezone.utc).replace(tzinfo=None)
if conn:
conn.api_token = api_token
if user_id is not None:
@@ -120,7 +120,7 @@ async def user_create(request: Request, db: Session = Depends(get_db)):
if user is None and phone:
user = db.query(User).filter(User.phone == phone).first()
now = datetime.utcnow()
now = datetime.now(timezone.utc).replace(tzinfo=None)
if user:
# Link Evotor to existing user
@@ -246,7 +246,7 @@ async def user_token(request: Request, db: Session = Depends(get_db)):
conn = db.query(EvotorConnection).filter(
EvotorConnection.evotor_user_id == evotor_user_id
).first()
now = datetime.utcnow()
now = datetime.now(timezone.utc).replace(tzinfo=None)
if conn:
conn.access_token = evotor_token
conn.is_online = True