Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
test_client.py281 linesDownload Raw Back to test
1import asyncio
2from typing import Any, Callable, Generator, cast, Dict, Tuple
3from unittest.mock import MagicMock, patch
4import chromadb
5from chromadb.config import Settings, System
6from chromadb.api import ClientAPI
7import chromadb.server.fastapi
8from chromadb.api.fastapi import FastAPI
9import pytest
10import tempfile
11import os
12
13
14@pytest.fixture
15def ephemeral_api() -> Generator[ClientAPI, None, None]:
16    if os.environ.get("CHROMA_INTEGRATION_TEST_ONLY"):
17        pytest.skip("Integration test only")
18    client = chromadb.EphemeralClient()
19    yield client
20    client.clear_system_cache()
21
22
23@pytest.fixture
24def persistent_api() -> Generator[ClientAPI, None, None]:
25    if os.environ.get("CHROMA_INTEGRATION_TEST_ONLY"):
26        pytest.skip("Integration test only")
27    client = chromadb.PersistentClient(
28        path=tempfile.gettempdir() + "/test_server",
29    )
30    yield client
31    client.clear_system_cache()
32
33
34HttpAPIFactory = Callable[..., ClientAPI]
35
36
37@pytest.fixture(params=["sync_client", "async_client"])
38def http_api_factory(
39    request: pytest.FixtureRequest,
40) -> Generator[HttpAPIFactory, None, None]:
41    if request.param == "sync_client":
42        with patch("chromadb.api.client.Client._validate_tenant_database"):
43            with patch("chromadb.api.client.Client.get_user_identity"):
44                yield chromadb.HttpClient
45    else:
46        with patch("chromadb.api.async_client.AsyncClient._validate_tenant_database"):
47            with patch("chromadb.api.async_client.AsyncClient.get_user_identity"):
48
49                def factory(*args: Any, **kwargs: Any) -> Any:
50                    cls = asyncio.get_event_loop().run_until_complete(
51                        chromadb.AsyncHttpClient(*args, **kwargs)
52                    )
53                    return cls
54
55                yield cast(HttpAPIFactory, factory)
56
57
58@pytest.fixture()
59def http_api(http_api_factory: HttpAPIFactory) -> Generator[ClientAPI, None, None]:
60    if os.environ.get("CHROMA_SERVER_HTTP_PORT") is not None:
61        port = int(os.environ.get("CHROMA_SERVER_HTTP_PORT"))  # type: ignore
62        client = http_api_factory(port=port)
63    else:
64        client = http_api_factory()
65    yield client
66    client.clear_system_cache()
67
68
69def test_ephemeral_client(ephemeral_api: ClientAPI) -> None:
70    settings = ephemeral_api.get_settings()
71    assert settings.is_persistent is False
72
73
74def test_persistent_client(persistent_api: ClientAPI) -> None:
75    settings = persistent_api.get_settings()
76    assert settings.is_persistent is True
77
78
79def test_http_client(http_api: ClientAPI) -> None:
80    settings = http_api.get_settings()
81    assert (
82        settings.chroma_api_impl == "chromadb.api.fastapi.FastAPI"
83        or settings.chroma_api_impl == "chromadb.api.async_fastapi.AsyncFastAPI"
84    )
85
86
87def test_http_client_with_inconsistent_host_settings(
88    http_api_factory: HttpAPIFactory,
89) -> None:
90    try:
91        http_api_factory(settings=Settings(chroma_server_host="127.0.0.1"))
92    except ValueError as e:
93        assert (
94            str(e)
95            == "Chroma server host provided in settings[127.0.0.1] is different to the one provided in HttpClient: [localhost]"
96        )
97
98
99def test_http_client_with_inconsistent_port_settings(
100    http_api_factory: HttpAPIFactory,
101) -> None:
102    try:
103        http_api_factory(
104            port=8002,
105            settings=Settings(
106                chroma_server_http_port=8001,
107            ),
108        )
109    except ValueError as e:
110        assert (
111            str(e)
112            == "Chroma server http port provided in settings[8001] is different to the one provided in HttpClient: [8002]"
113        )
114
115
116def make_sync_client_factory() -> Tuple[Callable[..., Any], Dict[str, Any]]:
117    captured: Dict[str, Any] = {}
118
119    # takes any positional args to match httpx.Client
120    def factory(*_: Any, **kwargs: Any) -> Any:
121        captured.update(kwargs)
122        session = MagicMock()
123        session.headers = {}
124        return session
125
126    return factory, captured
127
128
129def test_fastapi_uses_http_limits_from_settings() -> None:
130    settings = Settings(
131        chroma_api_impl="chromadb.api.fastapi.FastAPI",
132        chroma_server_host="localhost",
133        chroma_server_http_port=9000,
134        chroma_server_ssl_verify=True,
135        chroma_http_keepalive_secs=12.5,
136        chroma_http_max_connections=64,
137        chroma_http_max_keepalive_connections=16,
138    )
139    system = System(settings)
140
141    factory, captured = make_sync_client_factory()
142
143    with patch.object(FastAPI, "require", side_effect=[MagicMock(), MagicMock()]):
144        with patch("chromadb.api.fastapi.httpx.Client", side_effect=factory):
145            api = FastAPI(system)
146
147    api.stop()
148    limits = captured["limits"]
149    assert limits.keepalive_expiry == 12.5
150    assert limits.max_connections == 64
151    assert limits.max_keepalive_connections == 16
152    assert captured["timeout"] is None
153    assert captured["verify"] is True
154
155
156def test_persistent_client_close() -> None:
157    """Test that close() properly releases resources in PersistentClient."""
158    if os.environ.get("CHROMA_INTEGRATION_TEST_ONLY"):
159        pytest.skip("Integration test only")
160
161    with tempfile.TemporaryDirectory() as tmpdir:
162        # Create a client, add some data, and close it
163        client = chromadb.PersistentClient(path=tmpdir)
164        collection = client.create_collection("test_collection")
165        collection.add(
166            ids=["id1", "id2"],
167            documents=["doc1", "doc2"],
168            metadatas=[{"key": "value1"}, {"key": "value2"}],
169        )
170
171        # Save a reference to the system before close() removes it from the cache
172        system = client._system
173
174        # Close the client
175        client.close()
176
177        # Verify the system is stopped
178        assert system._running is False
179
180        # Create a new client with the same path to verify data was persisted
181        client2 = chromadb.PersistentClient(path=tmpdir)
182        collection2 = client2.get_collection("test_collection")
183        results = collection2.get()
184        assert len(results["ids"]) == 2
185        assert "id1" in results["ids"]
186        assert "id2" in results["ids"]
187
188        client2.close()
189        client.clear_system_cache()
190        client2.clear_system_cache()
191
192
193def test_persistent_client_context_manager() -> None:
194    """Test that PersistentClient works as a context manager."""
195    if os.environ.get("CHROMA_INTEGRATION_TEST_ONLY"):
196        pytest.skip("Integration test only")
197
198    with tempfile.TemporaryDirectory() as tmpdir:
199        # Use client as context manager
200        with chromadb.PersistentClient(path=tmpdir) as client:
201            # Save a reference to the system before close() removes it from the cache
202            system = client._system
203            collection = client.create_collection("test_collection")
204            collection.add(
205                ids=["id1", "id2"],
206                documents=["doc1", "doc2"],
207                metadatas=[{"key": "value1"}, {"key": "value2"}],
208            )
209
210        # Verify the system is stopped after context exit
211        assert system._running is False
212
213        # Verify data was persisted
214        with chromadb.PersistentClient(path=tmpdir) as client2:
215            collection2 = client2.get_collection("test_collection")
216            results = collection2.get()
217            assert len(results["ids"]) == 2
218
219        client.clear_system_cache()
220        client2.clear_system_cache()
221
222
223def test_ephemeral_client_close() -> None:
224    """Test that close() works with EphemeralClient."""
225    if os.environ.get("CHROMA_INTEGRATION_TEST_ONLY"):
226        pytest.skip("Integration test only")
227
228    client = chromadb.EphemeralClient()
229    # Save a reference to the system before close() removes it from the cache
230    system = client._system
231    collection = client.create_collection("test_collection")
232    collection.add(ids=["id1"], documents=["doc1"])
233
234    # Close the client
235    client.close()
236
237    # Verify the system is stopped
238    assert system._running is False
239
240    client.clear_system_cache()
241
242
243def test_ephemeral_client_context_manager() -> None:
244    """Test that EphemeralClient works as a context manager."""
245    if os.environ.get("CHROMA_INTEGRATION_TEST_ONLY"):
246        pytest.skip("Integration test only")
247
248    with chromadb.EphemeralClient() as client:
249        # Save a reference to the system before close() removes it from the cache
250        system = client._system
251        collection = client.create_collection("test_collection")
252        collection.add(ids=["id1"], documents=["doc1"])
253        assert system._running is True
254
255    # Verify the system is stopped after context exit
256    assert system._running is False
257
258    client.clear_system_cache()
259
260
261def test_client_close_idempotent() -> None:
262    """Test that calling close() multiple times is a safe no-op."""
263    if os.environ.get("CHROMA_INTEGRATION_TEST_ONLY"):
264        pytest.skip("Integration test only")
265
266    with tempfile.TemporaryDirectory() as tmpdir:
267        client = chromadb.PersistentClient(path=tmpdir)
268        collection = client.create_collection("test_collection")
269        collection.add(ids=["id1"], documents=["doc1"])
270
271        # First close should work normally
272        client.close()
273
274        # Second close should be a no-op, not raise KeyError
275        client.close()
276
277        # Third close should also be safe
278        client.close()
279
280        client.clear_system_cache()
281 
codekingpro/portable-devtools · Team Ai