Files
core/tests/test_config.py
Untone 1b48675b92
Some checks failed
Deploy on push / deploy (push) Failing after 2m22s
[0.9.7] - 2025-08-18
### 🔄 Изменения
- **SQLAlchemy KeyError** - исправление ошибки `KeyError: Reaction` при инициализации
- **Исправлена ошибка SQLAlchemy**: Устранена проблема `InvalidRequestError: When initializing mapper Mapper[Shout(shout)], expression Reaction failed to locate a name (Reaction)`

### 🧪 Тестирование
- **Исправление тестов** - адаптация к новой структуре моделей
- **RBAC инициализация** - добавление `rbac.initialize_rbac()` в `conftest.py`
- **Создан тест для getSession**: Добавлен комплексный тест `test_getSession_cookies.py` с проверкой всех сценариев
- **Покрытие edge cases**: Тесты проверяют работу с валидными/невалидными токенами, отсутствующими пользователями
- **Мокирование зависимостей**: Использование unittest.mock для изоляции тестируемого кода

### 🔧 Рефакторинг
- **Упрощена архитектура**: Убраны сложные конструкции с отложенными импортами, заменены на чистую архитектуру
- **Перемещение моделей** - `Author` и связанные модели перенесены в `orm/author.py`: Вынесены базовые модели пользователей (`Author`, `AuthorFollower`, `AuthorBookmark`, `AuthorRating`) из `orm.author` в отдельный модуль
- **Устранены циклические импорты**: Разорван цикл между `auth.core` → `orm.community` → `orm.author` через реструктуризацию архитектуры
- **Создан модуль `utils/password.py`**: Класс `Password` вынесен в utils для избежания циклических зависимостей
- **Оптимизированы импорты моделей**: Убран прямой импорт `Shout` из `orm/community.py`, заменен на строковые ссылки

### 🔧 Авторизация с cookies
- **getSession теперь работает с cookies**: Мутация `getSession` теперь может получать токен из httpOnly cookies даже без заголовка Authorization
- **Убрано требование авторизации**: `getSession` больше не требует декоратор `@login_required`, работает автономно
- **Поддержка dual-авторизации**: Токен может быть получен как из заголовка Authorization, так и из cookie `session_token`
- **Автоматическая установка cookies**: Middleware автоматически устанавливает httpOnly cookies при успешном `getSession`
- **Обновлена GraphQL схема**: `SessionInfo` теперь содержит поля `success`, `error` и опциональные `token`, `author`
- **Единообразная обработка токенов**: Все модули теперь используют централизованные функции для работы с токенами
- **Улучшена обработка ошибок**: Добавлена детальная валидация токенов и пользователей в `getSession`
- **Логирование операций**: Добавлены подробные логи для отслеживания процесса авторизации

### 📝 Документация
- **Обновлена схема GraphQL**: `SessionInfo` тип теперь соответствует новому формату ответа
- Обновлена документация RBAC
- Обновлена документация авторизации с cookies
2025-08-18 14:25:25 +03:00

129 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Конфигурация для тестов
"""
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.routing import Route
from starlette.testclient import TestClient
# Импортируем все модели чтобы SQLAlchemy знал о них
from orm.author import ( # noqa: F401
Author,
AuthorBookmark,
AuthorFollower,
AuthorRating
)
from orm.collection import ShoutCollection # noqa: F401
from orm.community import Community, CommunityAuthor, CommunityFollower # noqa: F401
from orm.draft import Draft, DraftAuthor, DraftTopic # noqa: F401
from orm.invite import Invite # noqa: F401
from orm.notification import Notification # noqa: F401
from orm.shout import Shout, ShoutReactionsFollower, ShoutTopic # noqa: F401
from orm.topic import Topic, TopicFollower # noqa: F401
# Используем in-memory SQLite для тестов
TEST_DB_URL = "sqlite:///:memory:"
class DatabaseMiddleware(BaseHTTPMiddleware):
"""Middleware для внедрения сессии БД"""
def __init__(self, app, session_maker):
super().__init__(app)
self.session_maker = session_maker
async def dispatch(self, request, call_next):
session = self.session_maker()
request.state.db = session
try:
response = await call_next(request)
finally:
session.close()
return response
def create_test_app():
"""Create a test Starlette application."""
from importlib import import_module
from ariadne import load_schema_from_path, make_executable_schema
from ariadne.asgi import GraphQL
from starlette.responses import JSONResponse
from storage.db import Base
from storage.schema import resolvers
# Создаем движок и таблицы
engine = create_engine(
TEST_DB_URL,
connect_args={"check_same_thread": False},
poolclass=StaticPool,
echo=False,
)
Base.metadata.drop_all(bind=engine)
Base.metadata.create_all(bind=engine)
# Создаем фабрику сессий
session_local = sessionmaker(bind=engine)
# Импортируем резолверы для GraphQL
import_module("resolvers")
# Создаем схему GraphQL
schema = make_executable_schema(load_schema_from_path("schema/"), list(resolvers))
# Создаем кастомный GraphQL класс для тестов
class TestGraphQL(GraphQL):
async def get_context_for_request(self, request, data):
"""Переопределяем контекст для тестов"""
context = {
"request": None, # Устанавливаем None для активации тестового режима
"author": None,
"roles": [],
}
# Для тестов, если есть заголовок авторизации, создаем мок пользователя
auth_header = request.headers.get("authorization")
if auth_header and auth_header.startswith("Bearer "):
# Простая мок авторизация для тестов - создаем пользователя с ID 1
context["author"] = {"id": 1, "name": "Test User"}
context["roles"] = ["reader", "author"]
return context
# Создаем GraphQL приложение с кастомным классом
graphql_app = TestGraphQL(schema, debug=True)
async def graphql_handler(request):
"""Простой GraphQL обработчик для тестов"""
try:
return await graphql_app.handle_request(request)
except Exception as e:
return JSONResponse({"error": str(e)}, status_code=500)
# Создаем middleware для сессий
middleware = [Middleware(DatabaseMiddleware, session_maker=session_local)]
# Создаем тестовое приложение с GraphQL маршрутом
app = Starlette(
debug=True,
middleware=middleware,
routes=[
Route("/", graphql_handler, methods=["GET", "POST"]), # Основной GraphQL эндпоинт
Route("/graphql", graphql_handler, methods=["GET", "POST"]), # Альтернативный путь
],
)
return app, session_local
def get_test_client():
"""Get a test client with initialized database."""
app, session_local = create_test_app()
return TestClient(app), session_local