diff --git a/backend/app/modules/admin/io/service.py b/backend/app/modules/admin/io/service.py
index f2dcb98..eb515d8 100644
--- a/backend/app/modules/admin/io/service.py
+++ b/backend/app/modules/admin/io/service.py
@@ -5,7 +5,6 @@
from app.modules.events.models import Event
from app.modules.fields.models import Field
-from app.modules.tags.crud import get_or_create_tags
from app.modules.tags.models import Tag
from app.shared.models import EventField, EventTag
from app.shared.service import assert_db_empty
@@ -82,14 +81,19 @@ def import_bundle(bundle: ImportBundle, db: Session):
example=field_data.example,
)
db.add(field)
- db.flush() # Ensures ID is available before linking
+ db.flush()
field_map[field.name] = field
- all_tag_ids = {tag.id for tag in bundle.tags}
+ tag_definitions = {tag.id: tag for tag in bundle.tags}
+
+ all_tag_ids = set(tag_definitions.keys())
for event in bundle.events:
all_tag_ids.update(event.tags)
- _ = get_or_create_tags(db, list(all_tag_ids))
+ for tag_id in all_tag_ids:
+ tag_def = tag_definitions.get(tag_id)
+ db.add(Tag(id=tag_id, description=tag_def.description if tag_def else None))
+
db.flush()
for event_data in bundle.events:
diff --git a/backend/poetry.lock b/backend/poetry.lock
index 3d39345..7c5beeb 100644
--- a/backend/poetry.lock
+++ b/backend/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
+# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand.
[[package]]
name = "alembic"
@@ -524,7 +524,7 @@ description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
groups = ["main", "dev"]
-markers = "python_version < \"3.11\""
+markers = "python_version == \"3.10\""
files = [
{file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
{file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
@@ -1948,7 +1948,6 @@ files = [
[package.dependencies]
anyio = ">=3.6.2,<5"
-typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
[package.extras]
full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"]
@@ -2407,5 +2406,5 @@ files = [
[metadata]
lock-version = "2.1"
-python-versions = ">=3.9,<4"
-content-hash = "7297c0e1dc7606c2e5f436245c8409fd160018df453a711ac608184fd6a3335f"
+python-versions = ">=3.10,<4"
+content-hash = "e2cc3cd9f0a18673e0fc475a8e001ae908c8d40d78a4ba1fa6cf8e89d47d77fe"
diff --git a/backend/pyproject.toml b/backend/pyproject.toml
index 38c7d69..56187d0 100644
--- a/backend/pyproject.toml
+++ b/backend/pyproject.toml
@@ -7,7 +7,7 @@ authors = [
]
license = "MIT"
readme = "README.md"
-requires-python = ">=3.9,<4"
+requires-python = ">=3.10,<4"
dependencies = [
"fastapi[all] (>=0.115.12,<0.116.0)",
"sqlalchemy (>=2.0.40,<3.0.0)",
diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py
index a88ed12..385fc49 100644
--- a/backend/tests/conftest.py
+++ b/backend/tests/conftest.py
@@ -9,21 +9,36 @@
from app.modules.auth.token import create_access_token
from app.settings import Settings
-# Load test settings from .env.test
-test_settings = Settings(_env_file=".env.test")
-# Initialize DB objects for test (engine + SessionLocal)
-test_engine, TestingSessionLocal = init_db(test_settings)
+@pytest.fixture(scope="session")
+def test_settings():
+ """Session-scoped fixture for test settings, using a file-based SQLite DB."""
+ settings = Settings(_env_file=".env.test")
+ settings.database_url = "sqlite:///./test.db"
+ return settings
+
-# Create the test app
-app = create_app(test_settings, TestingSessionLocal)
+@pytest.fixture(scope="session")
+def db_engine_session(test_settings: Settings):
+ """Session-scoped fixture for the database engine and session factory."""
+ engine, session_local = init_db(test_settings)
+ return engine, session_local
-# Override FastAPI's get_db dependency
@pytest.fixture(scope="session")
-def override_get_db():
+def app(test_settings: Settings, db_engine_session):
+ """Session-scoped fixture for the FastAPI application instance."""
+ _, session_local = db_engine_session
+ return create_app(test_settings, session_local)
+
+
+@pytest.fixture(scope="session")
+def override_get_db(db_engine_session):
+ """Session-scoped fixture to override the `get_db` dependency."""
+ _, session_local = db_engine_session
+
def _override_get_db():
- db: Session = TestingSessionLocal()
+ db: Session = session_local()
try:
yield db
finally:
@@ -32,13 +47,16 @@ def _override_get_db():
return _override_get_db
-# Set up and tear down the database
@pytest.fixture(scope="session", autouse=True)
-def setup_database(override_get_db):
- Base.metadata.create_all(bind=test_engine)
+def setup_database(app, db_engine_session, override_get_db):
+ """
+ Session-scoped, autouse fixture to set up the database schema and dependency overrides.
+ """
+ engine, _ = db_engine_session
+ Base.metadata.create_all(bind=engine)
app.dependency_overrides[get_db] = override_get_db
yield
- Base.metadata.drop_all(bind=test_engine)
+ Base.metadata.drop_all(bind=engine)
@pytest.fixture(scope="session")
@@ -56,19 +74,20 @@ def test_user(override_get_db):
@pytest.fixture(scope="session")
def access_token(test_user):
+ """Create an access token for the test user."""
return create_access_token({"sub": str(test_user.email)})
-# FastAPI test client
@pytest.fixture(scope="module")
-def client():
+def client(app):
+ """Module-scoped test client for unauthenticated requests."""
with TestClient(app) as c:
yield c
@pytest.fixture
-def auth_client(access_token):
- # Create a fresh client for authenticated requests to avoid polluting the shared client
+def auth_client(app, access_token):
+ """Function-scoped test client for authenticated requests."""
with TestClient(app) as auth_client:
auth_client.headers.update({"Authorization": f"Bearer {access_token}"})
yield auth_client
diff --git a/backend/tests/test_events.py b/backend/tests/test_events.py
index c793673..3ea3a2e 100644
--- a/backend/tests/test_events.py
+++ b/backend/tests/test_events.py
@@ -5,7 +5,6 @@
@pytest.fixture
def sample_event():
- """Тестовое событие"""
return EventCreate(
name="Test Event",
description="Some event description.",
diff --git a/frontend/src/modules/auth/components/LoginForm.vue b/frontend/src/modules/auth/components/LoginForm.vue
index 7868b3f..81b5ecf 100644
--- a/frontend/src/modules/auth/components/LoginForm.vue
+++ b/frontend/src/modules/auth/components/LoginForm.vue
@@ -65,7 +65,12 @@ const onSubmit = handleSubmit(values => runLogin(values))
Email
-
+
@@ -76,7 +81,7 @@ const onSubmit = handleSubmit(values => runLogin(values))
Password
-
+
diff --git a/frontend/src/modules/auth/components/SignupForm.vue b/frontend/src/modules/auth/components/SignupForm.vue
index cb8fb06..9199188 100644
--- a/frontend/src/modules/auth/components/SignupForm.vue
+++ b/frontend/src/modules/auth/components/SignupForm.vue
@@ -68,7 +68,12 @@ const onSubmit = handleSubmit(values => runSignup(values))
Email
-
+
@@ -79,7 +84,7 @@ const onSubmit = handleSubmit(values => runSignup(values))
Password
-
+
diff --git a/frontend/src/modules/switchboard/components/ResetPanel.vue b/frontend/src/modules/switchboard/components/ResetPanel.vue
index 143a098..0104a9d 100644
--- a/frontend/src/modules/switchboard/components/ResetPanel.vue
+++ b/frontend/src/modules/switchboard/components/ResetPanel.vue
@@ -6,16 +6,11 @@ import { Button } from '@/shared/ui/button'
import { useEnhancedToast } from '@/shared/composables/useEnhancedToast'
import type { ResetPreview } from '../types'
import { Badge } from '@/shared/ui/badge'
-import { Icon } from '@iconify/vue'
const queryClient = useQueryClient()
const { showSuccess } = useEnhancedToast()
-const {
- data: preview,
- isLoading: isFetching,
- refetch: fetchPreview,
-} = useQuery({
+const { data: preview } = useQuery({
queryKey: ['resetPreview'],
queryFn: () => resetDatabase(true),
select: data => (data as ResetPreview).would_delete,
@@ -57,16 +52,6 @@ const { mutate: handleReset, isPending: isResetting } = useMutation({
{{ preview.tags }} tags
-
diff --git a/frontend/src/shared/composables/useAsyncTask.ts b/frontend/src/shared/composables/useAsyncTask.ts
deleted file mode 100644
index a5c5842..0000000
--- a/frontend/src/shared/composables/useAsyncTask.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { ref } from 'vue'
-import { useEnhancedToast } from '@/shared/composables/useEnhancedToast'
-
-export function useAsyncTask(minDelay = 400) {
- const isLoading = ref(false)
- const { showError } = useEnhancedToast()
-
- async function run(
- task: () => Promise,
- onSuccess?: (result: T) => void
- ): Promise {
- isLoading.value = true
- const start = Date.now()
-
- try {
- const result = await task()
- onSuccess?.(result)
-
- const elapsed = Date.now() - start
- const remaining = minDelay - elapsed
- if (remaining > 0) {
- await new Promise(resolve => setTimeout(resolve, remaining))
- }
-
- return result
- } catch (error) {
- showError(error)
- return null
- } finally {
- isLoading.value = false
- }
- }
-
- return {
- isLoading,
- run,
- }
-}