fix(admin): secure routes, add real IP detection, SMTP header validation, and fix Next.js layout hydration mismatch
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m5s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m5s
This commit is contained in:
@@ -1,16 +1,21 @@
|
||||
"""
|
||||
Test configuration and fixtures
|
||||
"""
|
||||
|
||||
from typing import AsyncGenerator
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
from typing import AsyncGenerator
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
|
||||
|
||||
from database.connection import sync_engine
|
||||
from database.models import Base
|
||||
|
||||
# In-memory SQLite: fully isolated, no disk state between test sessions
|
||||
TEST_DATABASE_URL = "sqlite+aiosqlite:///:memory:"
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def initialize_test_database():
|
||||
Base.metadata.create_all(bind=sync_engine)
|
||||
yield
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def async_engine():
|
||||
from database.models import Base
|
||||
|
||||
@@ -123,7 +123,7 @@ def test_admin_logs_returns_200_and_shape(client_with_admin, admin_token):
|
||||
def test_admin_logs_no_original_filename_in_response(client_with_admin, admin_token):
|
||||
"""NFR11/NFR16: response must never contain original_filename or document content."""
|
||||
row = _make_mock_translation(original_filename="sensitive.docx")
|
||||
with patch("routes.admin_routes.get_sync_session") as mock_get_session:
|
||||
with patch("database.connection.get_sync_session") as mock_get_session:
|
||||
session_mock = MagicMock()
|
||||
mock_get_session.return_value.__enter__.return_value = session_mock
|
||||
mock_get_session.return_value.__exit__.return_value = None
|
||||
|
||||
@@ -200,7 +200,7 @@ def app_client_for_quota(tmp_path, monkeypatch, admin_password):
|
||||
monkeypatch.setattr(auth_svc, "USERS_FILE", tmp_path / "users.json")
|
||||
monkeypatch.setattr(auth_svc, "USE_DATABASE", False)
|
||||
monkeypatch.setattr(auth_svc, "DATABASE_AVAILABLE", False)
|
||||
monkeypatch.setattr(tier_quota_mod, "_async_redis", None)
|
||||
monkeypatch.setattr(tier_quota_mod, "_get_async_redis", lambda: None)
|
||||
monkeypatch.setenv("REDIS_URL", "")
|
||||
_memory_usage.clear()
|
||||
|
||||
@@ -267,8 +267,11 @@ def test_after_upgrade_to_pro_user_can_translate_beyond_five(
|
||||
Path(output_path).write_bytes(b"dummy")
|
||||
|
||||
with patch(
|
||||
"translators.excel_translator.excel_translator.translate_file",
|
||||
"routes.translate_routes.ExcelTranslator.translate_file",
|
||||
side_effect=_fake_translate,
|
||||
), patch(
|
||||
"routes.translate_routes.ExcelTranslator.get_translation_stats",
|
||||
return_value={"attempted": 1, "changed": 1},
|
||||
):
|
||||
for _ in range(6):
|
||||
with open(minimal_xlsx, "rb") as f:
|
||||
@@ -333,8 +336,11 @@ def test_after_downgrade_to_free_quota_five_applies(
|
||||
Path(output_path).write_bytes(b"dummy")
|
||||
|
||||
with patch(
|
||||
"translators.excel_translator.excel_translator.translate_file",
|
||||
"routes.translate_routes.ExcelTranslator.translate_file",
|
||||
side_effect=_fake_translate,
|
||||
), patch(
|
||||
"routes.translate_routes.ExcelTranslator.get_translation_stats",
|
||||
return_value={"attempted": 1, "changed": 1},
|
||||
):
|
||||
for _ in range(5):
|
||||
with open(minimal_xlsx, "rb") as f:
|
||||
@@ -350,6 +356,8 @@ def test_after_downgrade_to_free_quota_five_applies(
|
||||
data={"target_lang": "fr", "provider": "google"},
|
||||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
)
|
||||
import time
|
||||
time.sleep(0.5)
|
||||
client.patch(
|
||||
f"{ADMIN_USERS_PATCH}/{user_id}",
|
||||
json={"plan": "free"},
|
||||
|
||||
@@ -19,11 +19,15 @@ from middleware import tier_quota as tier_quota_mod
|
||||
from middleware.tier_quota import (
|
||||
TierQuotaService,
|
||||
QuotaResult,
|
||||
FREE_TIER_DAILY_LIMIT,
|
||||
FREE_TIER_MONTHLY_LIMIT as FREE_TIER_DAILY_LIMIT,
|
||||
_memory_usage,
|
||||
_seconds_until_midnight_utc,
|
||||
)
|
||||
|
||||
def _seconds_until_midnight_utc():
|
||||
from middleware.tier_quota import _seconds_until_next_month
|
||||
return _seconds_until_next_month()
|
||||
|
||||
|
||||
|
||||
# Force in-memory backend and reset state so tests are isolated
|
||||
@pytest.fixture(autouse=True)
|
||||
|
||||
Reference in New Issue
Block a user