This commit is contained in:
@@ -50,7 +50,7 @@ dependencies = [
|
||||
# https://docs.astral.sh/uv/concepts/dependencies/#development-dependencies
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"fakeredis",
|
||||
"fakeredis[aioredis]",
|
||||
"pytest",
|
||||
"pytest-asyncio",
|
||||
"pytest-cov",
|
||||
@@ -61,7 +61,7 @@ dev = [
|
||||
]
|
||||
|
||||
test = [
|
||||
"fakeredis",
|
||||
"fakeredis[aioredis]",
|
||||
"pytest",
|
||||
"pytest-asyncio",
|
||||
"pytest-cov",
|
||||
|
||||
@@ -9,10 +9,46 @@ import requests
|
||||
import subprocess
|
||||
from typing import Optional
|
||||
from unittest.mock import patch
|
||||
import importlib
|
||||
|
||||
from orm.base import BaseModel as Base
|
||||
|
||||
|
||||
def force_create_all_tables(engine):
|
||||
"""
|
||||
Принудительно создает все таблицы, перезагружая модели если нужно.
|
||||
Это помогает в CI среде где могут быть проблемы с метаданными.
|
||||
"""
|
||||
from sqlalchemy import inspect
|
||||
import orm
|
||||
|
||||
# Перезагружаем все модули ORM для гарантии актуальности метаданных
|
||||
importlib.reload(orm.base)
|
||||
importlib.reload(orm.community)
|
||||
importlib.reload(orm.author)
|
||||
importlib.reload(orm.draft)
|
||||
importlib.reload(orm.shout)
|
||||
importlib.reload(orm.topic)
|
||||
importlib.reload(orm.reaction)
|
||||
importlib.reload(orm.invite)
|
||||
importlib.reload(orm.notification)
|
||||
importlib.reload(orm.collection)
|
||||
importlib.reload(orm.rating)
|
||||
|
||||
# Получаем обновленную Base
|
||||
from orm.base import BaseModel as Base
|
||||
|
||||
# Создаем все таблицы
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
# Проверяем результат
|
||||
inspector = inspect(engine)
|
||||
created_tables = inspector.get_table_names()
|
||||
print(f"🔧 Force created tables: {created_tables}")
|
||||
|
||||
return created_tables
|
||||
|
||||
|
||||
def get_test_client():
|
||||
"""
|
||||
Создает и возвращает тестовый клиент для интеграционных тестов.
|
||||
@@ -54,10 +90,24 @@ def test_engine():
|
||||
Создает тестовый engine для всей сессии тестирования.
|
||||
Использует in-memory SQLite для быстрых тестов.
|
||||
"""
|
||||
# Импортируем все модели, чтобы они были зарегистрированы
|
||||
# Принудительно импортируем ВСЕ модели чтобы они были зарегистрированы в Base.metadata
|
||||
# Это критично для CI среды где импорты могут работать по-разному
|
||||
import orm.base
|
||||
import orm.community
|
||||
import orm.author
|
||||
import orm.draft
|
||||
import orm.shout
|
||||
import orm.topic
|
||||
import orm.reaction
|
||||
import orm.invite
|
||||
import orm.notification
|
||||
import orm.collection
|
||||
import orm.rating
|
||||
|
||||
# Явно импортируем классы для гарантии регистрации
|
||||
from orm.base import BaseModel as Base
|
||||
from orm.community import Community, CommunityAuthor
|
||||
from orm.author import Author
|
||||
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
|
||||
@@ -65,7 +115,6 @@ def test_engine():
|
||||
from orm.invite import Invite
|
||||
from orm.notification import Notification
|
||||
from orm.collection import Collection
|
||||
from orm.author import AuthorFollower, AuthorRating
|
||||
|
||||
# Инициализируем RBAC систему
|
||||
import rbac
|
||||
@@ -89,8 +138,8 @@ def test_engine():
|
||||
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',
|
||||
'shout', 'shout_author', 'shout_topic', 'shout_reactions_followers', 'shout_collection',
|
||||
'topic', 'topic_followers', 'reaction', 'invite', 'notification', 'notification_seen',
|
||||
'collection', 'author_follower', 'author_rating', 'author_bookmark'
|
||||
]
|
||||
|
||||
@@ -113,7 +162,60 @@ def test_engine():
|
||||
|
||||
if still_missing:
|
||||
print(f"❌ Still missing tables after explicit creation: {still_missing}")
|
||||
raise RuntimeError(f"Failed to create required tables: {still_missing}")
|
||||
# Последняя попытка: создаем таблицы по одной
|
||||
print("🔄 Last attempt: creating tables one by one...")
|
||||
for table_name in still_missing:
|
||||
try:
|
||||
if table_name == 'community_author':
|
||||
CommunityAuthor.__table__.create(engine, checkfirst=True)
|
||||
elif table_name == 'community_follower':
|
||||
CommunityFollower.__table__.create(engine, checkfirst=True)
|
||||
elif table_name == 'author_follower':
|
||||
AuthorFollower.__table__.create(engine, checkfirst=True)
|
||||
elif table_name == 'author_rating':
|
||||
AuthorRating.__table__.create(engine, checkfirst=True)
|
||||
elif table_name == 'author_bookmark':
|
||||
AuthorBookmark.__table__.create(engine, checkfirst=True)
|
||||
elif table_name == 'draft_author':
|
||||
DraftAuthor.__table__.create(engine, checkfirst=True)
|
||||
elif table_name == 'draft_topic':
|
||||
DraftTopic.__table__.create(engine, checkfirst=True)
|
||||
elif table_name == 'shout_author':
|
||||
ShoutAuthor.__table__.create(engine, checkfirst=True)
|
||||
elif table_name == 'shout_topic':
|
||||
ShoutTopic.__table__.create(engine, checkfirst=True)
|
||||
elif table_name == 'shout_reactions_followers':
|
||||
ShoutReactionsFollower.__table__.create(engine, checkfirst=True)
|
||||
elif table_name == 'collection':
|
||||
Collection.__table__.create(engine, checkfirst=True)
|
||||
elif table_name == 'topic_followers':
|
||||
TopicFollower.__table__.create(engine, checkfirst=True)
|
||||
elif table_name == 'notification_seen':
|
||||
# notification_seen может быть частью notification модели
|
||||
pass
|
||||
print(f"✅ Created table {table_name}")
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to create table {table_name}: {e}")
|
||||
|
||||
# Финальная проверка
|
||||
inspector = inspect(engine)
|
||||
final_tables = inspector.get_table_names()
|
||||
final_missing = [table for table in required_tables if table not in final_tables]
|
||||
if final_missing:
|
||||
print(f"❌ Still missing tables after individual creation: {final_missing}")
|
||||
print("🔄 Last resort: forcing table creation with module reload...")
|
||||
try:
|
||||
final_tables = force_create_all_tables(engine)
|
||||
final_missing = [table for table in required_tables if table not in final_tables]
|
||||
if final_missing:
|
||||
raise RuntimeError(f"Failed to create required tables after all attempts: {final_missing}")
|
||||
else:
|
||||
print("✅ All missing tables created successfully with force creation")
|
||||
except Exception as e:
|
||||
print(f"❌ Force creation failed: {e}")
|
||||
raise RuntimeError(f"Failed to create required tables after all attempts: {final_missing}")
|
||||
else:
|
||||
print("✅ All missing tables created successfully in test_engine")
|
||||
else:
|
||||
print("✅ All missing tables created successfully in test_engine")
|
||||
except Exception as e:
|
||||
|
||||
4
uv.lock
generated
4
uv.lock
generated
@@ -479,7 +479,7 @@ requires-dist = [
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "fakeredis" },
|
||||
{ name = "fakeredis", extras = ["aioredis"] },
|
||||
{ name = "mypy" },
|
||||
{ name = "playwright" },
|
||||
{ name = "pytest" },
|
||||
@@ -493,7 +493,7 @@ lint = [
|
||||
{ name = "ruff" },
|
||||
]
|
||||
test = [
|
||||
{ name = "fakeredis" },
|
||||
{ name = "fakeredis", extras = ["aioredis"] },
|
||||
{ name = "playwright" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-asyncio" },
|
||||
|
||||
Reference in New Issue
Block a user