diff --git a/.github/workflows/opensearch.yml b/.github/workflows/opensearch.yml index b91a419ffa..d4957d22f5 100644 --- a/.github/workflows/opensearch.yml +++ b/.github/workflows/opensearch.yml @@ -55,8 +55,8 @@ jobs: if: matrix.python-version == '3.10' && runner.os == 'Linux' run: hatch run docs - - name: Run tests - run: hatch run test:cov-retry + - name: Run tests (in parallel, using 4 cores) + run: hatch run test:cov-retry -n 4 # GA runner has 4 cores - name: Run unit tests with lowest direct dependencies run: | diff --git a/integrations/opensearch/pyproject.toml b/integrations/opensearch/pyproject.toml index 466863c445..e9652d1e60 100644 --- a/integrations/opensearch/pyproject.toml +++ b/integrations/opensearch/pyproject.toml @@ -58,6 +58,7 @@ dependencies = [ "pytest-asyncio", "pytest-cov", "pytest-rerunfailures", + "pytest-xdist", "mypy", "pip", "boto3", @@ -164,4 +165,3 @@ exclude_lines = ["no cov", "if __name__ == .__main__.:", "if TYPE_CHECKING:"] minversion = "6.0" markers = ["unit: unit tests", "integration: integration tests"] asyncio_mode = "auto" - diff --git a/integrations/opensearch/tests/conftest.py b/integrations/opensearch/tests/conftest.py index 8d7e30bbc1..2ae8462faa 100644 --- a/integrations/opensearch/tests/conftest.py +++ b/integrations/opensearch/tests/conftest.py @@ -1,4 +1,5 @@ import asyncio +import uuid import pytest from haystack import Document @@ -6,15 +7,23 @@ from haystack_integrations.document_stores.opensearch.document_store import OpenSearchDocumentStore +def _get_unique_index_name() -> str: + """ + Generate a unique, valid OpenSearch index name for test isolation. + + Each test gets its own index to enable parallel test execution without conflicts. + """ + return f"test_{uuid.uuid4().hex}" + + @pytest.fixture -def document_store(request): +def document_store(): """ We use this document store for basic tests and for testing filters. `return_embedding` is set to True because in filters tests we compare embeddings. """ hosts = ["https://localhost:9200"] - # Use a different index for each test so we can run them in parallel - index = f"{request.node.name}" + index = _get_unique_index_name() store = OpenSearchDocumentStore( hosts=hosts, @@ -36,9 +45,9 @@ def document_store(request): @pytest.fixture -def document_store_2(request): +def document_store_2(): hosts = ["https://localhost:9200"] - index = f"test_index_2_{request.node.name}" + index = f"test_index_2_{_get_unique_index_name()}" store = OpenSearchDocumentStore( hosts=hosts, @@ -61,13 +70,13 @@ def document_store_2(request): @pytest.fixture -def document_store_readonly(request): +def document_store_readonly(): """ A document store that does not automatically create the underlying index. """ hosts = ["https://localhost:9200"] # Use a different index for each test so we can run them in parallel - index = f"{request.node.name}" + index = _get_unique_index_name() store = OpenSearchDocumentStore( hosts=hosts, @@ -90,13 +99,12 @@ def document_store_readonly(request): @pytest.fixture -def document_store_embedding_dim_4_no_emb_returned(request): +def document_store_embedding_dim_4_no_emb_returned(): """ A document store with embedding dimension 4 that does not return embeddings. """ hosts = ["https://localhost:9200"] - # Use a different index for each test so we can run them in parallel - index = f"{request.node.name}" + index = _get_unique_index_name() store = OpenSearchDocumentStore( hosts=hosts, @@ -113,15 +121,14 @@ def document_store_embedding_dim_4_no_emb_returned(request): @pytest.fixture -def document_store_embedding_dim_4_no_emb_returned_faiss(request): +def document_store_embedding_dim_4_no_emb_returned_faiss(): """ A document store with embedding dimension 4 that uses a FAISS engine with HNSW algorithm for vector search. We use this document store for testing efficient k-NN filtering according to https://opensearch.org/docs/latest/vector-search/filter-search-knn/efficient-knn-filtering/. """ hosts = ["https://localhost:9200"] - # Use a different index for each test so we can run them in parallel - index = f"{request.node.name}" + index = _get_unique_index_name() store = OpenSearchDocumentStore( hosts=hosts, diff --git a/integrations/opensearch/tests/test_document_store.py b/integrations/opensearch/tests/test_document_store.py index 2f5976bb98..c75a5ca0a9 100644 --- a/integrations/opensearch/tests/test_document_store.py +++ b/integrations/opensearch/tests/test_document_store.py @@ -443,28 +443,6 @@ def test_write_documents_max_chunk_bytes(self, mock_bulk, document_store): assert mock_bulk.call_args.kwargs["max_chunk_bytes"] == DEFAULT_MAX_CHUNK_BYTES - @pytest.fixture - def document_store_embedding_dim_4_no_emb_returned(self, request): - """ - This is the most basic requirement for the child class: provide - an instance of this document store so the base class can use it. - """ - hosts = ["https://localhost:9200"] - # Use a different index for each test so we can run them in parallel - index = f"{request.node.name}" - - store = OpenSearchDocumentStore( - hosts=hosts, - index=index, - http_auth=("admin", "admin"), - verify_certs=False, - embedding_dim=4, - return_embedding=False, - method={"space_type": "cosinesimil", "engine": "nmslib", "name": "hnsw"}, - ) - yield store - store._client.indices.delete(index=index, params={"ignore": [400, 404]}) - def test_embedding_retrieval_but_dont_return_embeddings_for_embedding_retrieval( self, document_store_embedding_dim_4_no_emb_returned: OpenSearchDocumentStore ):