From 53e03d8907b9097444bca60aab0a0646c77019ff Mon Sep 17 00:00:00 2001 From: Ivan Skvortsov Date: Tue, 17 Feb 2026 11:20:42 +0100 Subject: [PATCH 1/7] Deleted useAsyncTask --- .../src/shared/composables/useAsyncTask.ts | 38 ------------------- 1 file changed, 38 deletions(-) delete mode 100644 frontend/src/shared/composables/useAsyncTask.ts 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, - } -} From 41d178f02a136103b3a036270fbc7de572aa1422 Mon Sep 17 00:00:00 2001 From: Ivan Skvortsov Date: Tue, 17 Feb 2026 11:23:38 +0100 Subject: [PATCH 2/7] Auth forms autocomplete --- frontend/src/modules/auth/components/LoginForm.vue | 9 +++++++-- frontend/src/modules/auth/components/SignupForm.vue | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) 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 - + From 52bd0b783c03cc47c1f050e14bdd6ee12305e985 Mon Sep 17 00:00:00 2001 From: Ivan Skvortsov Date: Tue, 17 Feb 2026 11:33:02 +0100 Subject: [PATCH 3/7] Tag import fix --- backend/app/modules/admin/io/service.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) 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: From bd7ed6c7be848dfaf673ed9b774a8e11a89722db Mon Sep 17 00:00:00 2001 From: Ivan Skvortsov Date: Tue, 17 Feb 2026 11:46:01 +0100 Subject: [PATCH 4/7] Run tests on local sqlite only --- backend/tests/conftest.py | 2 +- backend/tests/test_events.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index a88ed12..54ca1c8 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -9,8 +9,8 @@ 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") +test_settings.database_url = "sqlite:///./test.db" # Initialize DB objects for test (engine + SessionLocal) test_engine, TestingSessionLocal = init_db(test_settings) 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.", From e5dbfc92b2e435dba264a11fab2f1936e1a23452 Mon Sep 17 00:00:00 2001 From: Ivan Skvortsov Date: Tue, 17 Feb 2026 12:13:04 +0100 Subject: [PATCH 5/7] Improved backend test config --- backend/poetry.lock | 9 +++---- backend/pyproject.toml | 2 +- backend/tests/conftest.py | 52 +++++++++++++++++++++++++++------------ 3 files changed, 41 insertions(+), 22 deletions(-) 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 54ca1c8..80049f9 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -9,21 +9,37 @@ from app.modules.auth.token import create_access_token from app.settings import Settings -test_settings = Settings(_env_file=".env.test") -test_settings.database_url = "sqlite:///./test.db" -# 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 + + +@pytest.fixture(scope="session") +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) # Override FastAPI's get_db dependency @pytest.fixture(scope="session") -def override_get_db(): +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 +48,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 +75,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 From 86f0d3f8802255afba30382f109a49aea653618d Mon Sep 17 00:00:00 2001 From: Ivan Skvortsov Date: Tue, 17 Feb 2026 12:14:17 +0100 Subject: [PATCH 6/7] Uncomment --- backend/tests/conftest.py | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index 80049f9..385fc49 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -32,7 +32,6 @@ def app(test_settings: Settings, db_engine_session): return create_app(test_settings, session_local) -# Override FastAPI's get_db dependency @pytest.fixture(scope="session") def override_get_db(db_engine_session): """Session-scoped fixture to override the `get_db` dependency.""" From aaabadfa705bb6796c1601dd187c959fddfdac13 Mon Sep 17 00:00:00 2001 From: Ivan Skvortsov Date: Tue, 17 Feb 2026 12:17:01 +0100 Subject: [PATCH 7/7] Removed update button from the Reset switchboard panel --- .../switchboard/components/ResetPanel.vue | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) 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 -