From eedbeb06f534bb1418e09b47d62ae01b6803353f Mon Sep 17 00:00:00 2001 From: "Sharad." Date: Wed, 16 Sep 2026 22:47:55 +0530 Subject: [PATCH 1/2] fix(search): reject out-of-range limit and prefetch_limit before query search() passed limit=-1 or limit=10_000 straight into query_points. Guard both limit and prefetch_limit to 1-1000 and raise a descriptive ValueError before any embed or query work happens. Closes #10 --- src/tenantq/search.py | 5 +++++ tests/test_search.py | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/tenantq/search.py b/src/tenantq/search.py index fead624..c03e968 100644 --- a/src/tenantq/search.py +++ b/src/tenantq/search.py @@ -112,6 +112,11 @@ def search( ) query = str(query).strip() + if not 1 <= limit <= 1000: + raise ValueError(f"limit must be within 1-1000, got {limit!r}") + if not 1 <= prefetch_limit <= 1000: + raise ValueError(f"prefetch_limit must be within 1-1000, got {prefetch_limit!r}") + qfilter = build_filter(tenant_id, category, created_after, created_before) params = models.SearchParams(hnsw_ef=settings.hnsw.hnsw_ef) diff --git a/tests/test_search.py b/tests/test_search.py index ffe10a9..ebe6b4c 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -91,3 +91,24 @@ def test_build_filter_allows_wide_open_range(): from tenantq.search import build_filter f = build_filter("acme", created_after=100, created_before=200) assert f.must + + +@pytest.mark.parametrize("limit", [-1, 0, 10_000]) +def test_search_rejects_out_of_range_limit(settings, limit): + client = MagicMock() + embedder = MagicMock() + with pytest.raises(ValueError, match="limit must be within"): + search(client, settings, embedder, "dense query", tenant_id="acme", mode="dense", limit=limit) + client.query_points.assert_not_called() + + +@pytest.mark.parametrize("prefetch_limit", [-1, 0, 100_000]) +def test_search_rejects_out_of_range_prefetch_limit(settings, prefetch_limit): + client = MagicMock() + embedder = MagicMock() + with pytest.raises(ValueError, match="prefetch_limit must be within"): + search( + client, settings, embedder, "hybrid query", + tenant_id="acme", mode="hybrid", prefetch_limit=prefetch_limit, + ) + client.query_points.assert_not_called() From 9dfc1856257f18c39b863b3f4e10bef74e517aa6 Mon Sep 17 00:00:00 2001 From: "Sharad." Date: Fri, 18 Sep 2026 08:03:15 +0530 Subject: [PATCH 2/2] fix(search): raise prefetch_limit cap to 10000 to match documented range --- src/tenantq/search.py | 4 ++-- tests/test_search.py | 14 +++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/tenantq/search.py b/src/tenantq/search.py index c03e968..fd616d9 100644 --- a/src/tenantq/search.py +++ b/src/tenantq/search.py @@ -114,8 +114,8 @@ def search( if not 1 <= limit <= 1000: raise ValueError(f"limit must be within 1-1000, got {limit!r}") - if not 1 <= prefetch_limit <= 1000: - raise ValueError(f"prefetch_limit must be within 1-1000, got {prefetch_limit!r}") + if not 1 <= prefetch_limit <= 10000: + raise ValueError(f"prefetch_limit must be within 1-10000, got {prefetch_limit!r}") qfilter = build_filter(tenant_id, category, created_after, created_before) params = models.SearchParams(hnsw_ef=settings.hnsw.hnsw_ef) diff --git a/tests/test_search.py b/tests/test_search.py index ebe6b4c..8b8fa86 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -102,7 +102,7 @@ def test_search_rejects_out_of_range_limit(settings, limit): client.query_points.assert_not_called() -@pytest.mark.parametrize("prefetch_limit", [-1, 0, 100_000]) +@pytest.mark.parametrize("prefetch_limit", [-1, 0, 10_001]) def test_search_rejects_out_of_range_prefetch_limit(settings, prefetch_limit): client = MagicMock() embedder = MagicMock() @@ -112,3 +112,15 @@ def test_search_rejects_out_of_range_prefetch_limit(settings, prefetch_limit): tenant_id="acme", mode="hybrid", prefetch_limit=prefetch_limit, ) client.query_points.assert_not_called() + + +def test_search_accepts_max_prefetch_limit(settings): + client = MagicMock() + embedder = MagicMock() + client.query_points.return_value.points = [] + hits = search( + client, settings, embedder, "hybrid query", + tenant_id="acme", mode="hybrid", prefetch_limit=10_000, + ) + assert hits == [] + client.query_points.assert_called_once()