This commit is contained in:
@@ -50,7 +50,7 @@ dependencies = [
|
|||||||
# https://docs.astral.sh/uv/concepts/dependencies/#development-dependencies
|
# https://docs.astral.sh/uv/concepts/dependencies/#development-dependencies
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
dev = [
|
dev = [
|
||||||
"fakeredis",
|
"fakeredis[aioredis]",
|
||||||
"pytest",
|
"pytest",
|
||||||
"pytest-asyncio",
|
"pytest-asyncio",
|
||||||
"pytest-cov",
|
"pytest-cov",
|
||||||
@@ -61,7 +61,7 @@ dev = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
test = [
|
test = [
|
||||||
"fakeredis",
|
"fakeredis[aioredis]",
|
||||||
"pytest",
|
"pytest",
|
||||||
"pytest-asyncio",
|
"pytest-asyncio",
|
||||||
"pytest-cov",
|
"pytest-cov",
|
||||||
|
|||||||
@@ -9,10 +9,46 @@ import requests
|
|||||||
import subprocess
|
import subprocess
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
import importlib
|
||||||
|
|
||||||
from orm.base import BaseModel as Base
|
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():
|
def get_test_client():
|
||||||
"""
|
"""
|
||||||
Создает и возвращает тестовый клиент для интеграционных тестов.
|
Создает и возвращает тестовый клиент для интеграционных тестов.
|
||||||
@@ -54,10 +90,24 @@ def test_engine():
|
|||||||
Создает тестовый engine для всей сессии тестирования.
|
Создает тестовый engine для всей сессии тестирования.
|
||||||
Использует in-memory SQLite для быстрых тестов.
|
Использует 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.base import BaseModel as Base
|
||||||
from orm.community import Community, CommunityAuthor
|
from orm.community import Community, CommunityAuthor, CommunityFollower
|
||||||
from orm.author import Author
|
from orm.author import Author, AuthorFollower, AuthorRating, AuthorBookmark
|
||||||
from orm.draft import Draft, DraftAuthor, DraftTopic
|
from orm.draft import Draft, DraftAuthor, DraftTopic
|
||||||
from orm.shout import Shout, ShoutAuthor, ShoutTopic, ShoutReactionsFollower
|
from orm.shout import Shout, ShoutAuthor, ShoutTopic, ShoutReactionsFollower
|
||||||
from orm.topic import Topic, TopicFollower
|
from orm.topic import Topic, TopicFollower
|
||||||
@@ -65,7 +115,6 @@ def test_engine():
|
|||||||
from orm.invite import Invite
|
from orm.invite import Invite
|
||||||
from orm.notification import Notification
|
from orm.notification import Notification
|
||||||
from orm.collection import Collection
|
from orm.collection import Collection
|
||||||
from orm.author import AuthorFollower, AuthorRating
|
|
||||||
|
|
||||||
# Инициализируем RBAC систему
|
# Инициализируем RBAC систему
|
||||||
import rbac
|
import rbac
|
||||||
@@ -89,8 +138,8 @@ def test_engine():
|
|||||||
required_tables = [
|
required_tables = [
|
||||||
'author', 'community', 'community_author', 'community_follower',
|
'author', 'community', 'community_author', 'community_follower',
|
||||||
'draft', 'draft_author', 'draft_topic',
|
'draft', 'draft_author', 'draft_topic',
|
||||||
'shout', 'shout_author', 'shout_topic', 'shout_reactions_followers',
|
'shout', 'shout_author', 'shout_topic', 'shout_reactions_followers', 'shout_collection',
|
||||||
'topic', 'topic_followers', 'reaction', 'invite', 'notification',
|
'topic', 'topic_followers', 'reaction', 'invite', 'notification', 'notification_seen',
|
||||||
'collection', 'author_follower', 'author_rating', 'author_bookmark'
|
'collection', 'author_follower', 'author_rating', 'author_bookmark'
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -113,7 +162,60 @@ def test_engine():
|
|||||||
|
|
||||||
if still_missing:
|
if still_missing:
|
||||||
print(f"❌ Still missing tables after explicit creation: {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:
|
else:
|
||||||
print("✅ All missing tables created successfully in test_engine")
|
print("✅ All missing tables created successfully in test_engine")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
4
uv.lock
generated
4
uv.lock
generated
@@ -479,7 +479,7 @@ requires-dist = [
|
|||||||
|
|
||||||
[package.metadata.requires-dev]
|
[package.metadata.requires-dev]
|
||||||
dev = [
|
dev = [
|
||||||
{ name = "fakeredis" },
|
{ name = "fakeredis", extras = ["aioredis"] },
|
||||||
{ name = "mypy" },
|
{ name = "mypy" },
|
||||||
{ name = "playwright" },
|
{ name = "playwright" },
|
||||||
{ name = "pytest" },
|
{ name = "pytest" },
|
||||||
@@ -493,7 +493,7 @@ lint = [
|
|||||||
{ name = "ruff" },
|
{ name = "ruff" },
|
||||||
]
|
]
|
||||||
test = [
|
test = [
|
||||||
{ name = "fakeredis" },
|
{ name = "fakeredis", extras = ["aioredis"] },
|
||||||
{ name = "playwright" },
|
{ name = "playwright" },
|
||||||
{ name = "pytest" },
|
{ name = "pytest" },
|
||||||
{ name = "pytest-asyncio" },
|
{ name = "pytest-asyncio" },
|
||||||
|
|||||||
Reference in New Issue
Block a user