feature/e2e #4

Merged
to merged 22 commits from feature/e2e into dev 2025-08-20 17:21:31 +00:00
144 changed files with 7356 additions and 3787 deletions
Showing only changes of commit ddcf5630e2 - Show all commits

View File

@@ -65,7 +65,6 @@ def test_engine():
from orm.invite import Invite
from orm.notification import Notification
from orm.collection import Collection
from orm.shout import ShoutReactionsFollower
from orm.author import AuthorFollower, AuthorRating
# Инициализируем RBAC систему
@@ -79,6 +78,49 @@ def test_engine():
# Принудительно удаляем все таблицы и создаем заново
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
# Debug: проверяем какие таблицы созданы на уровне сессии
from sqlalchemy import inspect
inspector = inspect(engine)
session_tables = inspector.get_table_names()
print(f"🔍 Created tables in test_engine fixture: {session_tables}")
# Проверяем что все критические таблицы созданы
required_tables = [
'author', 'community', 'community_author', 'community_follower',
'draft', 'draft_author', 'draft_topic',
'shout', 'shout_author', 'shout_topic', 'shout_reactions_followers',
'topic', 'topic_followers', 'reaction', 'invite', 'notification',
'collection', 'author_follower', 'author_rating', 'author_bookmark'
]
missing_tables = [table for table in required_tables if table not in session_tables]
if missing_tables:
print(f"❌ Missing tables in test_engine: {missing_tables}")
print(f"Available tables: {session_tables}")
# Fallback: попробуем создать отсутствующие таблицы явно
print("🔄 Attempting to create missing tables explicitly in test_engine...")
try:
# Создаем все таблицы снова с принудительным импортом моделей
Base.metadata.create_all(engine)
# Проверяем снова
inspector = inspect(engine)
updated_tables = inspector.get_table_names()
still_missing = [table for table in required_tables if table not in updated_tables]
if still_missing:
print(f"❌ Still missing tables after explicit creation: {still_missing}")
raise RuntimeError(f"Failed to create required tables: {still_missing}")
else:
print("✅ All missing tables created successfully in test_engine")
except Exception as e:
print(f"❌ Failed to create missing tables in test_engine: {e}")
raise
else:
print("✅ All required tables created in test_engine")
yield engine
@@ -103,12 +145,87 @@ def db_session(test_session_factory, test_engine):
# Принудительно пересоздаем таблицы для каждого теста
from orm.base import BaseModel as Base
from sqlalchemy import inspect
# Убеждаемся что все модели импортированы перед созданием таблиц
# Явно импортируем все модели чтобы они были зарегистрированы в Base.metadata
from orm.community import Community, CommunityAuthor, CommunityFollower
from orm.author import Author, AuthorFollower, AuthorRating, AuthorBookmark
from orm.draft import Draft, DraftAuthor, DraftTopic
from orm.shout import Shout, ShoutAuthor, ShoutTopic, ShoutReactionsFollower
from orm.topic import Topic, TopicFollower
from orm.reaction import Reaction
from orm.invite import Invite
from orm.notification import Notification
from orm.collection import Collection
# Проверяем что все модели зарегистрированы
print(f"🔍 Registered tables in Base.metadata: {list(Base.metadata.tables.keys())}")
# Убеждаемся что все критические таблицы зарегистрированы в metadata
required_tables = [
'author', 'community', 'community_author', 'community_follower',
'draft', 'draft_author', 'draft_topic',
'shout', 'shout_author', 'shout_topic', 'shout_reactions_followers',
'topic', 'topic_followers', 'reaction', 'invite', 'notification',
'collection', 'author_follower', 'author_rating', 'author_bookmark'
]
missing_metadata_tables = [table for table in required_tables if table not in Base.metadata.tables]
if missing_metadata_tables:
print(f"❌ Missing tables in Base.metadata: {missing_metadata_tables}")
print("Available tables:", list(Base.metadata.tables.keys()))
raise RuntimeError(f"Critical tables not registered in Base.metadata: {missing_metadata_tables}")
else:
print("✅ All required tables registered in Base.metadata")
# Удаляем все таблицы
Base.metadata.drop_all(test_engine)
# Создаем таблицы заново
Base.metadata.create_all(test_engine)
# Debug: проверяем какие таблицы созданы
inspector = inspect(test_engine)
created_tables = inspector.get_table_names()
print(f"🔍 Created tables in db_session fixture: {created_tables}")
# Проверяем что все критические таблицы созданы
required_tables = [
'author', 'community', 'community_author', 'community_follower',
'draft', 'draft_author', 'draft_topic',
'shout', 'shout_author', 'shout_topic', 'shout_reactions_followers',
'topic', 'topic_followers', 'reaction', 'invite', 'notification',
'collection', 'author_follower', 'author_rating', 'author_bookmark'
]
missing_tables = [table for table in required_tables if table not in created_tables]
if missing_tables:
print(f"❌ Missing tables in db_session: {missing_tables}")
print(f"Available tables: {created_tables}")
# Fallback: попробуем создать отсутствующие таблицы явно
print("🔄 Attempting to create missing tables explicitly in db_session...")
try:
# Создаем все таблицы снова с принудительным импортом моделей
Base.metadata.create_all(test_engine)
# Проверяем снова
inspector = inspect(test_engine)
updated_tables = inspector.get_table_names()
still_missing = [table for table in required_tables if table not in updated_tables]
if still_missing:
print(f"❌ Still missing tables after explicit creation: {still_missing}")
raise RuntimeError(f"Failed to create required tables: {still_missing}")
else:
print("✅ All missing tables created successfully in db_session")
except Exception as e:
print(f"❌ Failed to create missing tables in db_session: {e}")
raise
else:
print("✅ All required tables created in db_session")
# Проверяем что таблица draft создана с правильной схемой
inspector = inspect(test_engine)
@@ -582,18 +699,27 @@ def redis_client():
return redis_service._client
# Mock для Redis если он недоступен
@pytest.fixture
def mock_redis_if_unavailable():
"""Мокает Redis если он недоступен - для тестов которые нуждаются в Redis"""
def fake_redis():
"""Создает fakeredis экземпляр для тестов"""
try:
import fakeredis.aioredis
# Используем fakeredis для тестов
with patch('storage.redis.redis') as mock_redis:
# Создаем fakeredis сервер
fake_redis = fakeredis.aioredis.FakeRedis()
return fakeredis.aioredis.FakeRedis()
except ImportError:
pytest.skip("fakeredis не установлен - установите: pip install fakeredis[aioredis]")
@pytest.fixture
def redis_service_mock(fake_redis):
"""Создает мок RedisService с fakeredis"""
try:
import fakeredis.aioredis
with patch('storage.redis.RedisService') as mock_service:
# Создаем экземпляр с fakeredis
mock_service.return_value._client = fake_redis
# Создаем mock для execute метода, который эмулирует поведение RedisService.execute
# Эмулируем execute метод
async def mock_execute(command: str, *args):
cmd_method = getattr(fake_redis, command.lower(), None)
if cmd_method is not None:
@@ -603,16 +729,66 @@ def mock_redis_if_unavailable():
return cmd_method
return None
# Патчим методы Redis
mock_service.return_value.execute = mock_execute
mock_service.return_value.get = fake_redis.get
mock_service.return_value.set = fake_redis.set
mock_service.return_value.delete = fake_redis.delete
mock_service.return_value.exists = fake_redis.exists
mock_service.return_value.hset = fake_redis.hset
mock_service.return_value.hget = fake_redis.hget
mock_service.return_value.hgetall = fake_redis.hgetall
mock_service.return_value.hdel = fake_redis.hdel
mock_service.return_value.expire = fake_redis.expire
mock_service.return_value.ttl = fake_redis.ttl
mock_service.return_value.keys = fake_redis.keys
mock_service.return_value.scan = fake_redis.scan
yield mock_service.return_value
except ImportError:
pytest.skip("fakeredis не установлен - установите: pip install fakeredis[aioredis]")
# Используем fakeredis для тестов Redis
@pytest.fixture
def mock_redis_if_unavailable():
"""Заменяет Redis на fakeredis для тестов - более реалистичная имитация Redis"""
try:
import fakeredis.aioredis
# Создаем fakeredis сервер
fake_redis = fakeredis.aioredis.FakeRedis()
# Патчим глобальный redis экземпляр
with patch('storage.redis.redis') as mock_redis:
# Эмулируем RedisService.execute метод
async def mock_execute(command: str, *args):
cmd_method = getattr(fake_redis, command.lower(), None)
if cmd_method is not None:
if hasattr(cmd_method, '__call__'):
return await cmd_method(*args)
else:
return cmd_method
return None
# Патчим все основные методы Redis
mock_redis.execute = mock_execute
mock_redis.get = fake_redis.get
mock_redis.set = fake_redis.set
mock_redis.delete = fake_redis.delete
mock_redis.exists = fake_redis.exists
mock_redis.ping = fake_redis.ping
mock_redis.hset = fake_redis.hset
mock_redis.hget = fake_redis.hget
mock_redis.hgetall = fake_redis.hgetall
mock_redis.hdel = fake_redis.hdel
mock_redis.expire = fake_redis.expire
mock_redis.ttl = fake_redis.ttl
mock_redis.keys = fake_redis.keys
mock_redis.scan = fake_redis.scan
mock_redis.is_connected = True
# Добавляем async методы для connect/disconnect
# Async методы для connect/disconnect
async def mock_connect():
return True
@@ -623,29 +799,9 @@ def mock_redis_if_unavailable():
mock_redis.disconnect = mock_disconnect
yield
except ImportError:
# fakeredis не установлен, используем базовый mock
with patch('storage.redis.redis') as mock_redis:
# Создаем базовый mock для Redis методов
mock_redis.execute.return_value = None
mock_redis.get.return_value = None
mock_redis.set.return_value = True
mock_redis.delete.return_value = True
mock_redis.exists.return_value = False
mock_redis.ping.return_value = True
mock_redis.is_connected = False
# Добавляем async методы для connect/disconnect
async def mock_connect():
return True
async def mock_disconnect():
pass
mock_redis.connect = mock_connect
mock_redis.disconnect = mock_disconnect
yield
pytest.skip("fakeredis не установлен - установите: pip install fakeredis[aioredis]")
@pytest.fixture(autouse=True)