test-tables-creating-fix
Some checks failed
Deploy on push / deploy (push) Failing after 2m50s

This commit is contained in:
2025-08-19 15:48:12 +03:00
parent ddcf5630e2
commit b92594d6a7
3 changed files with 113 additions and 11 deletions

View File

@@ -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",

View File

@@ -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
View File

@@ -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" },