From 14c571455572d8ca5f211295d2a16d4fa1ba901b Mon Sep 17 00:00:00 2001 From: Stefano Lottini Date: Tue, 17 Dec 2024 18:06:57 +0100 Subject: [PATCH 1/6] WIP restructure test and CI --- .../integration => admin}/__init__.py | 0 tests/admin/conftest.py | 23 + .../unit => admin/integration}/__init__.py | 0 .../integration/test_admin.py | 0 .../integration/test_nonastra_admin.py | 0 tests/{tables => base}/__init__.py | 0 .../collection_decimal_support_assets.py} | 0 tests/base/conftest.py | 487 ++++++++++++++++++ .../integration}/__init__.py | 0 .../integration/collections}/__init__.py | 0 .../test_collection_cursor_async.py | 0 .../test_collection_cursor_sync.py | 0 .../collections/test_collection_ddl_async.py} | 2 +- .../collections/test_collection_ddl_sync.py} | 2 +- .../test_collection_decimal_support.py | 0 .../collections/test_collection_dml_async.py} | 2 +- .../collections/test_collection_dml_sync.py} | 2 +- .../test_collection_exceptions_async.py} | 2 +- .../test_collection_exceptions_sync.py} | 2 +- .../test_collection_timeout_async.py} | 2 +- .../test_collection_timeout_sync.py} | 2 +- .../collections}/test_collection_typing.py | 0 ...est_collection_vectorize_methods_async.py} | 5 +- ...test_collection_vectorize_methods_sync.py} | 5 +- tests/base/integration/conftest.py | 33 ++ .../integration/misc}/__init__.py | 0 .../misc}/test_vectorize_ops_async.py | 0 .../misc}/test_vectorize_ops_sync.py | 0 .../integration/tables}/__init__.py | 0 .../integration/tables}/table_row_assets.py | 0 .../tables}/test_table_column_types_async.py | 0 .../tables}/test_table_column_types_sync.py | 0 .../tables}/test_table_cursor_async.py | 0 .../tables}/test_table_cursor_sync.py | 0 .../tables}/test_table_dml_async.py | 0 .../tables}/test_table_dml_sync.py | 0 .../tables}/test_table_lifecycle_async.py | 0 .../tables}/test_table_lifecycle_sync.py | 0 .../integration/tables}/test_table_typing.py | 0 .../tables}/test_table_vectorize_async.py | 0 .../tables}/test_table_vectorize_sync.py | 0 .../table_decimal_support_assets.py} | 0 .../table_structure_assets.py} | 323 +----------- .../unit/__init__.py | 0 .../unit/test_admin_conversions.py | 0 .../unit/test_apicommander.py | 0 .../unit/test_apioptions.py | 0 .../unit/test_collection_decimal_support.py | 2 +- .../unit/test_collection_options.py | 0 .../unit/test_collection_timeouts.py | 0 .../unit/test_collections_async.py | 0 .../unit/test_collections_sync.py | 0 .../test_collectionvectorserviceoptions.py | 0 .../{tables => base}/unit/test_dataapidate.py | 0 .../unit/test_dataapiduration.py | 0 .../{tables => base}/unit/test_dataapimap.py | 0 .../{tables => base}/unit/test_dataapiset.py | 0 .../{tables => base}/unit/test_dataapitime.py | 0 .../unit/test_dataapitimestamp.py | 0 .../unit/test_dataapivector.py | 0 .../unit/test_databases_async.py | 0 .../unit/test_databases_sync.py | 0 .../unit/test_datetime_serdes_options.py | 0 .../unit/test_document_extractors.py | 0 .../unit/test_embeddingheadersprovider.py | 0 .../unit/test_exceptions.py | 0 tests/{idiomatic => base}/unit/test_ids.py | 0 .../{idiomatic => base}/unit/test_imports.py | 0 tests/{idiomatic => base}/unit/test_info.py | 0 .../unit/test_multicalltimeoutmanager.py | 0 .../unit/test_table_decimal_support.py | 2 +- .../unit/test_table_dry_methods.py | 0 .../unit/test_tableconverteragent.py | 0 .../unit/test_tabledescriptors.py | 0 .../{idiomatic => base}/unit/test_timeouts.py | 0 .../unit/test_token_providers.py | 0 .../unit/test_tpostprocessors.py | 0 tests/conftest.py | 39 ++ .../docker-compose.yml | 2 +- tests/idiomatic/conftest.py | 158 ------ .../unit/test_datetime_serdes_options.py | 149 ------ tests/preprocess_env.py | 3 + tests/{tables/unit => vectorize}/__init__.py | 2 + tests/vectorize/conftest.py | 20 + tests/vectorize/integration/__init__.py | 15 + .../integration/test_vectorize_providers.py | 0 .../live_provider_info.py | 0 .../query_providers.py | 0 .../vectorize_models.py | 0 tests/vectorize_idiomatic/conftest.py | 125 ----- 90 files changed, 640 insertions(+), 769 deletions(-) rename tests/{idiomatic/integration => admin}/__init__.py (100%) create mode 100644 tests/admin/conftest.py rename tests/{idiomatic/unit => admin/integration}/__init__.py (100%) rename tests/{idiomatic => admin}/integration/test_admin.py (100%) rename tests/{idiomatic => admin}/integration/test_nonastra_admin.py (100%) rename tests/{tables => base}/__init__.py (100%) rename tests/{idiomatic/decimal_support_assets.py => base/collection_decimal_support_assets.py} (100%) create mode 100644 tests/base/conftest.py rename tests/{vectorize_idiomatic => base/integration}/__init__.py (100%) rename tests/{vectorize_idiomatic/integration => base/integration/collections}/__init__.py (100%) rename tests/{idiomatic/integration => base/integration/collections}/test_collection_cursor_async.py (100%) rename tests/{idiomatic/integration => base/integration/collections}/test_collection_cursor_sync.py (100%) rename tests/{idiomatic/integration/test_ddl_async.py => base/integration/collections/test_collection_ddl_async.py} (99%) rename tests/{idiomatic/integration/test_ddl_sync.py => base/integration/collections/test_collection_ddl_sync.py} (99%) rename tests/{idiomatic/integration => base/integration/collections}/test_collection_decimal_support.py (100%) rename tests/{idiomatic/integration/test_dml_async.py => base/integration/collections/test_collection_dml_async.py} (99%) rename tests/{idiomatic/integration/test_dml_sync.py => base/integration/collections/test_collection_dml_sync.py} (99%) rename tests/{idiomatic/integration/test_exceptions_async.py => base/integration/collections/test_collection_exceptions_async.py} (99%) rename tests/{idiomatic/integration/test_exceptions_sync.py => base/integration/collections/test_collection_exceptions_sync.py} (99%) rename tests/{idiomatic/integration/test_timeout_async.py => base/integration/collections/test_collection_timeout_async.py} (99%) rename tests/{idiomatic/integration/test_timeout_sync.py => base/integration/collections/test_collection_timeout_sync.py} (99%) rename tests/{idiomatic/integration => base/integration/collections}/test_collection_typing.py (100%) rename tests/{vectorize_idiomatic/integration/test_vectorize_methods_async.py => base/integration/collections/test_collection_vectorize_methods_async.py} (97%) rename tests/{vectorize_idiomatic/integration/test_vectorize_methods_sync.py => base/integration/collections/test_collection_vectorize_methods_sync.py} (97%) create mode 100644 tests/base/integration/conftest.py rename tests/{idiomatic => base/integration/misc}/__init__.py (100%) rename tests/{vectorize_idiomatic/integration => base/integration/misc}/test_vectorize_ops_async.py (100%) rename tests/{vectorize_idiomatic/integration => base/integration/misc}/test_vectorize_ops_sync.py (100%) rename tests/{tables/integration => base/integration/tables}/__init__.py (100%) rename tests/{tables/integration => base/integration/tables}/table_row_assets.py (100%) rename tests/{tables/integration => base/integration/tables}/test_table_column_types_async.py (100%) rename tests/{tables/integration => base/integration/tables}/test_table_column_types_sync.py (100%) rename tests/{tables/integration => base/integration/tables}/test_table_cursor_async.py (100%) rename tests/{tables/integration => base/integration/tables}/test_table_cursor_sync.py (100%) rename tests/{tables/integration => base/integration/tables}/test_table_dml_async.py (100%) rename tests/{tables/integration => base/integration/tables}/test_table_dml_sync.py (100%) rename tests/{tables/integration => base/integration/tables}/test_table_lifecycle_async.py (100%) rename tests/{tables/integration => base/integration/tables}/test_table_lifecycle_sync.py (100%) rename tests/{tables/integration => base/integration/tables}/test_table_typing.py (100%) rename tests/{tables/integration => base/integration/tables}/test_table_vectorize_async.py (100%) rename tests/{tables/integration => base/integration/tables}/test_table_vectorize_sync.py (100%) rename tests/{tables/decimal_support_assets.py => base/table_decimal_support_assets.py} (100%) rename tests/{tables/conftest.py => base/table_structure_assets.py} (51%) rename tests/{vectorize_idiomatic => base}/unit/__init__.py (100%) rename tests/{idiomatic => base}/unit/test_admin_conversions.py (100%) rename tests/{idiomatic => base}/unit/test_apicommander.py (100%) rename tests/{idiomatic => base}/unit/test_apioptions.py (100%) rename tests/{idiomatic => base}/unit/test_collection_decimal_support.py (98%) rename tests/{idiomatic => base}/unit/test_collection_options.py (100%) rename tests/{idiomatic => base}/unit/test_collection_timeouts.py (100%) rename tests/{idiomatic => base}/unit/test_collections_async.py (100%) rename tests/{idiomatic => base}/unit/test_collections_sync.py (100%) rename tests/{vectorize_idiomatic => base}/unit/test_collectionvectorserviceoptions.py (100%) rename tests/{tables => base}/unit/test_dataapidate.py (100%) rename tests/{tables => base}/unit/test_dataapiduration.py (100%) rename tests/{tables => base}/unit/test_dataapimap.py (100%) rename tests/{tables => base}/unit/test_dataapiset.py (100%) rename tests/{tables => base}/unit/test_dataapitime.py (100%) rename tests/{idiomatic => base}/unit/test_dataapitimestamp.py (100%) rename tests/{idiomatic => base}/unit/test_dataapivector.py (100%) rename tests/{idiomatic => base}/unit/test_databases_async.py (100%) rename tests/{idiomatic => base}/unit/test_databases_sync.py (100%) rename tests/{tables => base}/unit/test_datetime_serdes_options.py (100%) rename tests/{idiomatic => base}/unit/test_document_extractors.py (100%) rename tests/{vectorize_idiomatic => base}/unit/test_embeddingheadersprovider.py (100%) rename tests/{idiomatic => base}/unit/test_exceptions.py (100%) rename tests/{idiomatic => base}/unit/test_ids.py (100%) rename tests/{idiomatic => base}/unit/test_imports.py (100%) rename tests/{idiomatic => base}/unit/test_info.py (100%) rename tests/{idiomatic => base}/unit/test_multicalltimeoutmanager.py (100%) rename tests/{tables => base}/unit/test_table_decimal_support.py (99%) rename tests/{tables => base}/unit/test_table_dry_methods.py (100%) rename tests/{tables => base}/unit/test_tableconverteragent.py (100%) rename tests/{tables => base}/unit/test_tabledescriptors.py (100%) rename tests/{idiomatic => base}/unit/test_timeouts.py (100%) rename tests/{idiomatic => base}/unit/test_token_providers.py (100%) rename tests/{tables => base}/unit/test_tpostprocessors.py (100%) rename tests/{dse_compose => dse_compose_manual}/docker-compose.yml (97%) delete mode 100644 tests/idiomatic/conftest.py delete mode 100644 tests/idiomatic/unit/test_datetime_serdes_options.py rename tests/{tables/unit => vectorize}/__init__.py (94%) create mode 100644 tests/vectorize/conftest.py create mode 100644 tests/vectorize/integration/__init__.py rename tests/{vectorize_idiomatic => vectorize}/integration/test_vectorize_providers.py (100%) rename tests/{vectorize_idiomatic => vectorize}/live_provider_info.py (100%) rename tests/{vectorize_idiomatic => vectorize}/query_providers.py (100%) rename tests/{vectorize_idiomatic => vectorize}/vectorize_models.py (100%) delete mode 100644 tests/vectorize_idiomatic/conftest.py diff --git a/tests/idiomatic/integration/__init__.py b/tests/admin/__init__.py similarity index 100% rename from tests/idiomatic/integration/__init__.py rename to tests/admin/__init__.py diff --git a/tests/admin/conftest.py b/tests/admin/conftest.py new file mode 100644 index 00000000..d497841e --- /dev/null +++ b/tests/admin/conftest.py @@ -0,0 +1,23 @@ +# Copyright DataStax, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + + +from ..conftest import ( + ADMIN_ENV_LIST, + ADMIN_ENV_VARIABLE_MAP, + DO_IDIOMATIC_ADMIN_TESTS, + IS_ASTRA_DB, +) diff --git a/tests/idiomatic/unit/__init__.py b/tests/admin/integration/__init__.py similarity index 100% rename from tests/idiomatic/unit/__init__.py rename to tests/admin/integration/__init__.py diff --git a/tests/idiomatic/integration/test_admin.py b/tests/admin/integration/test_admin.py similarity index 100% rename from tests/idiomatic/integration/test_admin.py rename to tests/admin/integration/test_admin.py diff --git a/tests/idiomatic/integration/test_nonastra_admin.py b/tests/admin/integration/test_nonastra_admin.py similarity index 100% rename from tests/idiomatic/integration/test_nonastra_admin.py rename to tests/admin/integration/test_nonastra_admin.py diff --git a/tests/tables/__init__.py b/tests/base/__init__.py similarity index 100% rename from tests/tables/__init__.py rename to tests/base/__init__.py diff --git a/tests/idiomatic/decimal_support_assets.py b/tests/base/collection_decimal_support_assets.py similarity index 100% rename from tests/idiomatic/decimal_support_assets.py rename to tests/base/collection_decimal_support_assets.py diff --git a/tests/base/conftest.py b/tests/base/conftest.py new file mode 100644 index 00000000..d771a946 --- /dev/null +++ b/tests/base/conftest.py @@ -0,0 +1,487 @@ +# Copyright DataStax, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import math +from decimal import Decimal +from typing import Any, Dict, Iterable + +import pytest + +from astrapy import AsyncCollection, AsyncDatabase, AsyncTable, Collection, DataAPIClient, Database, Table +from astrapy.api_options import APIOptions, SerdesOptions +from astrapy.constants import SortMode, VectorMetric +from astrapy.data_types import DataAPIMap, DataAPISet +from astrapy.info import ( + CollectionDefinition, + CollectionVectorOptions, + CreateTableDefinition, + TableKeyValuedColumnTypeDescriptor, + TablePrimaryKeyDescriptor, + TableScalarColumnTypeDescriptor, + TableValuedColumnTypeDescriptor, + TableVectorColumnTypeDescriptor, + TableVectorIndexOptions, + VectorServiceOptions, +) + +from .table_structure_assets import ( + TEST_ALL_RETURNS_TABLE_NAME, + TEST_ALL_RETURNS_TABLE_DEFINITION, + TEST_SIMPLE_TABLE_NAME, + TEST_SIMPLE_TABLE_DEFINITION, + TEST_SIMPLE_TABLE_VECTOR_INDEX_NAME, + TEST_SIMPLE_TABLE_VECTOR_INDEX_COLUMN, + TEST_SIMPLE_TABLE_VECTOR_INDEX_OPTIONS, + TEST_COMPOSITE_TABLE_NAME, + TEST_COMPOSITE_TABLE_DEFINITION, + TEST_COMPOSITE_TABLE_VECTOR_INDEX_NAME, + TEST_COMPOSITE_TABLE_VECTOR_INDEX_COLUMN, + TEST_COMPOSITE_TABLE_VECTOR_INDEX_OPTIONS, + TEST_COMPOSITE_TABLE_BOOLEAN_INDEX_NAME, + TEST_COMPOSITE_TABLE_BOOLEAN_INDEX_COLUMN, + TEST_COMPOSITE_TABLE_BOOLEAN_INDEX_OPTIONS, + TEST_VECTORIZE_TABLE_NAME, + TEST_VECTORIZE_TABLE_DEFINITION, + TEST_VECTORIZE_TABLE_VECTOR_INDEX_NAME, + TEST_VECTORIZE_TABLE_VECTOR_INDEX_COLUMN, + TEST_VECTORIZE_TABLE_VECTOR_INDEX_OPTIONS, + TEST_KMS_VECTORIZE_TABLE_NAME, + TEST_KMS_VECTORIZE_TABLE_DEFINITION, + TEST_KMS_VECTORIZE_TABLE_VECTOR_INDEX_NAME, + TEST_KMS_VECTORIZE_TABLE_VECTOR_INDEX_COLUMN, + TEST_KMS_VECTORIZE_TABLE_VECTOR_INDEX_DEFINITION, + VECTORIZE_TEXTS, +) + +from ..conftest import ( + ADMIN_ENV_LIST, + ADMIN_ENV_VARIABLE_MAP, + DO_IDIOMATIC_ADMIN_TESTS, + HEADER_EMBEDDING_API_KEY_OPENAI, + IS_ASTRA_DB, + SECONDARY_KEYSPACE, + DataAPICredentials, + DataAPICredentialsInfo, + async_fail_if_not_removed, + clean_nulls_from_dict, + sync_fail_if_not_removed, +) + +DefaultCollection = Collection[Dict[str, Any]] +DefaultAsyncCollection = AsyncCollection[Dict[str, Any]] +DefaultTable = Table[Dict[str, Any]] +DefaultAsyncTable = AsyncTable[Dict[str, Any]] + +TEST_COLLECTION_INSTANCE_NAME = "test_coll_instance" +TEST_COLLECTION_NAME = "id_test_collection" +TEST_SERVICE_COLLECTION_NAME = "test_indepth_vectorize_collection" + + +_NaN = object() +_DNaN = object() + + +def _repaint_NaNs(val: Any) -> Any: + if isinstance(val, float) and math.isnan(val): + return _NaN + if isinstance(val, Decimal) and math.isnan(val): + return _DNaN + elif isinstance(val, dict): + return {_repaint_NaNs(k): _repaint_NaNs(v) for k, v in val.items()} + elif isinstance(val, list): + return [_repaint_NaNs(x) for x in val] + elif isinstance(val, DataAPISet): + return DataAPISet(_repaint_NaNs(v) for v in val) + elif isinstance(val, DataAPIMap): + return DataAPIMap((_repaint_NaNs(k), _repaint_NaNs(v)) for k, v in val.items()) + elif isinstance(val, set): + return {_repaint_NaNs(v) for v in val} + elif isinstance(val, dict): + return {_repaint_NaNs(k): _repaint_NaNs(v) for k, v in val.items()} + else: + return val + + +def _typify_tuple(tpl: tuple[Any, ...]) -> tuple[Any, ...]: + return tuple([(v, type(v)) for v in tpl]) + + +@pytest.fixture(scope="session") +def sync_collection_instance( + data_api_credentials_kwargs: DataAPICredentials, + sync_database: Database, +) -> Iterable[DefaultCollection]: + """Just an instance of the class, no DB-level stuff.""" + yield sync_database.get_collection(TEST_COLLECTION_INSTANCE_NAME) + + +@pytest.fixture(scope="function") +def async_collection_instance( + sync_collection_instance: DefaultCollection, +) -> Iterable[DefaultAsyncCollection]: + """Just an instance of the class, no DB-level stuff.""" + yield sync_collection_instance.to_async() + + +@pytest.fixture(scope="session") +def sync_collection( + data_api_credentials_kwargs: DataAPICredentials, + sync_database: Database, +) -> Iterable[DefaultCollection]: + """An actual collection on DB, in the main keyspace""" + collection = sync_database.create_collection( + TEST_COLLECTION_NAME, + definition=CollectionDefinition( + indexing={"deny": ["not_indexed"]}, + vector=CollectionVectorOptions( + dimension=2, + metric=VectorMetric.COSINE, + ), + ), + spawn_api_options=APIOptions( + serdes_options=SerdesOptions( + binary_encode_vectors=True, + custom_datatypes_in_reading=True, + unroll_iterables_to_lists=True, + ), + ), + ) + yield collection + + sync_database.drop_collection(TEST_COLLECTION_NAME) + + +@pytest.fixture(scope="function") +def sync_empty_collection( + sync_collection: DefaultCollection, +) -> Iterable[DefaultCollection]: + """Emptied for each test function""" + sync_collection.delete_many({}) + yield sync_collection + + +@pytest.fixture(scope="function") +def async_collection( + sync_collection: DefaultCollection, +) -> Iterable[DefaultAsyncCollection]: + """An actual collection on DB, the same as the sync counterpart""" + yield sync_collection.to_async() + + +@pytest.fixture(scope="function") +def async_empty_collection( + sync_empty_collection: DefaultCollection, +) -> Iterable[DefaultAsyncCollection]: + """Emptied for each test function""" + yield sync_empty_collection.to_async() + + +@pytest.fixture(scope="session") +def service_collection_parameters() -> Iterable[dict[str, Any]]: + yield { + "dimension": 1536, + "provider": "openai", + "modelName": "text-embedding-ada-002", + "api_key": HEADER_EMBEDDING_API_KEY_OPENAI, + } + + +@pytest.fixture(scope="session") +def sync_service_collection( + data_api_credentials_kwargs: DataAPICredentials, + sync_database: Database, + service_collection_parameters: dict[str, Any], +) -> Iterable[DefaultCollection]: + """ + An actual collection on DB, in the main keyspace. + """ + params = service_collection_parameters + collection = sync_database.create_collection( + TEST_SERVICE_COLLECTION_NAME, + definition=( + CollectionDefinition.builder() + .set_vector_metric(VectorMetric.DOT_PRODUCT) + .set_vector_service( + provider=params["provider"], + model_name=params["modelName"], + ) + .build() + ), + embedding_api_key=params["api_key"], + ) + yield collection + + sync_database.drop_collection(TEST_SERVICE_COLLECTION_NAME) + + +@pytest.fixture(scope="function") +def sync_empty_service_collection( + sync_service_collection: DefaultCollection, +) -> Iterable[DefaultCollection]: + """Emptied for each test function""" + sync_service_collection.delete_many({}) + yield sync_service_collection + + +@pytest.fixture(scope="function") +def async_empty_service_collection( + sync_empty_service_collection: DefaultCollection, +) -> Iterable[DefaultAsyncCollection]: + """Emptied for each test function""" + yield sync_empty_service_collection.to_async() + + +@pytest.fixture(scope="session") +def sync_table_all_returns( + data_api_credentials_kwargs: DataAPICredentials, + sync_database: Database, +) -> Iterable[DefaultTable]: + """An actual table on DB, in the main keyspace""" + table = sync_database.create_table( + TEST_ALL_RETURNS_TABLE_NAME, + definition=TEST_ALL_RETURNS_TABLE_DEFINITION, + ) + yield table + + sync_database.drop_table(TEST_ALL_RETURNS_TABLE_NAME) + + +@pytest.fixture(scope="function") +def sync_empty_table_all_returns( + sync_table_all_returns: DefaultTable, +) -> Iterable[DefaultTable]: + """Emptied for each test function""" + sync_table_all_returns.delete_many({}) + yield sync_table_all_returns + + +@pytest.fixture(scope="function") +def async_table_all_returns( + sync_table_all_returns: DefaultTable, +) -> Iterable[DefaultAsyncTable]: + """An actual table on DB, the same as the sync counterpart""" + yield sync_table_all_returns.to_async() + + +@pytest.fixture(scope="function") +def async_empty_table_all_returns( + sync_empty_table_all_returns: DefaultTable, +) -> Iterable[DefaultAsyncTable]: + """Emptied for each test function""" + yield sync_empty_table_all_returns.to_async() + + +@pytest.fixture(scope="session") +def sync_table_simple( + data_api_credentials_kwargs: DataAPICredentials, + sync_database: Database, +) -> Iterable[DefaultTable]: + """An actual table on DB, in the main keyspace""" + table = sync_database.create_table( + TEST_SIMPLE_TABLE_NAME, + definition=TEST_SIMPLE_TABLE_DEFINITION, + ) + table.create_vector_index( + TEST_SIMPLE_TABLE_VECTOR_INDEX_NAME, + column=TEST_SIMPLE_TABLE_VECTOR_INDEX_COLUMN, + options=TEST_SIMPLE_TABLE_VECTOR_INDEX_OPTIONS, + ) + yield table + + sync_database.drop_table(TEST_SIMPLE_TABLE_NAME) + + +@pytest.fixture(scope="function") +def sync_empty_table_simple( + sync_table_simple: DefaultTable, +) -> Iterable[DefaultTable]: + """Emptied for each test function""" + sync_table_simple.delete_many({}) + yield sync_table_simple + + +@pytest.fixture(scope="function") +def async_table_simple( + sync_table_simple: DefaultTable, +) -> Iterable[DefaultAsyncTable]: + """An actual table on DB, the same as the sync counterpart""" + yield sync_table_simple.to_async() + + +@pytest.fixture(scope="function") +def async_empty_table_simple( + sync_empty_table_simple: DefaultTable, +) -> Iterable[DefaultAsyncTable]: + """Emptied for each test function""" + yield sync_empty_table_simple.to_async() + + +@pytest.fixture(scope="session") +def sync_table_composite( + data_api_credentials_kwargs: DataAPICredentials, + sync_database: Database, +) -> Iterable[DefaultTable]: + """An actual table on DB, in the main keyspace""" + table = sync_database.create_table( + TEST_COMPOSITE_TABLE_NAME, + definition=TEST_COMPOSITE_TABLE_DEFINITION, + ) + table.create_vector_index( + TEST_COMPOSITE_TABLE_VECTOR_INDEX_NAME, + column=TEST_COMPOSITE_TABLE_VECTOR_INDEX_COLUMN, + options=TEST_COMPOSITE_TABLE_VECTOR_INDEX_OPTIONS, + ) + table.create_index( + TEST_COMPOSITE_TABLE_BOOLEAN_INDEX_NAME, + column=TEST_COMPOSITE_TABLE_BOOLEAN_INDEX_COLUMN, + options=TEST_COMPOSITE_TABLE_BOOLEAN_INDEX_OPTIONS, + ) + yield table + + sync_database.drop_table(TEST_COMPOSITE_TABLE_NAME) + + +@pytest.fixture(scope="function") +def sync_empty_table_composite( + sync_table_composite: DefaultTable, +) -> Iterable[DefaultTable]: + """Emptied for each test function""" + sync_table_composite.delete_many({}) + yield sync_table_composite + + +@pytest.fixture(scope="function") +def async_table_composite( + sync_table_composite: DefaultTable, +) -> Iterable[DefaultAsyncTable]: + """An actual table on DB, the same as the sync counterpart""" + yield sync_table_composite.to_async() + + +@pytest.fixture(scope="function") +def async_empty_table_composite( + sync_empty_table_composite: DefaultTable, +) -> Iterable[DefaultAsyncTable]: + """Emptied for each test function""" + yield sync_empty_table_composite.to_async() + + +@pytest.fixture(scope="session") +def sync_table_vectorize( + data_api_credentials_kwargs: DataAPICredentials, + sync_database: Database, +) -> Iterable[DefaultTable]: + """An actual table on DB, in the main keyspace""" + table = sync_database.create_table( + TEST_VECTORIZE_TABLE_NAME, + definition=TEST_VECTORIZE_TABLE_DEFINITION, + ) + table.create_vector_index( + TEST_VECTORIZE_TABLE_VECTOR_INDEX_NAME, + column=TEST_SIMPLE_TABLE_VECTOR_INDEX_COLUMN, + options=TEST_SIMPLE_TABLE_VECTOR_INDEX_OPTIONS, + ) + yield table + + sync_database.drop_table(TEST_VECTORIZE_TABLE_NAME) + + +@pytest.fixture(scope="function") +def sync_empty_table_vectorize( + sync_table_vectorize: DefaultTable, +) -> Iterable[DefaultTable]: + """Emptied for each test function""" + sync_table_vectorize.delete_many({}) + yield sync_table_vectorize + + +@pytest.fixture(scope="function") +def async_table_vectorize( + sync_table_vectorize: DefaultTable, +) -> Iterable[DefaultAsyncTable]: + """An actual table on DB, the same as the sync counterpart""" + yield sync_table_vectorize.to_async() + + +@pytest.fixture(scope="function") +def async_empty_table_vectorize( + sync_empty_table_vectorize: DefaultTable, +) -> Iterable[DefaultAsyncTable]: + """Emptied for each test function""" + yield sync_empty_table_vectorize.to_async() + + +@pytest.fixture(scope="session") +def sync_table_kms_vectorize( + data_api_credentials_kwargs: DataAPICredentials, + sync_database: Database, +) -> Iterable[DefaultTable]: + """An actual table on DB, in the main keyspace""" + table = sync_database.create_table( + TEST_KMS_VECTORIZE_TABLE_NAME, + definition=TEST_KMS_VECTORIZE_TABLE_DEFINITION, + ) + table.create_vector_index( + TEST_KMS_VECTORIZE_TABLE_VECTOR_INDEX_NAME, + column=TEST_SIMPLE_TABLE_VECTOR_INDEX_COLUMN, + options=TEST_SIMPLE_TABLE_VECTOR_INDEX_OPTIONS, + ) + yield table + + sync_database.drop_table(TEST_KMS_VECTORIZE_TABLE_NAME) + + +@pytest.fixture(scope="function") +def sync_empty_table_kms_vectorize( + sync_table_kms_vectorize: DefaultTable, +) -> Iterable[DefaultTable]: + """Emptied for each test function""" + sync_table_kms_vectorize.delete_many({}) + yield sync_table_kms_vectorize + + +@pytest.fixture(scope="function") +def async_table_kms_vectorize( + sync_table_kms_vectorize: DefaultTable, +) -> Iterable[DefaultAsyncTable]: + """An actual table on DB, the same as the sync counterpart""" + yield sync_table_kms_vectorize.to_async() + + +@pytest.fixture(scope="function") +def async_empty_table_kms_vectorize( + sync_empty_table_kms_vectorize: DefaultTable, +) -> Iterable[DefaultAsyncTable]: + """Emptied for each test function""" + yield sync_empty_table_kms_vectorize.to_async() + + +__all__ = [ + "DataAPICredentials", + "DataAPICredentialsInfo", + # "async_database", + "async_fail_if_not_removed", + "clean_nulls_from_dict", + "sync_fail_if_not_removed", + # "sync_database", + "IS_ASTRA_DB", + "ADMIN_ENV_LIST", + "ADMIN_ENV_VARIABLE_MAP", + "DO_IDIOMATIC_ADMIN_TESTS", + "SECONDARY_KEYSPACE", + "_repaint_NaNs", + "_typify_tuple", +] diff --git a/tests/vectorize_idiomatic/__init__.py b/tests/base/integration/__init__.py similarity index 100% rename from tests/vectorize_idiomatic/__init__.py rename to tests/base/integration/__init__.py diff --git a/tests/vectorize_idiomatic/integration/__init__.py b/tests/base/integration/collections/__init__.py similarity index 100% rename from tests/vectorize_idiomatic/integration/__init__.py rename to tests/base/integration/collections/__init__.py diff --git a/tests/idiomatic/integration/test_collection_cursor_async.py b/tests/base/integration/collections/test_collection_cursor_async.py similarity index 100% rename from tests/idiomatic/integration/test_collection_cursor_async.py rename to tests/base/integration/collections/test_collection_cursor_async.py diff --git a/tests/idiomatic/integration/test_collection_cursor_sync.py b/tests/base/integration/collections/test_collection_cursor_sync.py similarity index 100% rename from tests/idiomatic/integration/test_collection_cursor_sync.py rename to tests/base/integration/collections/test_collection_cursor_sync.py diff --git a/tests/idiomatic/integration/test_ddl_async.py b/tests/base/integration/collections/test_collection_ddl_async.py similarity index 99% rename from tests/idiomatic/integration/test_ddl_async.py rename to tests/base/integration/collections/test_collection_ddl_async.py index 34f4728c..bf2f3766 100644 --- a/tests/idiomatic/integration/test_ddl_async.py +++ b/tests/base/integration/collections/test_collection_ddl_async.py @@ -39,7 +39,7 @@ ) -class TestDDLAsync: +class TestCollectionDDLAsync: @pytest.mark.describe("test of collection creation, get, and then drop, async") async def test_collection_lifecycle_async( self, diff --git a/tests/idiomatic/integration/test_ddl_sync.py b/tests/base/integration/collections/test_collection_ddl_sync.py similarity index 99% rename from tests/idiomatic/integration/test_ddl_sync.py rename to tests/base/integration/collections/test_collection_ddl_sync.py index 1f2699ff..88cd3540 100644 --- a/tests/idiomatic/integration/test_ddl_sync.py +++ b/tests/base/integration/collections/test_collection_ddl_sync.py @@ -40,7 +40,7 @@ ) -class TestDDLSync: +class TestCollectionDDLSync: @pytest.mark.describe("test of collection creation, get, and then drop, sync") def test_collection_lifecycle_sync( self, diff --git a/tests/idiomatic/integration/test_collection_decimal_support.py b/tests/base/integration/collections/test_collection_decimal_support.py similarity index 100% rename from tests/idiomatic/integration/test_collection_decimal_support.py rename to tests/base/integration/collections/test_collection_decimal_support.py diff --git a/tests/idiomatic/integration/test_dml_async.py b/tests/base/integration/collections/test_collection_dml_async.py similarity index 99% rename from tests/idiomatic/integration/test_dml_async.py rename to tests/base/integration/collections/test_collection_dml_async.py index a46bac15..540c0765 100644 --- a/tests/idiomatic/integration/test_dml_async.py +++ b/tests/base/integration/collections/test_collection_dml_async.py @@ -36,7 +36,7 @@ async def _alist( return [doc async for doc in acursor] -class TestDMLAsync: +class TestCollectionDMLAsync: @pytest.mark.describe("test of collection count_documents, async") async def test_collection_count_documents_async( self, diff --git a/tests/idiomatic/integration/test_dml_sync.py b/tests/base/integration/collections/test_collection_dml_sync.py similarity index 99% rename from tests/idiomatic/integration/test_dml_sync.py rename to tests/base/integration/collections/test_collection_dml_sync.py index 33af349d..2935f4d4 100644 --- a/tests/idiomatic/integration/test_dml_sync.py +++ b/tests/base/integration/collections/test_collection_dml_sync.py @@ -29,7 +29,7 @@ from ..conftest import DefaultCollection -class TestDMLSync: +class TestCollectionDMLSync: @pytest.mark.describe("test of collection count_documents, sync") def test_collection_count_documents_sync( self, diff --git a/tests/idiomatic/integration/test_exceptions_async.py b/tests/base/integration/collections/test_collection_exceptions_async.py similarity index 99% rename from tests/idiomatic/integration/test_exceptions_async.py rename to tests/base/integration/collections/test_collection_exceptions_async.py index d7778354..f3186565 100644 --- a/tests/idiomatic/integration/test_exceptions_async.py +++ b/tests/base/integration/collections/test_collection_exceptions_async.py @@ -29,7 +29,7 @@ from ..conftest import IS_ASTRA_DB, DefaultAsyncCollection -class TestExceptionsAsync: +class TestCollectionExceptionsAsync: @pytest.mark.describe("test of collection insert_many type-failure modes, async") async def test_collection_insert_many_type_failures_async( self, diff --git a/tests/idiomatic/integration/test_exceptions_sync.py b/tests/base/integration/collections/test_collection_exceptions_sync.py similarity index 99% rename from tests/idiomatic/integration/test_exceptions_sync.py rename to tests/base/integration/collections/test_collection_exceptions_sync.py index 245e0c20..00056ff6 100644 --- a/tests/idiomatic/integration/test_exceptions_sync.py +++ b/tests/base/integration/collections/test_collection_exceptions_sync.py @@ -28,7 +28,7 @@ from ..conftest import IS_ASTRA_DB, DefaultCollection -class TestExceptionsSync: +class TestCollectionExceptionsSync: @pytest.mark.describe("test of collection insert_many type-failure modes, sync") def test_collection_insert_many_type_failures_sync( self, diff --git a/tests/idiomatic/integration/test_timeout_async.py b/tests/base/integration/collections/test_collection_timeout_async.py similarity index 99% rename from tests/idiomatic/integration/test_timeout_async.py rename to tests/base/integration/collections/test_collection_timeout_async.py index d7ef64a2..fcce2062 100644 --- a/tests/idiomatic/integration/test_timeout_async.py +++ b/tests/base/integration/collections/test_collection_timeout_async.py @@ -29,7 +29,7 @@ from ..conftest import DefaultAsyncCollection -class TestTimeoutAsync: +class TestCollectionTimeoutAsync: @pytest.mark.describe("test of collection count_documents timeout, async") async def test_collection_count_documents_timeout_async( self, diff --git a/tests/idiomatic/integration/test_timeout_sync.py b/tests/base/integration/collections/test_collection_timeout_sync.py similarity index 99% rename from tests/idiomatic/integration/test_timeout_sync.py rename to tests/base/integration/collections/test_collection_timeout_sync.py index 575b1867..f294eb53 100644 --- a/tests/idiomatic/integration/test_timeout_sync.py +++ b/tests/base/integration/collections/test_collection_timeout_sync.py @@ -25,7 +25,7 @@ from ..conftest import IS_ASTRA_DB, DefaultCollection -class TestTimeoutSync: +class TestCollectionTimeoutSync: @pytest.mark.describe("test of collection count_documents timeout, sync") def test_collection_count_documents_timeout_sync( self, diff --git a/tests/idiomatic/integration/test_collection_typing.py b/tests/base/integration/collections/test_collection_typing.py similarity index 100% rename from tests/idiomatic/integration/test_collection_typing.py rename to tests/base/integration/collections/test_collection_typing.py diff --git a/tests/vectorize_idiomatic/integration/test_vectorize_methods_async.py b/tests/base/integration/collections/test_collection_vectorize_methods_async.py similarity index 97% rename from tests/vectorize_idiomatic/integration/test_vectorize_methods_async.py rename to tests/base/integration/collections/test_collection_vectorize_methods_async.py index a359bb79..e6719f44 100644 --- a/tests/vectorize_idiomatic/integration/test_vectorize_methods_async.py +++ b/tests/base/integration/collections/test_collection_vectorize_methods_async.py @@ -25,10 +25,11 @@ from astrapy.exceptions import DataAPIResponseException from astrapy.info import CollectionDefinition -from ..conftest import DefaultAsyncCollection +from ..conftest import DefaultAsyncCollection, HEADER_EMBEDDING_API_KEY_OPENAI -class TestVectorizeMethodsAsync: +@pytest.mark.skipif(HEADER_EMBEDDING_API_KEY_OPENAI is None, reason="No HEADER_EMBEDDING_API_KEY_OPENAI credential") +class TestCollectionVectorizeMethodsAsync: @pytest.mark.describe("test of vectorize in collection methods, async") async def test_collection_methods_vectorize_async( self, diff --git a/tests/vectorize_idiomatic/integration/test_vectorize_methods_sync.py b/tests/base/integration/collections/test_collection_vectorize_methods_sync.py similarity index 97% rename from tests/vectorize_idiomatic/integration/test_vectorize_methods_sync.py rename to tests/base/integration/collections/test_collection_vectorize_methods_sync.py index 5eefc1e6..31d179eb 100644 --- a/tests/vectorize_idiomatic/integration/test_vectorize_methods_sync.py +++ b/tests/base/integration/collections/test_collection_vectorize_methods_sync.py @@ -23,10 +23,11 @@ from astrapy.exceptions import DataAPIResponseException from astrapy.info import CollectionDefinition -from ..conftest import DefaultCollection +from ..conftest import DefaultCollection, HEADER_EMBEDDING_API_KEY_OPENAI -class TestVectorizeMethodsSync: +@pytest.mark.skipif(HEADER_EMBEDDING_API_KEY_OPENAI is None, reason="No HEADER_EMBEDDING_API_KEY_OPENAI credential") +class TestCollectionVectorizeMethodsSync: @pytest.mark.describe("test of vectorize in collection methods, sync") def test_collection_methods_vectorize_sync( self, diff --git a/tests/base/integration/conftest.py b/tests/base/integration/conftest.py new file mode 100644 index 00000000..baeca2a0 --- /dev/null +++ b/tests/base/integration/conftest.py @@ -0,0 +1,33 @@ +# Copyright DataStax, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from ..conftest import ( + ADMIN_ENV_LIST, + ADMIN_ENV_VARIABLE_MAP, + DO_IDIOMATIC_ADMIN_TESTS, + HEADER_EMBEDDING_API_KEY_OPENAI, + IS_ASTRA_DB, + SECONDARY_KEYSPACE, + DataAPICredentials, + DataAPICredentialsInfo, + DefaultAsyncCollection, + DefaultAsyncTable, + DefaultCollection, + DefaultTable, + async_fail_if_not_removed, + clean_nulls_from_dict, + sync_fail_if_not_removed, +) \ No newline at end of file diff --git a/tests/idiomatic/__init__.py b/tests/base/integration/misc/__init__.py similarity index 100% rename from tests/idiomatic/__init__.py rename to tests/base/integration/misc/__init__.py diff --git a/tests/vectorize_idiomatic/integration/test_vectorize_ops_async.py b/tests/base/integration/misc/test_vectorize_ops_async.py similarity index 100% rename from tests/vectorize_idiomatic/integration/test_vectorize_ops_async.py rename to tests/base/integration/misc/test_vectorize_ops_async.py diff --git a/tests/vectorize_idiomatic/integration/test_vectorize_ops_sync.py b/tests/base/integration/misc/test_vectorize_ops_sync.py similarity index 100% rename from tests/vectorize_idiomatic/integration/test_vectorize_ops_sync.py rename to tests/base/integration/misc/test_vectorize_ops_sync.py diff --git a/tests/tables/integration/__init__.py b/tests/base/integration/tables/__init__.py similarity index 100% rename from tests/tables/integration/__init__.py rename to tests/base/integration/tables/__init__.py diff --git a/tests/tables/integration/table_row_assets.py b/tests/base/integration/tables/table_row_assets.py similarity index 100% rename from tests/tables/integration/table_row_assets.py rename to tests/base/integration/tables/table_row_assets.py diff --git a/tests/tables/integration/test_table_column_types_async.py b/tests/base/integration/tables/test_table_column_types_async.py similarity index 100% rename from tests/tables/integration/test_table_column_types_async.py rename to tests/base/integration/tables/test_table_column_types_async.py diff --git a/tests/tables/integration/test_table_column_types_sync.py b/tests/base/integration/tables/test_table_column_types_sync.py similarity index 100% rename from tests/tables/integration/test_table_column_types_sync.py rename to tests/base/integration/tables/test_table_column_types_sync.py diff --git a/tests/tables/integration/test_table_cursor_async.py b/tests/base/integration/tables/test_table_cursor_async.py similarity index 100% rename from tests/tables/integration/test_table_cursor_async.py rename to tests/base/integration/tables/test_table_cursor_async.py diff --git a/tests/tables/integration/test_table_cursor_sync.py b/tests/base/integration/tables/test_table_cursor_sync.py similarity index 100% rename from tests/tables/integration/test_table_cursor_sync.py rename to tests/base/integration/tables/test_table_cursor_sync.py diff --git a/tests/tables/integration/test_table_dml_async.py b/tests/base/integration/tables/test_table_dml_async.py similarity index 100% rename from tests/tables/integration/test_table_dml_async.py rename to tests/base/integration/tables/test_table_dml_async.py diff --git a/tests/tables/integration/test_table_dml_sync.py b/tests/base/integration/tables/test_table_dml_sync.py similarity index 100% rename from tests/tables/integration/test_table_dml_sync.py rename to tests/base/integration/tables/test_table_dml_sync.py diff --git a/tests/tables/integration/test_table_lifecycle_async.py b/tests/base/integration/tables/test_table_lifecycle_async.py similarity index 100% rename from tests/tables/integration/test_table_lifecycle_async.py rename to tests/base/integration/tables/test_table_lifecycle_async.py diff --git a/tests/tables/integration/test_table_lifecycle_sync.py b/tests/base/integration/tables/test_table_lifecycle_sync.py similarity index 100% rename from tests/tables/integration/test_table_lifecycle_sync.py rename to tests/base/integration/tables/test_table_lifecycle_sync.py diff --git a/tests/tables/integration/test_table_typing.py b/tests/base/integration/tables/test_table_typing.py similarity index 100% rename from tests/tables/integration/test_table_typing.py rename to tests/base/integration/tables/test_table_typing.py diff --git a/tests/tables/integration/test_table_vectorize_async.py b/tests/base/integration/tables/test_table_vectorize_async.py similarity index 100% rename from tests/tables/integration/test_table_vectorize_async.py rename to tests/base/integration/tables/test_table_vectorize_async.py diff --git a/tests/tables/integration/test_table_vectorize_sync.py b/tests/base/integration/tables/test_table_vectorize_sync.py similarity index 100% rename from tests/tables/integration/test_table_vectorize_sync.py rename to tests/base/integration/tables/test_table_vectorize_sync.py diff --git a/tests/tables/decimal_support_assets.py b/tests/base/table_decimal_support_assets.py similarity index 100% rename from tests/tables/decimal_support_assets.py rename to tests/base/table_decimal_support_assets.py diff --git a/tests/tables/conftest.py b/tests/base/table_structure_assets.py similarity index 51% rename from tests/tables/conftest.py rename to tests/base/table_structure_assets.py index 350beb72..fc397b64 100644 --- a/tests/tables/conftest.py +++ b/tests/base/table_structure_assets.py @@ -12,20 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Fixtures specific to the idiomatic testing.""" - from __future__ import annotations -import math -from decimal import Decimal -from typing import Any, Dict, Iterable - -import pytest - -from astrapy import AsyncDatabase, AsyncTable, DataAPIClient, Database, Table -from astrapy.api_options import APIOptions from astrapy.constants import SortMode -from astrapy.data_types import DataAPIMap, DataAPISet + from astrapy.info import ( CreateTableDefinition, TableKeyValuedColumnTypeDescriptor, @@ -37,16 +27,6 @@ VectorServiceOptions, ) -from ..conftest import ( - IS_ASTRA_DB, - SECONDARY_KEYSPACE, - DataAPICredentials, - DataAPICredentialsInfo, -) - -DefaultTable = Table[Dict[str, Any]] -DefaultAsyncTable = AsyncTable[Dict[str, Any]] - TEST_ALL_RETURNS_TABLE_NAME = "test_table_all_returns" TEST_ALL_RETURNS_TABLE_DEFINITION = CreateTableDefinition( columns={ @@ -225,304 +205,3 @@ "The possibility of its occurrence in atomic facts is the form of the object.", "The object is simple.", ] - -_NaN = object() -_DNaN = object() - - -def _repaint_NaNs(val: Any) -> Any: - if isinstance(val, float) and math.isnan(val): - return _NaN - if isinstance(val, Decimal) and math.isnan(val): - return _DNaN - elif isinstance(val, dict): - return {_repaint_NaNs(k): _repaint_NaNs(v) for k, v in val.items()} - elif isinstance(val, list): - return [_repaint_NaNs(x) for x in val] - elif isinstance(val, DataAPISet): - return DataAPISet(_repaint_NaNs(v) for v in val) - elif isinstance(val, DataAPIMap): - return DataAPIMap((_repaint_NaNs(k), _repaint_NaNs(v)) for k, v in val.items()) - elif isinstance(val, set): - return {_repaint_NaNs(v) for v in val} - elif isinstance(val, dict): - return {_repaint_NaNs(k): _repaint_NaNs(v) for k, v in val.items()} - else: - return val - - -def _typify_tuple(tpl: tuple[Any, ...]) -> tuple[Any, ...]: - return tuple([(v, type(v)) for v in tpl]) - - -@pytest.fixture(scope="session") -def client( - data_api_credentials_info: DataAPICredentialsInfo, -) -> Iterable[DataAPIClient]: - env = data_api_credentials_info["environment"] - client = DataAPIClient( - environment=env, - api_options=APIOptions( - database_additional_headers={"Feature-Flag-tables": "true"} - ), - ) - yield client - - -@pytest.fixture(scope="session") -def sync_database( - data_api_credentials_kwargs: DataAPICredentials, - data_api_credentials_info: DataAPICredentialsInfo, - client: DataAPIClient, -) -> Iterable[Database]: - database = client.get_database( - data_api_credentials_kwargs["api_endpoint"], - token=data_api_credentials_kwargs["token"], - keyspace=data_api_credentials_kwargs["keyspace"], - ) - - yield database - - -@pytest.fixture(scope="function") -def async_database( - sync_database: Database, -) -> Iterable[AsyncDatabase]: - yield sync_database.to_async() - - -@pytest.fixture(scope="session") -def sync_table_all_returns( - data_api_credentials_kwargs: DataAPICredentials, - sync_database: Database, -) -> Iterable[DefaultTable]: - """An actual table on DB, in the main keyspace""" - table = sync_database.create_table( - TEST_ALL_RETURNS_TABLE_NAME, - definition=TEST_ALL_RETURNS_TABLE_DEFINITION, - ) - yield table - - sync_database.drop_table(TEST_ALL_RETURNS_TABLE_NAME) - - -@pytest.fixture(scope="function") -def sync_empty_table_all_returns( - sync_table_all_returns: DefaultTable, -) -> Iterable[DefaultTable]: - """Emptied for each test function""" - sync_table_all_returns.delete_many({}) - yield sync_table_all_returns - - -@pytest.fixture(scope="function") -def async_table_all_returns( - sync_table_all_returns: DefaultTable, -) -> Iterable[DefaultAsyncTable]: - """An actual table on DB, the same as the sync counterpart""" - yield sync_table_all_returns.to_async() - - -@pytest.fixture(scope="function") -def async_empty_table_all_returns( - sync_empty_table_all_returns: DefaultTable, -) -> Iterable[DefaultAsyncTable]: - """Emptied for each test function""" - yield sync_empty_table_all_returns.to_async() - - -@pytest.fixture(scope="session") -def sync_table_simple( - data_api_credentials_kwargs: DataAPICredentials, - sync_database: Database, -) -> Iterable[DefaultTable]: - """An actual table on DB, in the main keyspace""" - table = sync_database.create_table( - TEST_SIMPLE_TABLE_NAME, - definition=TEST_SIMPLE_TABLE_DEFINITION, - ) - table.create_vector_index( - TEST_SIMPLE_TABLE_VECTOR_INDEX_NAME, - column=TEST_SIMPLE_TABLE_VECTOR_INDEX_COLUMN, - options=TEST_SIMPLE_TABLE_VECTOR_INDEX_OPTIONS, - ) - yield table - - sync_database.drop_table(TEST_SIMPLE_TABLE_NAME) - - -@pytest.fixture(scope="function") -def sync_empty_table_simple( - sync_table_simple: DefaultTable, -) -> Iterable[DefaultTable]: - """Emptied for each test function""" - sync_table_simple.delete_many({}) - yield sync_table_simple - - -@pytest.fixture(scope="function") -def async_table_simple( - sync_table_simple: DefaultTable, -) -> Iterable[DefaultAsyncTable]: - """An actual table on DB, the same as the sync counterpart""" - yield sync_table_simple.to_async() - - -@pytest.fixture(scope="function") -def async_empty_table_simple( - sync_empty_table_simple: DefaultTable, -) -> Iterable[DefaultAsyncTable]: - """Emptied for each test function""" - yield sync_empty_table_simple.to_async() - - -@pytest.fixture(scope="session") -def sync_table_composite( - data_api_credentials_kwargs: DataAPICredentials, - sync_database: Database, -) -> Iterable[DefaultTable]: - """An actual table on DB, in the main keyspace""" - table = sync_database.create_table( - TEST_COMPOSITE_TABLE_NAME, - definition=TEST_COMPOSITE_TABLE_DEFINITION, - ) - table.create_vector_index( - TEST_COMPOSITE_TABLE_VECTOR_INDEX_NAME, - column=TEST_COMPOSITE_TABLE_VECTOR_INDEX_COLUMN, - options=TEST_COMPOSITE_TABLE_VECTOR_INDEX_OPTIONS, - ) - table.create_index( - TEST_COMPOSITE_TABLE_BOOLEAN_INDEX_NAME, - column=TEST_COMPOSITE_TABLE_BOOLEAN_INDEX_COLUMN, - options=TEST_COMPOSITE_TABLE_BOOLEAN_INDEX_OPTIONS, - ) - yield table - - sync_database.drop_table(TEST_COMPOSITE_TABLE_NAME) - - -@pytest.fixture(scope="function") -def sync_empty_table_composite( - sync_table_composite: DefaultTable, -) -> Iterable[DefaultTable]: - """Emptied for each test function""" - sync_table_composite.delete_many({}) - yield sync_table_composite - - -@pytest.fixture(scope="function") -def async_table_composite( - sync_table_composite: DefaultTable, -) -> Iterable[DefaultAsyncTable]: - """An actual table on DB, the same as the sync counterpart""" - yield sync_table_composite.to_async() - - -@pytest.fixture(scope="function") -def async_empty_table_composite( - sync_empty_table_composite: DefaultTable, -) -> Iterable[DefaultAsyncTable]: - """Emptied for each test function""" - yield sync_empty_table_composite.to_async() - - -@pytest.fixture(scope="session") -def sync_table_vectorize( - data_api_credentials_kwargs: DataAPICredentials, - sync_database: Database, -) -> Iterable[DefaultTable]: - """An actual table on DB, in the main keyspace""" - table = sync_database.create_table( - TEST_VECTORIZE_TABLE_NAME, - definition=TEST_VECTORIZE_TABLE_DEFINITION, - ) - table.create_vector_index( - TEST_VECTORIZE_TABLE_VECTOR_INDEX_NAME, - column=TEST_SIMPLE_TABLE_VECTOR_INDEX_COLUMN, - options=TEST_SIMPLE_TABLE_VECTOR_INDEX_OPTIONS, - ) - yield table - - sync_database.drop_table(TEST_VECTORIZE_TABLE_NAME) - - -@pytest.fixture(scope="function") -def sync_empty_table_vectorize( - sync_table_vectorize: DefaultTable, -) -> Iterable[DefaultTable]: - """Emptied for each test function""" - sync_table_vectorize.delete_many({}) - yield sync_table_vectorize - - -@pytest.fixture(scope="function") -def async_table_vectorize( - sync_table_vectorize: DefaultTable, -) -> Iterable[DefaultAsyncTable]: - """An actual table on DB, the same as the sync counterpart""" - yield sync_table_vectorize.to_async() - - -@pytest.fixture(scope="function") -def async_empty_table_vectorize( - sync_empty_table_vectorize: DefaultTable, -) -> Iterable[DefaultAsyncTable]: - """Emptied for each test function""" - yield sync_empty_table_vectorize.to_async() - - -@pytest.fixture(scope="session") -def sync_table_kms_vectorize( - data_api_credentials_kwargs: DataAPICredentials, - sync_database: Database, -) -> Iterable[DefaultTable]: - """An actual table on DB, in the main keyspace""" - table = sync_database.create_table( - TEST_KMS_VECTORIZE_TABLE_NAME, - definition=TEST_KMS_VECTORIZE_TABLE_DEFINITION, - ) - table.create_vector_index( - TEST_KMS_VECTORIZE_TABLE_VECTOR_INDEX_NAME, - column=TEST_SIMPLE_TABLE_VECTOR_INDEX_COLUMN, - options=TEST_SIMPLE_TABLE_VECTOR_INDEX_OPTIONS, - ) - yield table - - sync_database.drop_table(TEST_KMS_VECTORIZE_TABLE_NAME) - - -@pytest.fixture(scope="function") -def sync_empty_table_kms_vectorize( - sync_table_kms_vectorize: DefaultTable, -) -> Iterable[DefaultTable]: - """Emptied for each test function""" - sync_table_kms_vectorize.delete_many({}) - yield sync_table_kms_vectorize - - -@pytest.fixture(scope="function") -def async_table_kms_vectorize( - sync_table_kms_vectorize: DefaultTable, -) -> Iterable[DefaultAsyncTable]: - """An actual table on DB, the same as the sync counterpart""" - yield sync_table_kms_vectorize.to_async() - - -@pytest.fixture(scope="function") -def async_empty_table_kms_vectorize( - sync_empty_table_kms_vectorize: DefaultTable, -) -> Iterable[DefaultAsyncTable]: - """Emptied for each test function""" - yield sync_empty_table_kms_vectorize.to_async() - - -__all__ = [ - "DataAPICredentials", - "DataAPICredentialsInfo", - "sync_database", - "async_database", - "IS_ASTRA_DB", - "SECONDARY_KEYSPACE", - "_repaint_NaNs", - "_typify_tuple", -] diff --git a/tests/vectorize_idiomatic/unit/__init__.py b/tests/base/unit/__init__.py similarity index 100% rename from tests/vectorize_idiomatic/unit/__init__.py rename to tests/base/unit/__init__.py diff --git a/tests/idiomatic/unit/test_admin_conversions.py b/tests/base/unit/test_admin_conversions.py similarity index 100% rename from tests/idiomatic/unit/test_admin_conversions.py rename to tests/base/unit/test_admin_conversions.py diff --git a/tests/idiomatic/unit/test_apicommander.py b/tests/base/unit/test_apicommander.py similarity index 100% rename from tests/idiomatic/unit/test_apicommander.py rename to tests/base/unit/test_apicommander.py diff --git a/tests/idiomatic/unit/test_apioptions.py b/tests/base/unit/test_apioptions.py similarity index 100% rename from tests/idiomatic/unit/test_apioptions.py rename to tests/base/unit/test_apioptions.py diff --git a/tests/idiomatic/unit/test_collection_decimal_support.py b/tests/base/unit/test_collection_decimal_support.py similarity index 98% rename from tests/idiomatic/unit/test_collection_decimal_support.py rename to tests/base/unit/test_collection_decimal_support.py index e7492cdd..d91b2adf 100644 --- a/tests/idiomatic/unit/test_collection_decimal_support.py +++ b/tests/base/unit/test_collection_decimal_support.py @@ -22,7 +22,7 @@ ) from astrapy.utils.api_commander import APICommander -from ..decimal_support_assets import ( +from ..collection_decimal_support_assets import ( BASELINE_OBJ, S_OPTS_NO_DECS, S_OPTS_OK_DECS, diff --git a/tests/idiomatic/unit/test_collection_options.py b/tests/base/unit/test_collection_options.py similarity index 100% rename from tests/idiomatic/unit/test_collection_options.py rename to tests/base/unit/test_collection_options.py diff --git a/tests/idiomatic/unit/test_collection_timeouts.py b/tests/base/unit/test_collection_timeouts.py similarity index 100% rename from tests/idiomatic/unit/test_collection_timeouts.py rename to tests/base/unit/test_collection_timeouts.py diff --git a/tests/idiomatic/unit/test_collections_async.py b/tests/base/unit/test_collections_async.py similarity index 100% rename from tests/idiomatic/unit/test_collections_async.py rename to tests/base/unit/test_collections_async.py diff --git a/tests/idiomatic/unit/test_collections_sync.py b/tests/base/unit/test_collections_sync.py similarity index 100% rename from tests/idiomatic/unit/test_collections_sync.py rename to tests/base/unit/test_collections_sync.py diff --git a/tests/vectorize_idiomatic/unit/test_collectionvectorserviceoptions.py b/tests/base/unit/test_collectionvectorserviceoptions.py similarity index 100% rename from tests/vectorize_idiomatic/unit/test_collectionvectorserviceoptions.py rename to tests/base/unit/test_collectionvectorserviceoptions.py diff --git a/tests/tables/unit/test_dataapidate.py b/tests/base/unit/test_dataapidate.py similarity index 100% rename from tests/tables/unit/test_dataapidate.py rename to tests/base/unit/test_dataapidate.py diff --git a/tests/tables/unit/test_dataapiduration.py b/tests/base/unit/test_dataapiduration.py similarity index 100% rename from tests/tables/unit/test_dataapiduration.py rename to tests/base/unit/test_dataapiduration.py diff --git a/tests/tables/unit/test_dataapimap.py b/tests/base/unit/test_dataapimap.py similarity index 100% rename from tests/tables/unit/test_dataapimap.py rename to tests/base/unit/test_dataapimap.py diff --git a/tests/tables/unit/test_dataapiset.py b/tests/base/unit/test_dataapiset.py similarity index 100% rename from tests/tables/unit/test_dataapiset.py rename to tests/base/unit/test_dataapiset.py diff --git a/tests/tables/unit/test_dataapitime.py b/tests/base/unit/test_dataapitime.py similarity index 100% rename from tests/tables/unit/test_dataapitime.py rename to tests/base/unit/test_dataapitime.py diff --git a/tests/idiomatic/unit/test_dataapitimestamp.py b/tests/base/unit/test_dataapitimestamp.py similarity index 100% rename from tests/idiomatic/unit/test_dataapitimestamp.py rename to tests/base/unit/test_dataapitimestamp.py diff --git a/tests/idiomatic/unit/test_dataapivector.py b/tests/base/unit/test_dataapivector.py similarity index 100% rename from tests/idiomatic/unit/test_dataapivector.py rename to tests/base/unit/test_dataapivector.py diff --git a/tests/idiomatic/unit/test_databases_async.py b/tests/base/unit/test_databases_async.py similarity index 100% rename from tests/idiomatic/unit/test_databases_async.py rename to tests/base/unit/test_databases_async.py diff --git a/tests/idiomatic/unit/test_databases_sync.py b/tests/base/unit/test_databases_sync.py similarity index 100% rename from tests/idiomatic/unit/test_databases_sync.py rename to tests/base/unit/test_databases_sync.py diff --git a/tests/tables/unit/test_datetime_serdes_options.py b/tests/base/unit/test_datetime_serdes_options.py similarity index 100% rename from tests/tables/unit/test_datetime_serdes_options.py rename to tests/base/unit/test_datetime_serdes_options.py diff --git a/tests/idiomatic/unit/test_document_extractors.py b/tests/base/unit/test_document_extractors.py similarity index 100% rename from tests/idiomatic/unit/test_document_extractors.py rename to tests/base/unit/test_document_extractors.py diff --git a/tests/vectorize_idiomatic/unit/test_embeddingheadersprovider.py b/tests/base/unit/test_embeddingheadersprovider.py similarity index 100% rename from tests/vectorize_idiomatic/unit/test_embeddingheadersprovider.py rename to tests/base/unit/test_embeddingheadersprovider.py diff --git a/tests/idiomatic/unit/test_exceptions.py b/tests/base/unit/test_exceptions.py similarity index 100% rename from tests/idiomatic/unit/test_exceptions.py rename to tests/base/unit/test_exceptions.py diff --git a/tests/idiomatic/unit/test_ids.py b/tests/base/unit/test_ids.py similarity index 100% rename from tests/idiomatic/unit/test_ids.py rename to tests/base/unit/test_ids.py diff --git a/tests/idiomatic/unit/test_imports.py b/tests/base/unit/test_imports.py similarity index 100% rename from tests/idiomatic/unit/test_imports.py rename to tests/base/unit/test_imports.py diff --git a/tests/idiomatic/unit/test_info.py b/tests/base/unit/test_info.py similarity index 100% rename from tests/idiomatic/unit/test_info.py rename to tests/base/unit/test_info.py diff --git a/tests/idiomatic/unit/test_multicalltimeoutmanager.py b/tests/base/unit/test_multicalltimeoutmanager.py similarity index 100% rename from tests/idiomatic/unit/test_multicalltimeoutmanager.py rename to tests/base/unit/test_multicalltimeoutmanager.py diff --git a/tests/tables/unit/test_table_decimal_support.py b/tests/base/unit/test_table_decimal_support.py similarity index 99% rename from tests/tables/unit/test_table_decimal_support.py rename to tests/base/unit/test_table_decimal_support.py index f52e45c2..a2549621 100644 --- a/tests/tables/unit/test_table_decimal_support.py +++ b/tests/base/unit/test_table_decimal_support.py @@ -24,7 +24,7 @@ from astrapy.utils.api_options import FullSerdesOptions, defaultAPIOptions from ..conftest import _repaint_NaNs -from ..decimal_support_assets import ( +from ..table_decimal_support_assets import ( BASELINE_COLUMNS, BASELINE_KEY_STR, BASELINE_OBJ, diff --git a/tests/tables/unit/test_table_dry_methods.py b/tests/base/unit/test_table_dry_methods.py similarity index 100% rename from tests/tables/unit/test_table_dry_methods.py rename to tests/base/unit/test_table_dry_methods.py diff --git a/tests/tables/unit/test_tableconverteragent.py b/tests/base/unit/test_tableconverteragent.py similarity index 100% rename from tests/tables/unit/test_tableconverteragent.py rename to tests/base/unit/test_tableconverteragent.py diff --git a/tests/tables/unit/test_tabledescriptors.py b/tests/base/unit/test_tabledescriptors.py similarity index 100% rename from tests/tables/unit/test_tabledescriptors.py rename to tests/base/unit/test_tabledescriptors.py diff --git a/tests/idiomatic/unit/test_timeouts.py b/tests/base/unit/test_timeouts.py similarity index 100% rename from tests/idiomatic/unit/test_timeouts.py rename to tests/base/unit/test_timeouts.py diff --git a/tests/idiomatic/unit/test_token_providers.py b/tests/base/unit/test_token_providers.py similarity index 100% rename from tests/idiomatic/unit/test_token_providers.py rename to tests/base/unit/test_token_providers.py diff --git a/tests/tables/unit/test_tpostprocessors.py b/tests/base/unit/test_tpostprocessors.py similarity index 100% rename from tests/tables/unit/test_tpostprocessors.py rename to tests/base/unit/test_tpostprocessors.py diff --git a/tests/conftest.py b/tests/conftest.py index 4fe30111..da4c5e76 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -27,6 +27,7 @@ from astrapy import DataAPIClient from astrapy.admin import parse_api_endpoint +from astrapy.api_options import APIOptions from astrapy.authentication import TokenProvider from astrapy.constants import Environment from astrapy.settings.defaults import DEFAULT_ASTRA_DB_KEYSPACE @@ -40,6 +41,7 @@ ASTRA_DB_TOKEN_PROVIDER, DO_IDIOMATIC_ADMIN_TESTS, DOCKER_COMPOSE_LOCAL_DATA_API, + HEADER_EMBEDDING_API_KEY_OPENAI, IS_ASTRA_DB, LOCAL_DATA_API_APPLICATION_TOKEN, LOCAL_DATA_API_ENDPOINT, @@ -190,11 +192,48 @@ def data_api_credentials_info( return astra_db_cred_info +@pytest.fixture(scope="session") +def client( + data_api_credentials_info: DataAPICredentialsInfo, +) -> Iterable[DataAPIClient]: + env = data_api_credentials_info["environment"] + client = DataAPIClient( + environment=env, + api_options=APIOptions( + database_additional_headers={"Feature-Flag-Tables": "true"}, + ), + ) + yield client + + +@pytest.fixture(scope="session") +def sync_database( + data_api_credentials_kwargs: DataAPICredentials, + data_api_credentials_info: DataAPICredentialsInfo, + client: DataAPIClient, +) -> Iterable[Database]: + database = client.get_database( + data_api_credentials_kwargs["api_endpoint"], + token=data_api_credentials_kwargs["token"], + keyspace=data_api_credentials_kwargs["keyspace"], + ) + + yield database + + +@pytest.fixture(scope="function") +def async_database( + sync_database: Database, +) -> Iterable[AsyncDatabase]: + yield sync_database.to_async() + + __all__ = [ "ASTRA_DB_API_ENDPOINT", "ASTRA_DB_APPLICATION_TOKEN", "ASTRA_DB_KEYSPACE", "DOCKER_COMPOSE_LOCAL_DATA_API", + "HEADER_EMBEDDING_API_KEY_OPENAI", "IS_ASTRA_DB", "LOCAL_DATA_API_APPLICATION_TOKEN", "LOCAL_DATA_API_ENDPOINT", diff --git a/tests/dse_compose/docker-compose.yml b/tests/dse_compose_manual/docker-compose.yml similarity index 97% rename from tests/dse_compose/docker-compose.yml rename to tests/dse_compose_manual/docker-compose.yml index 85025a71..116dc10c 100644 --- a/tests/dse_compose/docker-compose.yml +++ b/tests/dse_compose_manual/docker-compose.yml @@ -28,7 +28,7 @@ services: retries: 10 data-api: - image: stargateio/data-api:v1.0.18 + image: stargateio/data-api:v1.0.20 depends_on: coordinator: condition: service_healthy diff --git a/tests/idiomatic/conftest.py b/tests/idiomatic/conftest.py deleted file mode 100644 index 39ac485a..00000000 --- a/tests/idiomatic/conftest.py +++ /dev/null @@ -1,158 +0,0 @@ -# Copyright DataStax, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Fixtures specific to the idiomatic testing.""" - -from __future__ import annotations - -from typing import Any, Dict, Iterable - -import pytest - -from astrapy import AsyncCollection, AsyncDatabase, Collection, DataAPIClient, Database -from astrapy.constants import VectorMetric -from astrapy.info import CollectionDefinition, CollectionVectorOptions -from astrapy.utils.api_options import APIOptions, SerdesOptions - -from ..conftest import ( - ADMIN_ENV_LIST, - ADMIN_ENV_VARIABLE_MAP, - DO_IDIOMATIC_ADMIN_TESTS, - IS_ASTRA_DB, - SECONDARY_KEYSPACE, - DataAPICredentials, - DataAPICredentialsInfo, - async_fail_if_not_removed, - sync_fail_if_not_removed, -) - -DefaultCollection = Collection[Dict[str, Any]] -DefaultAsyncCollection = AsyncCollection[Dict[str, Any]] - -TEST_COLLECTION_INSTANCE_NAME = "test_coll_instance" -TEST_COLLECTION_NAME = "id_test_collection" - - -@pytest.fixture(scope="session") -def client( - data_api_credentials_info: DataAPICredentialsInfo, -) -> Iterable[DataAPIClient]: - env = data_api_credentials_info["environment"] - client = DataAPIClient(environment=env) - yield client - - -@pytest.fixture(scope="session") -def sync_database( - data_api_credentials_kwargs: DataAPICredentials, - data_api_credentials_info: DataAPICredentialsInfo, - client: DataAPIClient, -) -> Iterable[Database]: - database = client.get_database( - data_api_credentials_kwargs["api_endpoint"], - token=data_api_credentials_kwargs["token"], - keyspace=data_api_credentials_kwargs["keyspace"], - ) - - yield database - - -@pytest.fixture(scope="function") -def async_database( - sync_database: Database, -) -> Iterable[AsyncDatabase]: - yield sync_database.to_async() - - -@pytest.fixture(scope="session") -def sync_collection_instance( - data_api_credentials_kwargs: DataAPICredentials, - sync_database: Database, -) -> Iterable[DefaultCollection]: - """Just an instance of the class, no DB-level stuff.""" - yield sync_database.get_collection(TEST_COLLECTION_INSTANCE_NAME) - - -@pytest.fixture(scope="function") -def async_collection_instance( - sync_collection_instance: DefaultCollection, -) -> Iterable[DefaultAsyncCollection]: - """Just an instance of the class, no DB-level stuff.""" - yield sync_collection_instance.to_async() - - -@pytest.fixture(scope="session") -def sync_collection( - data_api_credentials_kwargs: DataAPICredentials, - sync_database: Database, -) -> Iterable[DefaultCollection]: - """An actual collection on DB, in the main keyspace""" - collection = sync_database.create_collection( - TEST_COLLECTION_NAME, - definition=CollectionDefinition( - indexing={"deny": ["not_indexed"]}, - vector=CollectionVectorOptions( - dimension=2, - metric=VectorMetric.COSINE, - ), - ), - spawn_api_options=APIOptions( - serdes_options=SerdesOptions( - binary_encode_vectors=True, - custom_datatypes_in_reading=True, - unroll_iterables_to_lists=True, - ), - ), - ) - yield collection - - sync_database.drop_collection(TEST_COLLECTION_NAME) - - -@pytest.fixture(scope="function") -def sync_empty_collection( - sync_collection: DefaultCollection, -) -> Iterable[DefaultCollection]: - """Emptied for each test function""" - sync_collection.delete_many({}) - yield sync_collection - - -@pytest.fixture(scope="function") -def async_collection( - sync_collection: DefaultCollection, -) -> Iterable[DefaultAsyncCollection]: - """An actual collection on DB, the same as the sync counterpart""" - yield sync_collection.to_async() - - -@pytest.fixture(scope="function") -def async_empty_collection( - sync_empty_collection: DefaultCollection, -) -> Iterable[DefaultAsyncCollection]: - """Emptied for each test function""" - yield sync_empty_collection.to_async() - - -__all__ = [ - "DataAPICredentials", - "DataAPICredentialsInfo", - "sync_fail_if_not_removed", - "async_fail_if_not_removed", - "IS_ASTRA_DB", - "ADMIN_ENV_LIST", - "ADMIN_ENV_VARIABLE_MAP", - "DO_IDIOMATIC_ADMIN_TESTS", - "SECONDARY_KEYSPACE", -] diff --git a/tests/idiomatic/unit/test_datetime_serdes_options.py b/tests/idiomatic/unit/test_datetime_serdes_options.py deleted file mode 100644 index 0a09839f..00000000 --- a/tests/idiomatic/unit/test_datetime_serdes_options.py +++ /dev/null @@ -1,149 +0,0 @@ -# Copyright DataStax, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import annotations - -import datetime - -import pytest - -from astrapy.data.utils.collection_converters import ( - postprocess_collection_response, - preprocess_collection_payload, -) -from astrapy.data_types import DataAPITimestamp -from astrapy.utils.api_options import FullSerdesOptions - - -class TestDatetimeSerdesOptions: - @pytest.mark.describe("test reading $date from collection, custom dt") - def test_ddate_collection_reading_customdt(self) -> None: - tz = datetime.timezone(datetime.timedelta(hours=2, minutes=45)) - sd_options = FullSerdesOptions( - binary_encode_vectors=False, - custom_datatypes_in_reading=True, - unroll_iterables_to_lists=False, - use_decimals_in_collections=False, - accept_naive_datetimes=False, - datetime_tzinfo=tz, - ) - ts = DataAPITimestamp.from_string("1991-07-23T12:34:56+01:30") - raw_response = {"mu": {"$date": ts.timestamp_ms}} - postprocessed = postprocess_collection_response( - raw_response, - options=sd_options, - ) - assert postprocessed["mu"] == ts - - @pytest.mark.describe("test reading $date from collection, stdlib and tzaware") - def test_ddate_collection_reading_stdlibtz(self) -> None: - tz = datetime.timezone(datetime.timedelta(hours=2, minutes=45)) - sd_options = FullSerdesOptions( - binary_encode_vectors=False, - custom_datatypes_in_reading=False, - unroll_iterables_to_lists=False, - use_decimals_in_collections=False, - accept_naive_datetimes=False, - datetime_tzinfo=tz, - ) - dt = datetime.datetime(1991, 7, 23, 12, 34, 56, tzinfo=tz) - raw_response = {"mu": {"$date": dt.timestamp() * 1000}} - postprocessed = postprocess_collection_response( - raw_response, - options=sd_options, - ) - assert postprocessed["mu"] == dt - - @pytest.mark.describe("test reading $date from collection, stdlib and naive") - def test_ddate_collection_reading_stdlibnaive(self) -> None: - sd_options = FullSerdesOptions( - binary_encode_vectors=False, - custom_datatypes_in_reading=False, - unroll_iterables_to_lists=False, - use_decimals_in_collections=False, - accept_naive_datetimes=False, - datetime_tzinfo=None, - ) - dt = datetime.datetime(1991, 7, 23, 12, 34, 56) - raw_response = {"mu": {"$date": dt.timestamp() * 1000}} - postprocessed = postprocess_collection_response( - raw_response, - options=sd_options, - ) - assert postprocessed["mu"] == dt - - @pytest.mark.describe( - "test writing tzaware datetime to collection, no naive allowed" - ) - def test_ddate_collection_writing_tzaware_strict(self) -> None: - tz = datetime.timezone(datetime.timedelta(hours=2, minutes=45)) - sd_options = FullSerdesOptions( - binary_encode_vectors=False, - custom_datatypes_in_reading=False, - unroll_iterables_to_lists=False, - use_decimals_in_collections=False, - accept_naive_datetimes=False, - datetime_tzinfo=None, - ) - dt = datetime.datetime(1991, 7, 23, 12, 34, 56, tzinfo=tz) - preprocessed = preprocess_collection_payload({"mu": dt}, options=sd_options) - assert preprocessed is not None - assert preprocessed["mu"]["$date"] == dt.timestamp() * 1000 - - @pytest.mark.describe("test writing naive datetime to collection, no naive allowed") - def test_ddate_collection_writing_naive_strict(self) -> None: - sd_options = FullSerdesOptions( - binary_encode_vectors=False, - custom_datatypes_in_reading=False, - unroll_iterables_to_lists=False, - use_decimals_in_collections=False, - accept_naive_datetimes=False, - datetime_tzinfo=None, - ) - dt = datetime.datetime(1991, 7, 23, 12, 34, 56) - with pytest.raises(ValueError, match="tz"): - preprocess_collection_payload({"mu": dt}, options=sd_options) - - @pytest.mark.describe( - "test writing tzaware datetime to collection, naive permitted" - ) - def test_ddate_collection_writing_tzaware_relaxed(self) -> None: - tz = datetime.timezone(datetime.timedelta(hours=2, minutes=45)) - sd_options = FullSerdesOptions( - binary_encode_vectors=False, - custom_datatypes_in_reading=False, - unroll_iterables_to_lists=False, - use_decimals_in_collections=False, - accept_naive_datetimes=True, - datetime_tzinfo=None, - ) - dt = datetime.datetime(1991, 7, 23, 12, 34, 56, tzinfo=tz) - preprocessed = preprocess_collection_payload({"mu": dt}, options=sd_options) - assert preprocessed is not None - assert preprocessed["mu"]["$date"] == dt.timestamp() * 1000 - - @pytest.mark.describe("test writing naive datetime to collection, naive permitted") - def test_ddate_collection_writing_naive_relaxed(self) -> None: - sd_options = FullSerdesOptions( - binary_encode_vectors=False, - custom_datatypes_in_reading=False, - unroll_iterables_to_lists=False, - use_decimals_in_collections=False, - accept_naive_datetimes=True, - datetime_tzinfo=None, - ) - dt = datetime.datetime(1991, 7, 23, 12, 34, 56) - preprocessed = preprocess_collection_payload({"mu": dt}, options=sd_options) - assert preprocessed is not None - assert preprocessed["mu"]["$date"] == dt.timestamp() * 1000 diff --git a/tests/preprocess_env.py b/tests/preprocess_env.py index 3dbdd989..ea78bf86 100644 --- a/tests/preprocess_env.py +++ b/tests/preprocess_env.py @@ -167,3 +167,6 @@ def docker_compose_command(self) -> list[str]: } for admin_env in ADMIN_ENV_LIST } + +# misc variables +HEADER_EMBEDDING_API_KEY_OPENAI = os.environ.get("HEADER_EMBEDDING_API_KEY_OPENAI") diff --git a/tests/tables/unit/__init__.py b/tests/vectorize/__init__.py similarity index 94% rename from tests/tables/unit/__init__.py rename to tests/vectorize/__init__.py index 2c9ca172..84497ed1 100644 --- a/tests/tables/unit/__init__.py +++ b/tests/vectorize/__init__.py @@ -11,3 +11,5 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + +from __future__ import annotations diff --git a/tests/vectorize/conftest.py b/tests/vectorize/conftest.py new file mode 100644 index 00000000..65bc492d --- /dev/null +++ b/tests/vectorize/conftest.py @@ -0,0 +1,20 @@ +# Copyright DataStax, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + + +from ..conftest import ( + IS_ASTRA_DB, +) diff --git a/tests/vectorize/integration/__init__.py b/tests/vectorize/integration/__init__.py new file mode 100644 index 00000000..84497ed1 --- /dev/null +++ b/tests/vectorize/integration/__init__.py @@ -0,0 +1,15 @@ +# Copyright DataStax, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations diff --git a/tests/vectorize_idiomatic/integration/test_vectorize_providers.py b/tests/vectorize/integration/test_vectorize_providers.py similarity index 100% rename from tests/vectorize_idiomatic/integration/test_vectorize_providers.py rename to tests/vectorize/integration/test_vectorize_providers.py diff --git a/tests/vectorize_idiomatic/live_provider_info.py b/tests/vectorize/live_provider_info.py similarity index 100% rename from tests/vectorize_idiomatic/live_provider_info.py rename to tests/vectorize/live_provider_info.py diff --git a/tests/vectorize_idiomatic/query_providers.py b/tests/vectorize/query_providers.py similarity index 100% rename from tests/vectorize_idiomatic/query_providers.py rename to tests/vectorize/query_providers.py diff --git a/tests/vectorize_idiomatic/vectorize_models.py b/tests/vectorize/vectorize_models.py similarity index 100% rename from tests/vectorize_idiomatic/vectorize_models.py rename to tests/vectorize/vectorize_models.py diff --git a/tests/vectorize_idiomatic/conftest.py b/tests/vectorize_idiomatic/conftest.py deleted file mode 100644 index cc2912b5..00000000 --- a/tests/vectorize_idiomatic/conftest.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright DataStax, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -Fixtures specific to testing on vectorize-ready Data API. -""" - -from __future__ import annotations - -import os -from typing import Any, Dict, Iterable - -import pytest - -from astrapy import AsyncCollection, AsyncDatabase, Collection, DataAPIClient, Database -from astrapy.constants import VectorMetric -from astrapy.info import CollectionDefinition - -from ..conftest import ( - IS_ASTRA_DB, - DataAPICredentials, - DataAPICredentialsInfo, - clean_nulls_from_dict, -) - -DefaultCollection = Collection[Dict[str, Any]] -DefaultAsyncCollection = AsyncCollection[Dict[str, Any]] - -TEST_SERVICE_COLLECTION_NAME = "test_indepth_vectorize_collection" - - -@pytest.fixture(scope="session") -def sync_database( - data_api_credentials_kwargs: DataAPICredentials, - data_api_credentials_info: DataAPICredentialsInfo, -) -> Iterable[Database]: - env = data_api_credentials_info["environment"] - client = DataAPIClient(environment=env) - database = client.get_database( - data_api_credentials_kwargs["api_endpoint"], - token=data_api_credentials_kwargs["token"], - keyspace=data_api_credentials_kwargs["keyspace"], - ) - yield database - - -@pytest.fixture(scope="function") -def async_database( - sync_database: Database, -) -> Iterable[AsyncDatabase]: - yield sync_database.to_async() - - -@pytest.fixture(scope="session") -def service_collection_parameters() -> Iterable[dict[str, Any]]: - yield { - "dimension": 1536, - "provider": "openai", - "modelName": "text-embedding-ada-002", - "api_key": os.environ["HEADER_EMBEDDING_API_KEY_OPENAI"], - } - - -@pytest.fixture(scope="session") -def sync_service_collection( - data_api_credentials_kwargs: DataAPICredentials, - sync_database: Database, - service_collection_parameters: dict[str, Any], -) -> Iterable[DefaultCollection]: - """ - An actual collection on DB, in the main keyspace. - """ - params = service_collection_parameters - collection = sync_database.create_collection( - TEST_SERVICE_COLLECTION_NAME, - definition=( - CollectionDefinition.builder() - .set_vector_metric(VectorMetric.DOT_PRODUCT) - .set_vector_service( - provider=params["provider"], - model_name=params["modelName"], - ) - .build() - ), - embedding_api_key=params["api_key"], - ) - yield collection - - sync_database.drop_collection(TEST_SERVICE_COLLECTION_NAME) - - -@pytest.fixture(scope="function") -def sync_empty_service_collection( - sync_service_collection: DefaultCollection, -) -> Iterable[DefaultCollection]: - """Emptied for each test function""" - sync_service_collection.delete_many({}) - yield sync_service_collection - - -@pytest.fixture(scope="function") -def async_empty_service_collection( - sync_empty_service_collection: DefaultCollection, -) -> Iterable[DefaultAsyncCollection]: - """Emptied for each test function""" - yield sync_empty_service_collection.to_async() - - -__all__ = [ - "sync_database", - "async_database", - "clean_nulls_from_dict", - "IS_ASTRA_DB", -] From 055afa97147d5bf213a9e60f5c3bd2af4c5c2432 Mon Sep 17 00:00:00 2001 From: Stefano Lottini Date: Wed, 18 Dec 2024 00:10:12 +0100 Subject: [PATCH 2/6] tests and CI rework --- .github/workflows/hcd.yaml | 6 +- .github/workflows/main.yml | 12 +- .github/workflows/unit.yaml | 45 ++++ CHANGES | 2 +- Makefile | 23 +- README.md | 220 ++++++------------ tests/admin/conftest.py | 8 +- tests/admin/integration/test_admin.py | 2 - .../admin/integration/test_nonastra_admin.py | 2 - tests/base/conftest.py | 77 +++--- .../collection_decimal_support_assets.py | 31 +++ .../test_collection_decimal_support.py | 4 +- ...test_collection_vectorize_methods_async.py | 7 +- .../test_collection_vectorize_methods_sync.py | 7 +- tests/base/integration/conftest.py | 28 ++- .../tables/test_table_dml_async.py | 30 +-- .../integration/tables/test_table_dml_sync.py | 30 +-- tests/base/table_structure_assets.py | 1 - tests/base/unit/test_imports.py | 44 ++++ tests/conftest.py | 14 +- tests/dse_compose_manual/docker-compose.yml | 1 + tests/env_templates/env.astra.admin.template | 9 +- tests/env_templates/env.astra.template | 6 +- tests/env_templates/env.local.template | 6 +- .../env_templates/env.testcontainers.template | 5 + .../env.vectorize-minimal.template | 6 +- tests/env_templates/env.vectorize.template | 6 +- tests/hcd_compose/docker-compose.yml | 1 + tests/preprocess_env.py | 14 +- tests/vectorize/conftest.py | 5 +- 30 files changed, 344 insertions(+), 308 deletions(-) create mode 100644 .github/workflows/unit.yaml create mode 100644 tests/base/integration/collection_decimal_support_assets.py create mode 100644 tests/env_templates/env.testcontainers.template diff --git a/.github/workflows/hcd.yaml b/.github/workflows/hcd.yaml index 33febf5f..7b1218a9 100644 --- a/.github/workflows/hcd.yaml +++ b/.github/workflows/hcd.yaml @@ -1,4 +1,4 @@ -name: Run idiomatic pytest on HCD +name: Run base integration tests on HCD on: push: @@ -11,8 +11,6 @@ on: jobs: test: env: - # for admin-related testing if enabled - DO_IDIOMATIC_ADMIN_TESTS: ${{ secrets.DO_IDIOMATIC_ADMIN_TESTS }} # hardcoding the target DB DOCKER_COMPOSE_LOCAL_DATA_API: "yes" runs-on: ubuntu-latest @@ -34,4 +32,4 @@ jobs: - name: Run pytest run: | - poetry run pytest tests/idiomatic + poetry run pytest tests/base/integration diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fd1781bd..1984663c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: Run idiomatic pytest on Astra DB +name: Run base integration tests on Astra DB on: push: @@ -16,14 +16,6 @@ jobs: ASTRA_DB_API_ENDPOINT: ${{ secrets.ASTRA_DB_API_ENDPOINT }} ASTRA_DB_KEYSPACE: ${{ secrets.ASTRA_DB_KEYSPACE }} ASTRA_DB_SECONDARY_KEYSPACE: ${{ secrets.ASTRA_DB_SECONDARY_KEYSPACE }} - # for admin-related testing if enabled - DO_IDIOMATIC_ADMIN_TESTS: ${{ secrets.DO_IDIOMATIC_ADMIN_TESTS }} - PROD_ADMIN_TEST_ASTRA_DB_APPLICATION_TOKEN: ${{ secrets.PROD_ADMIN_TEST_ASTRA_DB_APPLICATION_TOKEN }} - PROD_ADMIN_TEST_ASTRA_DB_PROVIDER: ${{ secrets.PROD_ADMIN_TEST_ASTRA_DB_PROVIDER }} - PROD_ADMIN_TEST_ASTRA_DB_REGION: ${{ secrets.PROD_ADMIN_TEST_ASTRA_DB_REGION }} - DEV_ADMIN_TEST_ASTRA_DB_APPLICATION_TOKEN: ${{ secrets.DEV_ADMIN_TEST_ASTRA_DB_APPLICATION_TOKEN }} - DEV_ADMIN_TEST_ASTRA_DB_PROVIDER: ${{ secrets.DEV_ADMIN_TEST_ASTRA_DB_PROVIDER }} - DEV_ADMIN_TEST_ASTRA_DB_REGION: ${{ secrets.DEV_ADMIN_TEST_ASTRA_DB_REGION }} runs-on: ubuntu-latest steps: @@ -43,4 +35,4 @@ jobs: - name: Run pytest run: | - poetry run pytest tests/idiomatic + poetry run pytest tests/base/integration diff --git a/.github/workflows/unit.yaml b/.github/workflows/unit.yaml new file mode 100644 index 00000000..ffd36712 --- /dev/null +++ b/.github/workflows/unit.yaml @@ -0,0 +1,45 @@ +name: Run unit tests + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + test: + env: + # basic secrets + ASTRA_DB_APPLICATION_TOKEN: ${{ secrets.ASTRA_DB_APPLICATION_TOKEN }} + ASTRA_DB_API_ENDPOINT: ${{ secrets.ASTRA_DB_API_ENDPOINT }} + ASTRA_DB_KEYSPACE: ${{ secrets.ASTRA_DB_KEYSPACE }} + ASTRA_DB_SECONDARY_KEYSPACE: ${{ secrets.ASTRA_DB_SECONDARY_KEYSPACE }} + runs-on: ubuntu-latest + strategy: + matrix: + python-version: + - "3.9" + - "3.10" + - "3.11" + - "3.12" + name: "unit test on #${{ matrix.python-version }}" + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install poetry + poetry install + + - name: Run pytest + run: | + poetry run pytest tests/base/unit diff --git a/CHANGES b/CHANGES index 782c4ddf..c954a63b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,7 @@ main ==== restore support for Python 3.8, 3.9 -(reinstated some Data API functionalities being tested after the API got patched re: sorting on PK and pagination with that sorting) +maintenance: full restructuring of tests and CI (tables+collections on same footing+other) v 2.0.0-preview =============== diff --git a/Makefile b/Makefile index e1c67b17..3d3321ce 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ SHELL := /bin/bash -.PHONY: all format format-fix format-tests format-src test-idiomatic test-idiomatic-unit test-idiomatic-integration build help +.PHONY: all format format-fix format-tests format-src test-integration test build help all: help @@ -26,23 +26,14 @@ format-fix-src: format-src format-fix-tests: FMT_FLAGS= format-fix-tests: format-tests -test-idiomatic: - poetry run pytest tests/idiomatic -vv +test-integration: + poetry run pytest tests/base -vv -test-idiomatic-unit: - poetry run pytest tests/idiomatic/unit -vv +test: + poetry run pytest tests/base/unit -vv -test-idiomatic-integration: - poetry run pytest tests/idiomatic/integration -vv - -docker-test-idiomatic: - DOCKER_COMPOSE_LOCAL_DATA_API="yes" poetry run pytest tests/idiomatic -vv - -docker-test-idiomatic-unit: - DOCKER_COMPOSE_LOCAL_DATA_API="yes" poetry run pytest tests/idiomatic/unit -vv - -docker-test-idiomatic-integration: - DOCKER_COMPOSE_LOCAL_DATA_API="yes" poetry run pytest tests/idiomatic/integration -vv +docker-test-integration: + DOCKER_COMPOSE_LOCAL_DATA_API="yes" poetry run pytest tests/base -vv build: rm -f dist/astrapy* diff --git a/README.md b/README.md index 24fa2ef2..66cb0fe9 100644 --- a/README.md +++ b/README.md @@ -405,25 +405,17 @@ make format With `make format-fix` the style and imports are autofixed (by `ruff`) -Features must be thoroughly covered in tests (see `tests/idiomatic/*` for +Features must be thoroughly covered in tests (have a look at `tests/*` to infer naming convention and module structure). ### Running tests -Tests are grouped in three _blocks_ (in as many subdirs of `tests/`): +Tests are grouped in: +- "base", covering general-purpose astrapy functionality. Divided in unit/integration; +- "vectorize", extensively running a base workload on all provider/integration choices; +- "admin", doing a whole sweep of admin operations. Very slow on Astra DB. -- **idiomatic**: all 2.0+ classes and APIs, except... -- **tables**: the "tables" part only; and, -- **vectorize**: ... everything making use of `$vectorize` (within the idiomatic classes) - -Actually, for convenience, _sub-blocks_ of tests are considered: - -- **idiomatic regular**: everything except the admin parts -- **idiomatic admin Astra**: the Astra-specific admin operations -- **idiomatic admin nonAstra**: the nonAstra-specific admin operations -- **tables**: everything around just tables -- **vectorize in-depth**: many Data API interactions for a single choice of provider/model. This is mostly test the client -- **vectorize all-providers**: a slightly more shallow test repeated for all providers, models, auth methods etc. This is mostly testing the API +Astrapy's CI only runs "base". The other are to be checked manually when it's needed. Tests can be run on three types of Data API _targets_ (with slight differences in what is applicable): @@ -431,105 +423,58 @@ Tests can be run on three types of Data API _targets_ (with slight differences i - **nonAstra**: a ready-to-use (user-supplied) local Data API - **Astra**: an Astra DB target account (or two, as some tests are specific to dev environment) -Depending on the (sub-block, target) combination, some environment variables may be needed. -Templates for the environment variables are to be found in `tests/env_templates`. - -The general expectation is that idiomatic non-Admin tests, and vectorize in-depth tests, are -part of the main CI flow; conversely, admin and vectorize all-providers are kept as a -manual task to run (locally in most cases) when circumstances require it (use your judgement). - -#### Required environment variables - -Below is a detail of the reference template files needed for the various types -of testing: - -- **DockerCompose**: generally no variables needed, except: - - **vectorize in-depth**: provide as in `env.vectorize-minimal.template` - - **vectorize all-providers**: provide as in `env.vectorize.template` - - (also note that _idiomatic admin Astra_ amounts to nothing in this case) -- **nonAstra**: all tests require as in `env.local.template`, plus: - - **vectorize in-depth**: also provide as in `env.vectorize-minimal.template` - - **vectorize all-providers**: also provide as in `env.vectorize.template` - - (also note that _idiomatic admin Astra_ amounts to nothing in this case) -- **Astra**: all tests require as in `env.astra.template`, plus: - - **idiomatic admin Astra**: also provide as in `env.astra.admin.template` - - **vectorize in-depth**: also provide as in `env.vectorize-minimal.template` - - **vectorize all-providers**: also provide as in `env.vectorize.template` - - (also note that _idiomatic admin nonAstra_ amounts to nothing in this case) +Depending on the test, different environment variables are needed: refer to +the templates in `tests/env_templates`. The "basic" credentials (one of the three options) +are always required, _even for unit testing_. #### Sample testing commands -For the **DockerCompose** case, prepend all of the following with `DOCKER_COMPOSE_LOCAL_DATA_API="yes" `. - -All the usual `pytest` ways of restricting the test selection hold in addition -(e.g. `poetry run pytest tests/idiomatic/unit` or `[...] -k `). - -##### _idiomatic regular_: - -Warning: this will also trigger the very long-running _idiomatic admin Astra_ if the vars as in `env.astra.admin.template` are also detected. Likewise, the _idiomatic admin nonAstra_ may start (if `DO_IDIOMATIC_ADMIN_TESTS` is set), which however takes few seconds. - -``` -poetry run pytest tests/idiomatic -``` - -##### _idiomatic admin Astra_: - -``` -poetry run pytest tests/idiomatic/integration/test_admin.py -``` - -##### _idiomatic admin nonAstra_: +Base: ``` -DO_IDIOMATIC_ADMIN_TESTS="1" poetry run pytest tests/idiomatic/integration/test_nonastra_admin.py +# choose one: +poetry run pytest tests/base +poetry run pytest tests/base/unit +poetry run pytest tests/base/integration ``` -##### _vectorize in-depth_: +Admin: ``` -poetry run pytest tests/vectorize_idiomatic/integration/test_vectorize_methods*.py +# depending on the environment, different 'admin tests' will run: +poetry run pytest tests/admin ``` -or just: +Extended vectorize: ``` -poetry run pytest tests/vectorize_idiomatic/integration/test_vectorize_methods_sync.py -``` +# very many env. variables required for this one: +poetry run pytest tests/vectorize -##### _vectorize all-providers_: - -This generates all possible test cases and runs them: - -``` -poetry run pytest tests/vectorize_idiomatic +# restrict to some combination(s) with e.g.: +EMBEDDING_MODEL_TAGS="openai/text-embedding-3-large/HEADER/0,voyageAI/voyage-finance-2/SHARED_SECRET/f" \ + poetry run pytest tests/vectorize/integration/test_vectorize_providers.py \ + -k test_vectorize_usage_auth_type_header_sync ``` -For a spot test, you may restrict to one case, e.g. - -``` -EMBEDDING_MODEL_TAGS="openai/text-embedding-3-large/HEADER/0" poetry run pytest tests/vectorize_idiomatic/integration/test_vectorize_providers.py -k test_vectorize_usage_auth_type_header_sync -``` - -#### Useful flags for testing - -Remove logging noise with: +All the usual `pytest` ways of restricting the test selection hold +(e.g. `poetry run pytest tests/idiomatic/unit` or `[...] -k `). Also e.g.: ``` +# suppress log noise poetry run pytest [...] -o log_cli=0 -``` -Increase logging level to `DEBUG` (i.e. level `10`): - -``` +# increase log level poetry run pytest [...] -o log_cli=1 --log-cli-level=10 ``` - ## Appendices -### Appendix A: quick reference for imports +### Appendix A: quick reference for key imports -Client, data and admin abstractions (_Note: table-related imports to be added_): +_Note: check `tests/base/unit/test_imports.py` for more._ + +Client, data and admin abstractions ```python from astrapy import ( @@ -537,10 +482,12 @@ from astrapy import ( AstraDBDatabaseAdmin, AsyncCollection, AsyncDatabase, + AsyncTable, Collection, + Database, DataAPIClient, DataAPIDatabaseAdmin, - Database, + Table, ) ``` @@ -584,50 +531,17 @@ from astrapy.api_options import ( ) ``` -Result classes: +Data types: ```python -from astrapy.results import ( - CollectionDeleteResult, - CollectionInsertManyResult, - CollectionInsertOneResult, - CollectionUpdateResult, - OperationResult, - TableDeleteResult, - TableInsertManyResult, - TableInsertOneResult, - TableUpdateResult, -) -``` - -Exceptions: - -```python -from astrapy.exceptions import ( - CollectionDeleteManyException, - CollectionInsertManyException, - CollectionUpdateManyException, - CumulativeOperationException, - CursorException, - DataAPIDetailedErrorDescriptor, - DataAPIErrorDescriptor, - DataAPIException, - DataAPIHttpException, - DataAPIResponseException, - DataAPITimeoutException, - DevOpsAPIErrorDescriptor, - DevOpsAPIException, - DevOpsAPIHttpException, - DevOpsAPIResponseException, - DevOpsAPITimeoutException, - MultiCallTimeoutManager, - TableDeleteManyException, - TableInsertManyException, - TableUpdateManyException, - TooManyDocumentsToCountException, - TooManyRowsToCountException, - UnexpectedDataAPIResponseException, - UnexpectedDevOpsAPIResponseException, +from astrapy.data_types import ( + DataAPITimestamp, + DataAPIVector, + DataAPIDate, + DataAPIDuration, + DataAPIMap, + DataAPISet, + DataAPITime, ) ``` @@ -635,41 +549,45 @@ Info/metadata classes: ```python from astrapy.info import ( - AstraDBAdminDatabaseInfo, + AlterTableAddColumns, + AlterTableAddVectorize, + AlterTableDropColumns, + AlterTableDropVectorize, CollectionDefaultIDOptions, CollectionDefinition, - CollectionDescriptor, - CollectionInfo, CollectionVectorOptions, - AstraDBDatabaseInfo, + ColumnType, + CreateTableDefinition, EmbeddingProvider, EmbeddingProviderAuthentication, EmbeddingProviderModel, EmbeddingProviderParameter, EmbeddingProviderToken, - FindEmbeddingProvidersResult, + TableBaseIndexDefinition, + TableIndexDefinition, + TableIndexOptions, + TableKeyValuedColumnType, + TableKeyValuedColumnTypeDescriptor, + TablePrimaryKeyDescriptor, + TableScalarColumnTypeDescriptor, + TableUnsupportedColumnTypeDescriptor, + TableValuedColumnType, + TableValuedColumnTypeDescriptor, + TableVectorColumnTypeDescriptor, + TableVectorIndexDefinition, + TableVectorIndexOptions, VectorServiceOptions, ) ``` -Admin-related classes, functions and constants: - -```python -from astrapy.admin import ( - DatabaseAdmin, - ParsedAPIEndpoint, - fetch_database_info, - parse_api_endpoint, -) -``` - -Cursors: +Authentication: ```python -from astrapy.cursors import ( - AsyncCursor, - Cursor, - FindCursorState, +from astrapy.authentication import ( + StaticTokenProvider, + UsernamePasswordTokenProvider, + EmbeddingAPIKeyHeaderProvider, + AWSEmbeddingHeadersProvider, ) ``` diff --git a/tests/admin/conftest.py b/tests/admin/conftest.py index d497841e..0b6e5ca4 100644 --- a/tests/admin/conftest.py +++ b/tests/admin/conftest.py @@ -14,10 +14,14 @@ from __future__ import annotations - from ..conftest import ( ADMIN_ENV_LIST, ADMIN_ENV_VARIABLE_MAP, - DO_IDIOMATIC_ADMIN_TESTS, IS_ASTRA_DB, ) + +__all__ = [ + "ADMIN_ENV_LIST", + "ADMIN_ENV_VARIABLE_MAP", + "IS_ASTRA_DB", +] diff --git a/tests/admin/integration/test_admin.py b/tests/admin/integration/test_admin.py index dbb4f69b..97e33d01 100644 --- a/tests/admin/integration/test_admin.py +++ b/tests/admin/integration/test_admin.py @@ -25,7 +25,6 @@ from ..conftest import ( ADMIN_ENV_LIST, ADMIN_ENV_VARIABLE_MAP, - DO_IDIOMATIC_ADMIN_TESTS, IS_ASTRA_DB, ) @@ -77,7 +76,6 @@ async def await_until_true( @pytest.mark.skipif(not IS_ASTRA_DB, reason="Not supported outside of Astra DB") -@pytest.mark.skipif(not DO_IDIOMATIC_ADMIN_TESTS, reason="Admin tests are suppressed") class TestAdmin: @pytest.mark.parametrize( "admin_env_token", admin_test_envs_tokens(), ids=ADMIN_ENV_LIST diff --git a/tests/admin/integration/test_nonastra_admin.py b/tests/admin/integration/test_nonastra_admin.py index 318cf6bf..09330ed9 100644 --- a/tests/admin/integration/test_nonastra_admin.py +++ b/tests/admin/integration/test_nonastra_admin.py @@ -19,13 +19,11 @@ from astrapy import AsyncDatabase, Database from ..conftest import ( - DO_IDIOMATIC_ADMIN_TESTS, IS_ASTRA_DB, ) @pytest.mark.skipif(IS_ASTRA_DB, reason="Not supported on Astra DB") -@pytest.mark.skipif(not DO_IDIOMATIC_ADMIN_TESTS, reason="Admin tests are suppressed") class TestNonAstraAdmin: @pytest.mark.describe( "test of the keyspace crud with non-Astra DataAPIDatabaseAdmin, sync" diff --git a/tests/base/conftest.py b/tests/base/conftest.py index d771a946..c552ade5 100644 --- a/tests/base/conftest.py +++ b/tests/base/conftest.py @@ -20,56 +20,24 @@ import pytest -from astrapy import AsyncCollection, AsyncDatabase, AsyncTable, Collection, DataAPIClient, Database, Table +from astrapy import ( + AsyncCollection, + AsyncTable, + Collection, + Database, + Table, +) from astrapy.api_options import APIOptions, SerdesOptions -from astrapy.constants import SortMode, VectorMetric +from astrapy.constants import VectorMetric from astrapy.data_types import DataAPIMap, DataAPISet from astrapy.info import ( CollectionDefinition, CollectionVectorOptions, - CreateTableDefinition, - TableKeyValuedColumnTypeDescriptor, - TablePrimaryKeyDescriptor, - TableScalarColumnTypeDescriptor, - TableValuedColumnTypeDescriptor, - TableVectorColumnTypeDescriptor, - TableVectorIndexOptions, - VectorServiceOptions, -) - -from .table_structure_assets import ( - TEST_ALL_RETURNS_TABLE_NAME, - TEST_ALL_RETURNS_TABLE_DEFINITION, - TEST_SIMPLE_TABLE_NAME, - TEST_SIMPLE_TABLE_DEFINITION, - TEST_SIMPLE_TABLE_VECTOR_INDEX_NAME, - TEST_SIMPLE_TABLE_VECTOR_INDEX_COLUMN, - TEST_SIMPLE_TABLE_VECTOR_INDEX_OPTIONS, - TEST_COMPOSITE_TABLE_NAME, - TEST_COMPOSITE_TABLE_DEFINITION, - TEST_COMPOSITE_TABLE_VECTOR_INDEX_NAME, - TEST_COMPOSITE_TABLE_VECTOR_INDEX_COLUMN, - TEST_COMPOSITE_TABLE_VECTOR_INDEX_OPTIONS, - TEST_COMPOSITE_TABLE_BOOLEAN_INDEX_NAME, - TEST_COMPOSITE_TABLE_BOOLEAN_INDEX_COLUMN, - TEST_COMPOSITE_TABLE_BOOLEAN_INDEX_OPTIONS, - TEST_VECTORIZE_TABLE_NAME, - TEST_VECTORIZE_TABLE_DEFINITION, - TEST_VECTORIZE_TABLE_VECTOR_INDEX_NAME, - TEST_VECTORIZE_TABLE_VECTOR_INDEX_COLUMN, - TEST_VECTORIZE_TABLE_VECTOR_INDEX_OPTIONS, - TEST_KMS_VECTORIZE_TABLE_NAME, - TEST_KMS_VECTORIZE_TABLE_DEFINITION, - TEST_KMS_VECTORIZE_TABLE_VECTOR_INDEX_NAME, - TEST_KMS_VECTORIZE_TABLE_VECTOR_INDEX_COLUMN, - TEST_KMS_VECTORIZE_TABLE_VECTOR_INDEX_DEFINITION, - VECTORIZE_TEXTS, ) from ..conftest import ( ADMIN_ENV_LIST, ADMIN_ENV_VARIABLE_MAP, - DO_IDIOMATIC_ADMIN_TESTS, HEADER_EMBEDDING_API_KEY_OPENAI, IS_ASTRA_DB, SECONDARY_KEYSPACE, @@ -79,6 +47,30 @@ clean_nulls_from_dict, sync_fail_if_not_removed, ) +from .table_structure_assets import ( + TEST_ALL_RETURNS_TABLE_DEFINITION, + TEST_ALL_RETURNS_TABLE_NAME, + TEST_COMPOSITE_TABLE_BOOLEAN_INDEX_COLUMN, + TEST_COMPOSITE_TABLE_BOOLEAN_INDEX_NAME, + TEST_COMPOSITE_TABLE_BOOLEAN_INDEX_OPTIONS, + TEST_COMPOSITE_TABLE_DEFINITION, + TEST_COMPOSITE_TABLE_NAME, + TEST_COMPOSITE_TABLE_VECTOR_INDEX_COLUMN, + TEST_COMPOSITE_TABLE_VECTOR_INDEX_NAME, + TEST_COMPOSITE_TABLE_VECTOR_INDEX_OPTIONS, + TEST_KMS_VECTORIZE_TABLE_DEFINITION, + TEST_KMS_VECTORIZE_TABLE_NAME, + TEST_KMS_VECTORIZE_TABLE_VECTOR_INDEX_NAME, + TEST_SIMPLE_TABLE_DEFINITION, + TEST_SIMPLE_TABLE_NAME, + TEST_SIMPLE_TABLE_VECTOR_INDEX_COLUMN, + TEST_SIMPLE_TABLE_VECTOR_INDEX_NAME, + TEST_SIMPLE_TABLE_VECTOR_INDEX_OPTIONS, + TEST_VECTORIZE_TABLE_DEFINITION, + TEST_VECTORIZE_TABLE_NAME, + TEST_VECTORIZE_TABLE_VECTOR_INDEX_NAME, + VECTORIZE_TEXTS, +) DefaultCollection = Collection[Dict[str, Any]] DefaultAsyncCollection = AsyncCollection[Dict[str, Any]] @@ -472,16 +464,15 @@ def async_empty_table_kms_vectorize( __all__ = [ "DataAPICredentials", "DataAPICredentialsInfo", - # "async_database", "async_fail_if_not_removed", "clean_nulls_from_dict", "sync_fail_if_not_removed", - # "sync_database", + "HEADER_EMBEDDING_API_KEY_OPENAI", "IS_ASTRA_DB", "ADMIN_ENV_LIST", "ADMIN_ENV_VARIABLE_MAP", - "DO_IDIOMATIC_ADMIN_TESTS", "SECONDARY_KEYSPACE", + "VECTORIZE_TEXTS", "_repaint_NaNs", "_typify_tuple", ] diff --git a/tests/base/integration/collection_decimal_support_assets.py b/tests/base/integration/collection_decimal_support_assets.py new file mode 100644 index 00000000..31a8e39d --- /dev/null +++ b/tests/base/integration/collection_decimal_support_assets.py @@ -0,0 +1,31 @@ +# Copyright DataStax, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from ..collection_decimal_support_assets import ( + BASELINE_OBJ, + S_OPTS_NO_DECS, + S_OPTS_OK_DECS, + WDECS_OBJ, + is_decimal_super, +) + +__all__ = [ + "BASELINE_OBJ", + "S_OPTS_NO_DECS", + "S_OPTS_OK_DECS", + "WDECS_OBJ", + "is_decimal_super", +] diff --git a/tests/base/integration/collections/test_collection_decimal_support.py b/tests/base/integration/collections/test_collection_decimal_support.py index 397d44b2..20ea7611 100644 --- a/tests/base/integration/collections/test_collection_decimal_support.py +++ b/tests/base/integration/collections/test_collection_decimal_support.py @@ -18,14 +18,14 @@ from astrapy.api_options import APIOptions -from ..conftest import DefaultAsyncCollection, DefaultCollection -from ..decimal_support_assets import ( +from ..collection_decimal_support_assets import ( BASELINE_OBJ, S_OPTS_NO_DECS, S_OPTS_OK_DECS, WDECS_OBJ, is_decimal_super, ) +from ..conftest import DefaultAsyncCollection, DefaultCollection class TestCollectionDecimalSupportIntegration: diff --git a/tests/base/integration/collections/test_collection_vectorize_methods_async.py b/tests/base/integration/collections/test_collection_vectorize_methods_async.py index e6719f44..989fcd5f 100644 --- a/tests/base/integration/collections/test_collection_vectorize_methods_async.py +++ b/tests/base/integration/collections/test_collection_vectorize_methods_async.py @@ -25,10 +25,13 @@ from astrapy.exceptions import DataAPIResponseException from astrapy.info import CollectionDefinition -from ..conftest import DefaultAsyncCollection, HEADER_EMBEDDING_API_KEY_OPENAI +from ..conftest import HEADER_EMBEDDING_API_KEY_OPENAI, DefaultAsyncCollection -@pytest.mark.skipif(HEADER_EMBEDDING_API_KEY_OPENAI is None, reason="No HEADER_EMBEDDING_API_KEY_OPENAI credential") +@pytest.mark.skipif( + HEADER_EMBEDDING_API_KEY_OPENAI is None, + reason="No HEADER_EMBEDDING_API_KEY_OPENAI credential", +) class TestCollectionVectorizeMethodsAsync: @pytest.mark.describe("test of vectorize in collection methods, async") async def test_collection_methods_vectorize_async( diff --git a/tests/base/integration/collections/test_collection_vectorize_methods_sync.py b/tests/base/integration/collections/test_collection_vectorize_methods_sync.py index 31d179eb..139f50cd 100644 --- a/tests/base/integration/collections/test_collection_vectorize_methods_sync.py +++ b/tests/base/integration/collections/test_collection_vectorize_methods_sync.py @@ -23,10 +23,13 @@ from astrapy.exceptions import DataAPIResponseException from astrapy.info import CollectionDefinition -from ..conftest import DefaultCollection, HEADER_EMBEDDING_API_KEY_OPENAI +from ..conftest import HEADER_EMBEDDING_API_KEY_OPENAI, DefaultCollection -@pytest.mark.skipif(HEADER_EMBEDDING_API_KEY_OPENAI is None, reason="No HEADER_EMBEDDING_API_KEY_OPENAI credential") +@pytest.mark.skipif( + HEADER_EMBEDDING_API_KEY_OPENAI is None, + reason="No HEADER_EMBEDDING_API_KEY_OPENAI credential", +) class TestCollectionVectorizeMethodsSync: @pytest.mark.describe("test of vectorize in collection methods, sync") def test_collection_methods_vectorize_sync( diff --git a/tests/base/integration/conftest.py b/tests/base/integration/conftest.py index baeca2a0..7d80d2d3 100644 --- a/tests/base/integration/conftest.py +++ b/tests/base/integration/conftest.py @@ -17,17 +17,41 @@ from ..conftest import ( ADMIN_ENV_LIST, ADMIN_ENV_VARIABLE_MAP, - DO_IDIOMATIC_ADMIN_TESTS, HEADER_EMBEDDING_API_KEY_OPENAI, IS_ASTRA_DB, SECONDARY_KEYSPACE, + TEST_COLLECTION_NAME, + VECTORIZE_TEXTS, DataAPICredentials, DataAPICredentialsInfo, DefaultAsyncCollection, DefaultAsyncTable, DefaultCollection, DefaultTable, + _repaint_NaNs, + _typify_tuple, async_fail_if_not_removed, clean_nulls_from_dict, sync_fail_if_not_removed, -) \ No newline at end of file +) + +__all__ = [ + "DataAPICredentials", + "DataAPICredentialsInfo", + "async_fail_if_not_removed", + "clean_nulls_from_dict", + "sync_fail_if_not_removed", + "HEADER_EMBEDDING_API_KEY_OPENAI", + "IS_ASTRA_DB", + "ADMIN_ENV_LIST", + "ADMIN_ENV_VARIABLE_MAP", + "SECONDARY_KEYSPACE", + "TEST_COLLECTION_NAME", + "VECTORIZE_TEXTS", + "_repaint_NaNs", + "_typify_tuple", + "DefaultCollection", + "DefaultAsyncCollection", + "DefaultAsyncTable", + "DefaultTable", +] diff --git a/tests/base/integration/tables/test_table_dml_async.py b/tests/base/integration/tables/test_table_dml_async.py index aa79b58b..02064678 100644 --- a/tests/base/integration/tables/test_table_dml_async.py +++ b/tests/base/integration/tables/test_table_dml_async.py @@ -125,11 +125,13 @@ async def test_table_insert_one_find_one_async( assert fo_fil_ann_row is not None assert (fo_fil_ann_row["p_text"], fo_fil_ann_row["p_int"]) == ("pB", 100) # just regular sort - fo_unf_srt_row = await async_empty_table_composite.find_one( - sort={"p_int": SortMode.DESCENDING} - ) - assert fo_unf_srt_row is not None - assert (fo_unf_srt_row["p_text"], fo_unf_srt_row["p_int"]) == ("pB", 100) + if False: + # TODO: reinstate this part on Astra/nonAstra once patch in docker image and Astra prod + fo_unf_srt_row = await async_empty_table_composite.find_one( + sort={"p_int": SortMode.DESCENDING} + ) + assert fo_unf_srt_row is not None + assert (fo_unf_srt_row["p_text"], fo_unf_srt_row["p_int"]) == ("pB", 100) # regular sort, filtered fo_fil_srt_row = await async_empty_table_composite.find_one( filter={"p_text": "pA"}, @@ -629,14 +631,16 @@ async def test_table_find_async( # (nonvector) sorting await async_empty_table_all_returns.insert_many(INSMANY_AR_ROWS) # sorting as per clustering column - srows_in_part = await async_empty_table_all_returns.find( - filter={"p_ascii": "A", "p_bigint": 100}, - sort={"p_int": SortMode.DESCENDING}, - limit=INSMANY_AR_ROW_HALFN + 1, - ).to_list() - assert len(srows_in_part) == INSMANY_AR_ROW_HALFN - srows_in_part_pints = [row["p_int"] for row in srows_in_part] - assert sorted(srows_in_part_pints) == srows_in_part_pints[::-1] + if False: + # TODO: reinstate this part on Astra/nonAstra once patch in docker image and Astra prod + srows_in_part = await async_empty_table_all_returns.find( + filter={"p_ascii": "A", "p_bigint": 100}, + sort={"p_int": SortMode.DESCENDING}, + limit=INSMANY_AR_ROW_HALFN + 1, + ).to_list() + assert len(srows_in_part) == INSMANY_AR_ROW_HALFN + srows_in_part_pints = [row["p_int"] for row in srows_in_part] + assert sorted(srows_in_part_pints) == srows_in_part_pints[::-1] # sorting by any regular column srows_anycol = await async_empty_table_all_returns.find( filter={"p_ascii": "A", "p_bigint": 100}, diff --git a/tests/base/integration/tables/test_table_dml_sync.py b/tests/base/integration/tables/test_table_dml_sync.py index 437937e1..e873dca1 100644 --- a/tests/base/integration/tables/test_table_dml_sync.py +++ b/tests/base/integration/tables/test_table_dml_sync.py @@ -125,11 +125,13 @@ def test_table_insert_one_find_one_sync( assert fo_fil_ann_row is not None assert (fo_fil_ann_row["p_text"], fo_fil_ann_row["p_int"]) == ("pB", 100) # just regular sort - fo_unf_srt_row = sync_empty_table_composite.find_one( - sort={"p_int": SortMode.DESCENDING} - ) - assert fo_unf_srt_row is not None - assert (fo_unf_srt_row["p_text"], fo_unf_srt_row["p_int"]) == ("pB", 100) + if False: + # TODO: reinstate this part on Astra/nonAstra once patch in docker image and Astra prod + fo_unf_srt_row = sync_empty_table_composite.find_one( + sort={"p_int": SortMode.DESCENDING} + ) + assert fo_unf_srt_row is not None + assert (fo_unf_srt_row["p_text"], fo_unf_srt_row["p_int"]) == ("pB", 100) # regular sort, filtered fo_fil_srt_row = sync_empty_table_composite.find_one( filter={"p_text": "pA"}, @@ -615,14 +617,16 @@ def test_table_find_sync( # (nonvector) sorting sync_empty_table_all_returns.insert_many(INSMANY_AR_ROWS) # sorting as per clustering column - srows_in_part = sync_empty_table_all_returns.find( - filter={"p_ascii": "A", "p_bigint": 100}, - sort={"p_int": SortMode.DESCENDING}, - limit=INSMANY_AR_ROW_HALFN + 1, - ).to_list() - assert len(srows_in_part) == INSMANY_AR_ROW_HALFN - srows_in_part_pints = [row["p_int"] for row in srows_in_part] - assert sorted(srows_in_part_pints) == srows_in_part_pints[::-1] + if False: + # TODO: reinstate this part on Astra/nonAstra once patch in docker image and Astra prod + srows_in_part = sync_empty_table_all_returns.find( + filter={"p_ascii": "A", "p_bigint": 100}, + sort={"p_int": SortMode.DESCENDING}, + limit=INSMANY_AR_ROW_HALFN + 1, + ).to_list() + assert len(srows_in_part) == INSMANY_AR_ROW_HALFN + srows_in_part_pints = [row["p_int"] for row in srows_in_part] + assert sorted(srows_in_part_pints) == srows_in_part_pints[::-1] # sorting by any regular column srows_anycol = sync_empty_table_all_returns.find( filter={"p_ascii": "A", "p_bigint": 100}, diff --git a/tests/base/table_structure_assets.py b/tests/base/table_structure_assets.py index fc397b64..67c3c55a 100644 --- a/tests/base/table_structure_assets.py +++ b/tests/base/table_structure_assets.py @@ -15,7 +15,6 @@ from __future__ import annotations from astrapy.constants import SortMode - from astrapy.info import ( CreateTableDefinition, TableKeyValuedColumnTypeDescriptor, diff --git a/tests/base/unit/test_imports.py b/tests/base/unit/test_imports.py index 625d8067..a2061901 100644 --- a/tests/base/unit/test_imports.py +++ b/tests/base/unit/test_imports.py @@ -59,10 +59,12 @@ def test_imports() -> None: AstraDBDatabaseAdmin, AsyncCollection, AsyncDatabase, + AsyncTable, Collection, DataAPIClient, DataAPIDatabaseAdmin, Database, + Table, ) from astrapy.admin import ( # noqa: F401 ParsedAPIEndpoint, @@ -75,6 +77,13 @@ def test_imports() -> None: SerdesOptions, TimeoutOptions, ) + from astrapy.authentication import ( # noqa: F401 + AWSEmbeddingHeadersProvider, + EmbeddingAPIKeyHeaderProvider, + EmbeddingHeadersProvider, + StaticTokenProvider, + UsernamePasswordTokenProvider, + ) from astrapy.constants import ( # noqa: F401 DefaultIdType, Environment, @@ -87,6 +96,15 @@ def test_imports() -> None: CollectionFindCursor, FindCursorState, ) + from astrapy.data_types import ( # noqa: F401 + DataAPIDate, + DataAPIDuration, + DataAPIMap, + DataAPISet, + DataAPITime, + DataAPITimestamp, + DataAPIVector, + ) from astrapy.exceptions import ( # noqa: F401 CollectionDeleteManyException, CollectionInsertManyException, @@ -123,6 +141,10 @@ def test_imports() -> None: uuid8, ) from astrapy.info import ( # noqa: F401 + AlterTableAddColumns, + AlterTableAddVectorize, + AlterTableDropColumns, + AlterTableDropVectorize, AstraDBAdminDatabaseInfo, AstraDBDatabaseInfo, CollectionDefaultIDOptions, @@ -130,12 +152,34 @@ def test_imports() -> None: CollectionDescriptor, CollectionInfo, CollectionVectorOptions, + ColumnType, + CreateTableDefinition, EmbeddingProvider, EmbeddingProviderAuthentication, EmbeddingProviderModel, EmbeddingProviderParameter, EmbeddingProviderToken, FindEmbeddingProvidersResult, + ListTableDefinition, + ListTableDescriptor, + TableAPIIndexSupportDescriptor, + TableAPISupportDescriptor, + TableBaseIndexDefinition, + TableIndexDefinition, + TableIndexDescriptor, + TableIndexOptions, + TableInfo, + TableKeyValuedColumnType, + TableKeyValuedColumnTypeDescriptor, + TablePrimaryKeyDescriptor, + TableScalarColumnTypeDescriptor, + TableUnsupportedColumnTypeDescriptor, + TableUnsupportedIndexDefinition, + TableValuedColumnType, + TableValuedColumnTypeDescriptor, + TableVectorColumnTypeDescriptor, + TableVectorIndexDefinition, + TableVectorIndexOptions, VectorServiceOptions, ) from astrapy.results import ( # noqa: F401 diff --git a/tests/conftest.py b/tests/conftest.py index da4c5e76..25f59158 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -20,14 +20,13 @@ import functools import warnings -from typing import Any, Awaitable, Callable, TypedDict +from typing import Any, Awaitable, Callable, Iterable, TypedDict import pytest from deprecation import UnsupportedWarning -from astrapy import DataAPIClient +from astrapy import AsyncDatabase, DataAPIClient, Database from astrapy.admin import parse_api_endpoint -from astrapy.api_options import APIOptions from astrapy.authentication import TokenProvider from astrapy.constants import Environment from astrapy.settings.defaults import DEFAULT_ASTRA_DB_KEYSPACE @@ -39,7 +38,6 @@ ASTRA_DB_APPLICATION_TOKEN, ASTRA_DB_KEYSPACE, ASTRA_DB_TOKEN_PROVIDER, - DO_IDIOMATIC_ADMIN_TESTS, DOCKER_COMPOSE_LOCAL_DATA_API, HEADER_EMBEDDING_API_KEY_OPENAI, IS_ASTRA_DB, @@ -197,12 +195,7 @@ def client( data_api_credentials_info: DataAPICredentialsInfo, ) -> Iterable[DataAPIClient]: env = data_api_credentials_info["environment"] - client = DataAPIClient( - environment=env, - api_options=APIOptions( - database_additional_headers={"Feature-Flag-Tables": "true"}, - ), - ) + client = DataAPIClient(environment=env) yield client @@ -243,7 +236,6 @@ def async_database( "SECONDARY_KEYSPACE", "ADMIN_ENV_LIST", "ADMIN_ENV_VARIABLE_MAP", - "DO_IDIOMATIC_ADMIN_TESTS", "ASTRA_DB_TOKEN_PROVIDER", "LOCAL_DATA_API_TOKEN_PROVIDER", ] diff --git a/tests/dse_compose_manual/docker-compose.yml b/tests/dse_compose_manual/docker-compose.yml index 116dc10c..21a8117f 100644 --- a/tests/dse_compose_manual/docker-compose.yml +++ b/tests/dse_compose_manual/docker-compose.yml @@ -43,6 +43,7 @@ services: - STARGATE_DATA_STORE_SAI_ENABLED=true - STARGATE_DATA_STORE_VECTOR_SEARCH_ENABLED=true - STARGATE_JSONAPI_OPERATIONS_VECTORIZE_ENABLED=true + - STARGATE_FEATURE_FLAGS_TABLES=true - STARGATE_DATA_STORE_IGNORE_BRIDGE=true - STARGATE_JSONAPI_OPERATIONS_DATABASE_CONFIG_CASSANDRA_END_POINTS=coordinator - QUARKUS_HTTP_ACCESS_LOG_ENABLED=FALSE diff --git a/tests/env_templates/env.astra.admin.template b/tests/env_templates/env.astra.admin.template index fe01bf00..162f3dd0 100644 --- a/tests/env_templates/env.astra.admin.template +++ b/tests/env_templates/env.astra.admin.template @@ -1,9 +1,6 @@ -################################## -# FOR THE (idiomatic) ADMIN TESTS: -################################## - -# General setting -export DO_IDIOMATIC_ADMIN_TESTS="1" +######################## +# FOR THE ADMIN TESTS: # +######################## # PROD settings export PROD_ADMIN_TEST_ASTRA_DB_APPLICATION_TOKEN="AstraCS:..." diff --git a/tests/env_templates/env.astra.template b/tests/env_templates/env.astra.template index 91396587..06c9f8f9 100644 --- a/tests/env_templates/env.astra.template +++ b/tests/env_templates/env.astra.template @@ -1,6 +1,6 @@ -######################## -# FOR THE REGULAR TESTS: -######################## +############################## +# FOR THE TESTS ON ASTRA DB: # +############################## export ASTRA_DB_APPLICATION_TOKEN="AstraCS:..." diff --git a/tests/env_templates/env.local.template b/tests/env_templates/env.local.template index d3a61c6b..4af4cb70 100644 --- a/tests/env_templates/env.local.template +++ b/tests/env_templates/env.local.template @@ -1,6 +1,6 @@ -######################## -# FOR THE REGULAR TESTS: -######################## +######################################## +# FOR THE TESTS WITH A LOCAL DATA API: # +######################################## # Authentication. Choose either: export LOCAL_DATA_API_APPLICATION_TOKEN="Cassandra:Y2Fzc2FuZHJh:Y2Fzc2FuZHJh" diff --git a/tests/env_templates/env.testcontainers.template b/tests/env_templates/env.testcontainers.template new file mode 100644 index 00000000..3bd2c289 --- /dev/null +++ b/tests/env_templates/env.testcontainers.template @@ -0,0 +1,5 @@ +###################################### +# FOR THE TESTS WITH TESTCONTAINERS: # +###################################### + +export DOCKER_COMPOSE_LOCAL_DATA_API="yes" diff --git a/tests/env_templates/env.vectorize-minimal.template b/tests/env_templates/env.vectorize-minimal.template index 7deb7e54..e7aeebfe 100644 --- a/tests/env_templates/env.vectorize-minimal.template +++ b/tests/env_templates/env.vectorize-minimal.template @@ -1,5 +1,5 @@ -################################## -# FOR THE MINIMAL VECTORIZE TESTS: -################################## +#################################### +# FOR THE VECTORIZE TESTS IN BASE: # +#################################### export HEADER_EMBEDDING_API_KEY_OPENAI="..." diff --git a/tests/env_templates/env.vectorize.template b/tests/env_templates/env.vectorize.template index 3367bf65..2572d689 100644 --- a/tests/env_templates/env.vectorize.template +++ b/tests/env_templates/env.vectorize.template @@ -1,6 +1,6 @@ -############################### -# FOR THE FULL VECTORIZE TESTS: -############################### +##################################### +# FOR THE EXTENDED VECTORIZE TESTS: # +##################################### export HEADER_EMBEDDING_API_KEY_HUGGINGFACE="..." diff --git a/tests/hcd_compose/docker-compose.yml b/tests/hcd_compose/docker-compose.yml index 23fa1685..5651b042 100644 --- a/tests/hcd_compose/docker-compose.yml +++ b/tests/hcd_compose/docker-compose.yml @@ -39,6 +39,7 @@ services: - QUARKUS_HTTP_ACCESS_LOG_ENABLED=FALSE - QUARKUS_LOG_LEVEL=INFO - STARGATE_JSONAPI_OPERATIONS_VECTORIZE_ENABLED=true + - STARGATE_FEATURE_FLAGS_TABLES=true - JAVA_OPTS_APPEND=-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager healthcheck: test: curl -f http://localhost:8181/stargate/health || exit 1 diff --git a/tests/preprocess_env.py b/tests/preprocess_env.py index ea78bf86..bda9ba2b 100644 --- a/tests/preprocess_env.py +++ b/tests/preprocess_env.py @@ -52,7 +52,7 @@ ASTRA_DB_TOKEN_PROVIDER: TokenProvider | None = None LOCAL_DATA_API_TOKEN_PROVIDER: TokenProvider | None = None -# idiomatic-related settings +# basic settings about which DB/API to use if "LOCAL_DATA_API_ENDPOINT" in os.environ: IS_ASTRA_DB = False DOCKER_COMPOSE_LOCAL_DATA_API = False @@ -145,17 +145,7 @@ def docker_compose_command(self) -> list[str]: is_docker_compose_started = True -# Idomatic admin test flag -DO_IDIOMATIC_ADMIN_TESTS: bool -if "DO_IDIOMATIC_ADMIN_TESTS" in os.environ: - _do_idiomatic_admin_tests = os.environ["DO_IDIOMATIC_ADMIN_TESTS"] - if _do_idiomatic_admin_tests.strip(): - DO_IDIOMATIC_ADMIN_TESTS = int(_do_idiomatic_admin_tests) != 0 - else: - DO_IDIOMATIC_ADMIN_TESTS = False -else: - DO_IDIOMATIC_ADMIN_TESTS = False - +# admin-test values for Astra DB ADMIN_ENV_LIST = ["prod", "dev", "test"] ADMIN_ENV_VARIABLE_MAP = { admin_env: { diff --git a/tests/vectorize/conftest.py b/tests/vectorize/conftest.py index 65bc492d..20cbf7ae 100644 --- a/tests/vectorize/conftest.py +++ b/tests/vectorize/conftest.py @@ -14,7 +14,10 @@ from __future__ import annotations - from ..conftest import ( IS_ASTRA_DB, ) + +__all__ = [ + "IS_ASTRA_DB", +] From ab908b8f687c4dff1f15dcb7b1062be2e2b42b93 Mon Sep 17 00:00:00 2001 From: Stefano Lottini Date: Wed, 18 Dec 2024 00:36:51 +0100 Subject: [PATCH 3/6] adjust for vectorize coverage in workflows --- .github/workflows/hcd.yaml | 1 + .github/workflows/main.yml | 1 + .github/workflows/unit.yaml | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/workflows/hcd.yaml b/.github/workflows/hcd.yaml index 7b1218a9..ad9a4c64 100644 --- a/.github/workflows/hcd.yaml +++ b/.github/workflows/hcd.yaml @@ -11,6 +11,7 @@ on: jobs: test: env: + HEADER_EMBEDDING_API_KEY_OPENAI: ${{ secrets.ASTRA_DB_SECONDARY_KEYSPACE }} # hardcoding the target DB DOCKER_COMPOSE_LOCAL_DATA_API: "yes" runs-on: ubuntu-latest diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1984663c..62ea197e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,6 +16,7 @@ jobs: ASTRA_DB_API_ENDPOINT: ${{ secrets.ASTRA_DB_API_ENDPOINT }} ASTRA_DB_KEYSPACE: ${{ secrets.ASTRA_DB_KEYSPACE }} ASTRA_DB_SECONDARY_KEYSPACE: ${{ secrets.ASTRA_DB_SECONDARY_KEYSPACE }} + HEADER_EMBEDDING_API_KEY_OPENAI: ${{ secrets.ASTRA_DB_SECONDARY_KEYSPACE }} runs-on: ubuntu-latest steps: diff --git a/.github/workflows/unit.yaml b/.github/workflows/unit.yaml index ffd36712..463441e8 100644 --- a/.github/workflows/unit.yaml +++ b/.github/workflows/unit.yaml @@ -20,6 +20,7 @@ jobs: strategy: matrix: python-version: + - "3.8" - "3.9" - "3.10" - "3.11" From 0ce8a2443dc290558ad4e7211b27930e8b9edb78 Mon Sep 17 00:00:00 2001 From: Stefano Lottini Date: Wed, 18 Dec 2024 00:48:10 +0100 Subject: [PATCH 4/6] fix silly mistake in bringing env vars to flows --- .github/workflows/hcd.yaml | 2 +- .github/workflows/main.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/hcd.yaml b/.github/workflows/hcd.yaml index ad9a4c64..f0100d88 100644 --- a/.github/workflows/hcd.yaml +++ b/.github/workflows/hcd.yaml @@ -11,7 +11,7 @@ on: jobs: test: env: - HEADER_EMBEDDING_API_KEY_OPENAI: ${{ secrets.ASTRA_DB_SECONDARY_KEYSPACE }} + HEADER_EMBEDDING_API_KEY_OPENAI: ${{ secrets.HEADER_EMBEDDING_API_KEY_OPENAI }} # hardcoding the target DB DOCKER_COMPOSE_LOCAL_DATA_API: "yes" runs-on: ubuntu-latest diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 62ea197e..44e2b641 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,7 +16,7 @@ jobs: ASTRA_DB_API_ENDPOINT: ${{ secrets.ASTRA_DB_API_ENDPOINT }} ASTRA_DB_KEYSPACE: ${{ secrets.ASTRA_DB_KEYSPACE }} ASTRA_DB_SECONDARY_KEYSPACE: ${{ secrets.ASTRA_DB_SECONDARY_KEYSPACE }} - HEADER_EMBEDDING_API_KEY_OPENAI: ${{ secrets.ASTRA_DB_SECONDARY_KEYSPACE }} + HEADER_EMBEDDING_API_KEY_OPENAI: ${{ secrets.HEADER_EMBEDDING_API_KEY_OPENAI }} runs-on: ubuntu-latest steps: From 0b67aa958ecabe00864c76d4cb834fe8112d3465 Mon Sep 17 00:00:00 2001 From: Stefano Lottini Date: Thu, 19 Dec 2024 15:33:36 +0100 Subject: [PATCH 5/6] Update README.md Co-authored-by: Christophe Bornet --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 66cb0fe9..1c4f20a5 100644 --- a/README.md +++ b/README.md @@ -415,7 +415,7 @@ Tests are grouped in: - "vectorize", extensively running a base workload on all provider/integration choices; - "admin", doing a whole sweep of admin operations. Very slow on Astra DB. -Astrapy's CI only runs "base". The other are to be checked manually when it's needed. +Astrapy's CI only runs "base". The others are to be checked manually when it's needed. Tests can be run on three types of Data API _targets_ (with slight differences in what is applicable): From 70add1db640b052d3c53f8d7a20542f25a32d12b Mon Sep 17 00:00:00 2001 From: Stefano Lottini Date: Thu, 19 Dec 2024 15:47:09 +0100 Subject: [PATCH 6/6] ruff exemption rule in test_imports.py made module-level --- tests/base/unit/test_imports.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/tests/base/unit/test_imports.py b/tests/base/unit/test_imports.py index a2061901..4fe95a99 100644 --- a/tests/base/unit/test_imports.py +++ b/tests/base/unit/test_imports.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# ruff: noqa: F401 + from __future__ import annotations import pytest @@ -54,7 +56,7 @@ def test_namespace() -> None: @pytest.mark.describe("test imports") def test_imports() -> None: - from astrapy import ( # noqa: F401 + from astrapy import ( AstraDBAdmin, AstraDBDatabaseAdmin, AsyncCollection, @@ -66,37 +68,37 @@ def test_imports() -> None: Database, Table, ) - from astrapy.admin import ( # noqa: F401 + from astrapy.admin import ( ParsedAPIEndpoint, parse_api_endpoint, ) - from astrapy.api_options import ( # noqa: F401 + from astrapy.api_options import ( APIOptions, DataAPIURLOptions, DevOpsAPIURLOptions, SerdesOptions, TimeoutOptions, ) - from astrapy.authentication import ( # noqa: F401 + from astrapy.authentication import ( AWSEmbeddingHeadersProvider, EmbeddingAPIKeyHeaderProvider, EmbeddingHeadersProvider, StaticTokenProvider, UsernamePasswordTokenProvider, ) - from astrapy.constants import ( # noqa: F401 + from astrapy.constants import ( DefaultIdType, Environment, ReturnDocument, SortMode, VectorMetric, ) - from astrapy.cursors import ( # noqa: F401 + from astrapy.cursors import ( AsyncCollectionFindCursor, CollectionFindCursor, FindCursorState, ) - from astrapy.data_types import ( # noqa: F401 + from astrapy.data_types import ( DataAPIDate, DataAPIDuration, DataAPIMap, @@ -105,7 +107,7 @@ def test_imports() -> None: DataAPITimestamp, DataAPIVector, ) - from astrapy.exceptions import ( # noqa: F401 + from astrapy.exceptions import ( CollectionDeleteManyException, CollectionInsertManyException, CollectionUpdateManyException, @@ -129,7 +131,7 @@ def test_imports() -> None: UnexpectedDataAPIResponseException, UnexpectedDevOpsAPIResponseException, ) - from astrapy.ids import ( # noqa: F401 + from astrapy.ids import ( UUID, ObjectId, uuid1, @@ -140,7 +142,7 @@ def test_imports() -> None: uuid7, uuid8, ) - from astrapy.info import ( # noqa: F401 + from astrapy.info import ( AlterTableAddColumns, AlterTableAddVectorize, AlterTableDropColumns, @@ -182,7 +184,7 @@ def test_imports() -> None: TableVectorIndexOptions, VectorServiceOptions, ) - from astrapy.results import ( # noqa: F401 + from astrapy.results import ( CollectionDeleteResult, CollectionInsertManyResult, CollectionInsertOneResult,