Spaces:
Sleeping
Sleeping
import logging | |
from typing import Dict, Optional, Tuple | |
import pytest | |
from chromadb.api import AdminAPI | |
import chromadb.api.types as types | |
from chromadb.api.client import AdminClient, Client | |
from chromadb.config import DEFAULT_DATABASE, DEFAULT_TENANT | |
from chromadb.test.property.test_collections import CollectionStateMachine | |
from hypothesis.stateful import ( | |
Bundle, | |
rule, | |
initialize, | |
multiple, | |
run_state_machine_as_test, | |
MultipleResults, | |
) | |
import chromadb.test.property.strategies as strategies | |
class TenantDatabaseCollectionStateMachine(CollectionStateMachine): | |
"""A collection state machine test that includes tenant and database information, | |
and switches between them.""" | |
tenants: Bundle[str] | |
databases: Bundle[Tuple[str, str]] # database to tenant it belongs to | |
tenant_to_database_to_model: Dict[ | |
str, Dict[str, Dict[str, Optional[types.CollectionMetadata]]] | |
] | |
admin_client: AdminAPI | |
curr_tenant: str | |
curr_database: str | |
tenants = Bundle("tenants") | |
databases = Bundle("databases") | |
def __init__(self, client: Client): | |
super().__init__(client) | |
self.api = client | |
self.admin_client = AdminClient.from_system(client._system) | |
def initialize(self) -> None: | |
self.api.reset() | |
self.tenant_to_database_to_model = {} | |
self.curr_tenant = DEFAULT_TENANT | |
self.curr_database = DEFAULT_DATABASE | |
self.api.set_tenant(DEFAULT_TENANT, DEFAULT_DATABASE) | |
self.tenant_to_database_to_model[self.curr_tenant] = {} | |
self.tenant_to_database_to_model[self.curr_tenant][self.curr_database] = {} | |
def create_tenant(self, name: str) -> MultipleResults[str]: | |
# Check if tenant already exists | |
if name in self.tenant_to_database_to_model: | |
with pytest.raises(Exception): | |
self.admin_client.create_tenant(name) | |
return multiple() | |
self.admin_client.create_tenant(name) | |
# When we create a tenant, create a default database for it just for testing | |
# since the state machine could call collection operations before creating a | |
# database | |
self.admin_client.create_database(DEFAULT_DATABASE, tenant=name) | |
self.tenant_to_database_to_model[name] = {} | |
self.tenant_to_database_to_model[name][DEFAULT_DATABASE] = {} | |
return multiple(name) | |
def create_database(self, name: str) -> MultipleResults[Tuple[str, str]]: | |
# If database already exists in current tenant, raise an error | |
if name in self.tenant_to_database_to_model[self.curr_tenant]: | |
with pytest.raises(Exception): | |
self.admin_client.create_database(name, tenant=self.curr_tenant) | |
return multiple() | |
self.admin_client.create_database(name, tenant=self.curr_tenant) | |
self.tenant_to_database_to_model[self.curr_tenant][name] = {} | |
return multiple((name, self.curr_tenant)) | |
def set_database_and_tenant(self, database: Tuple[str, str]) -> None: | |
# Get a database and switch to the database and the tenant it belongs to | |
database_name = database[0] | |
tenant_name = database[1] | |
self.api.set_tenant(tenant_name, database_name) | |
self.curr_database = database_name | |
self.curr_tenant = tenant_name | |
def set_tenant(self, tenant: str) -> None: | |
self.api.set_tenant(tenant, DEFAULT_DATABASE) | |
self.curr_tenant = tenant | |
self.curr_database = DEFAULT_DATABASE | |
def model(self) -> Dict[str, Optional[types.CollectionMetadata]]: | |
return self.tenant_to_database_to_model[self.curr_tenant][self.curr_database] | |
def test_collections(caplog: pytest.LogCaptureFixture, client: Client) -> None: | |
caplog.set_level(logging.ERROR) | |
run_state_machine_as_test(lambda: TenantDatabaseCollectionStateMachine(client)) # type: ignore | |