diff --git a/CHANGELOG.md b/CHANGELOG.md index d785dc2a9..2370d20e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to ## [Unreleased] +### Fixed + +- 🐛(backend) close thread DB connections to fix test teardown + OperationalError #2385 + ## [v5.2.0] - 2026-06-03 ### Added diff --git a/src/backend/core/tests/documents/test_api_documents_children_create.py b/src/backend/core/tests/documents/test_api_documents_children_create.py index 355e9236c..d5e25f7bb 100644 --- a/src/backend/core/tests/documents/test_api_documents_children_create.py +++ b/src/backend/core/tests/documents/test_api_documents_children_create.py @@ -8,6 +8,8 @@ from unittest import mock from unittest.mock import patch from uuid import uuid4 +from django.db import connection + import pytest from rest_framework.test import APIClient @@ -286,12 +288,17 @@ def test_api_documents_create_document_children_race_condition(): factories.UserDocumentAccessFactory(user=user, document=document, role="owner") def create_document(): - return client.post( - f"/api/v1.0/documents/{document.id}/children/", - { - "title": "my child", - }, - ) + try: + return client.post( + f"/api/v1.0/documents/{document.id}/children/", + { + "title": "my child", + }, + ) + finally: + # Close this worker thread's thread-local database connection so it + # does not linger and block dropping the test database at teardown. + connection.close() with ThreadPoolExecutor(max_workers=2) as executor: future1 = executor.submit(create_document) diff --git a/src/backend/core/tests/documents/test_api_documents_create.py b/src/backend/core/tests/documents/test_api_documents_create.py index c2896e2aa..46d34ccf7 100644 --- a/src/backend/core/tests/documents/test_api_documents_create.py +++ b/src/backend/core/tests/documents/test_api_documents_create.py @@ -6,6 +6,8 @@ from concurrent.futures import ThreadPoolExecutor from unittest import mock from uuid import uuid4 +from django.db import connection + import pytest from rest_framework.test import APIClient @@ -70,16 +72,21 @@ def test_api_documents_create_document_race_condition(): """ def create_document(title): - user = factories.UserFactory() - client = APIClient() - client.force_login(user) - return client.post( - "/api/v1.0/documents/", - { - "title": title, - }, - format="json", - ) + try: + user = factories.UserFactory() + client = APIClient() + client.force_login(user) + return client.post( + "/api/v1.0/documents/", + { + "title": title, + }, + format="json", + ) + finally: + # Close this worker thread's thread-local database connection so it + # does not linger and block dropping the test database at teardown. + connection.close() with ThreadPoolExecutor(max_workers=2) as executor: future1 = executor.submit(create_document, "my document 1") diff --git a/src/backend/core/tests/documents/test_api_documents_create_for_owner.py b/src/backend/core/tests/documents/test_api_documents_create_for_owner.py index cf02674c2..7c15e7f1a 100644 --- a/src/backend/core/tests/documents/test_api_documents_create_for_owner.py +++ b/src/backend/core/tests/documents/test_api_documents_create_for_owner.py @@ -9,6 +9,7 @@ from unittest import mock from unittest.mock import patch from django.core import mail +from django.db import connection from django.test import override_settings import pytest @@ -480,16 +481,21 @@ def test_api_documents_create_document_race_condition(): """ def create_document(title): - user = factories.UserFactory() - client = APIClient() - client.force_login(user) - return client.post( - "/api/v1.0/documents/", - { - "title": title, - }, - format="json", - ) + try: + user = factories.UserFactory() + client = APIClient() + client.force_login(user) + return client.post( + "/api/v1.0/documents/", + { + "title": title, + }, + format="json", + ) + finally: + # Close this worker thread's thread-local database connection so it + # does not linger and block dropping the test database at teardown. + connection.close() with ThreadPoolExecutor(max_workers=2) as executor: future1 = executor.submit(create_document, "my document 1") diff --git a/src/backend/core/tests/test_models_users.py b/src/backend/core/tests/test_models_users.py index f1b044db4..b8bc20ffc 100644 --- a/src/backend/core/tests/test_models_users.py +++ b/src/backend/core/tests/test_models_users.py @@ -7,6 +7,7 @@ from concurrent.futures import ThreadPoolExecutor from unittest.mock import patch from django.core.exceptions import ValidationError +from django.db import connection from django.test.utils import override_settings import pytest @@ -341,7 +342,14 @@ def test_models_users_duplicate_onboarding_sandbox_race_condition(): """ def create_user(): - return factories.UserFactory() + try: + return factories.UserFactory() + finally: + # Each worker thread gets its own thread-local database connection. + # Close it explicitly so it does not linger and block dropping the + # test database during teardown (OperationalError: "database is being + # accessed by other users"). + connection.close() template_document = factories.DocumentFactory(title="Getting started with Docs") with (