chore: import upstream snapshot with attribution
Integ / changes (push) Has been skipped
Pre-commit / pre-commit (push) Failing after 1s
CLI exit codes / changes (push) Has been skipped
Test (Install) / changes (push) Has been skipped
Test (Python) / changes (push) Has been skipped
Test (TypeScript) / changes (push) Has been skipped
CLI exit codes / cli-gate (push) Has been cancelled
Test (Install) / test-install-gate (push) Has been cancelled
Integ / integ-gate (push) Has been cancelled
Test (Python) / test-python-gate (push) Has been cancelled
Test (TypeScript) / test-typescript-gate (push) Has been cancelled
Test (Install) / python-minimal (3.12) (push) Has been cancelled
Test (Install) / python-minimal (3.11) (push) Has been cancelled
Test (Install) / python-extra (agno, mirage.agents.agno) (push) Has been cancelled
Test (Install) / python-extra (chroma, mirage.resource.chroma) (push) Has been cancelled
Test (Install) / python-extra (pdf, mirage.core.filetype.pdf) (push) Has been cancelled
Integ / integ (push) Has been cancelled
Integ / integ-database (push) Has been cancelled
Integ / integ-database-ts (push) Has been cancelled
Integ / integ-data (push) Has been cancelled
Integ / integ-ssh (push) Has been cancelled
Integ / integ-ssh-ts (push) Has been cancelled
Test (Python) / audit (push) Has been cancelled
Test (TypeScript) / test (push) Has been cancelled
Test (TypeScript) / python-fs-shim (push) Has been cancelled
CLI exit codes / Python CLI (push) Has been cancelled
CLI exit codes / TypeScript CLI (push) Has been cancelled
CLI exit codes / Cross-language snapshot interop (push) Has been cancelled
Test (Python) / test (push) Has been cancelled
Test (Python) / import-isolation (deepagents, openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Python) / import-isolation (deepagents, pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Integ / integ-ts (push) Has been cancelled
Integ / integ-fuse (push) Has been cancelled
Test (Install) / python-extra (databricks, mirage.resource.databricks_volume) (push) Has been cancelled
Test (Install) / python-extra (deepagents, mirage.agents.langchain) (push) Has been cancelled
Test (Install) / python-extra (email, mirage.resource.email) (push) Has been cancelled
Test (Install) / python-extra (fuse, mirage.fuse.mount) (push) Has been cancelled
Test (Install) / python-extra (hdf5, mirage.core.filetype.hdf5) (push) Has been cancelled
Test (Install) / python-extra (hf, mirage.resource.hf_buckets) (push) Has been cancelled
Test (Install) / python-extra (lancedb, mirage.resource.lancedb) (push) Has been cancelled
Test (Install) / python-extra (langfuse, mirage.resource.langfuse) (push) Has been cancelled
Test (Install) / python-extra (mongodb, mirage.resource.mongodb) (push) Has been cancelled
Test (Install) / python-extra (nextcloud, mirage.resource.nextcloud) (push) Has been cancelled
Test (Install) / python-extra (openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Install) / python-extra (openhands, mirage.agents.openhands, 3.12) (push) Has been cancelled
Test (Install) / python-extra (parquet, mirage.core.filetype.parquet) (push) Has been cancelled
Test (Install) / python-extra (postgres, mirage.resource.postgres) (push) Has been cancelled
Test (Install) / python-extra (pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Test (Install) / python-extra (qdrant, mirage.resource.qdrant) (push) Has been cancelled
Test (Install) / python-extra (redis, mirage.resource.redis) (push) Has been cancelled
Test (Install) / python-extra (s3, mirage.resource.s3) (push) Has been cancelled
Test (Install) / python-extra (ssh, mirage.resource.ssh) (push) Has been cancelled
Test (Install) / ts-minimal (push) Has been cancelled
Integ / changes (push) Has been skipped
Pre-commit / pre-commit (push) Failing after 1s
CLI exit codes / changes (push) Has been skipped
Test (Install) / changes (push) Has been skipped
Test (Python) / changes (push) Has been skipped
Test (TypeScript) / changes (push) Has been skipped
CLI exit codes / cli-gate (push) Has been cancelled
Test (Install) / test-install-gate (push) Has been cancelled
Integ / integ-gate (push) Has been cancelled
Test (Python) / test-python-gate (push) Has been cancelled
Test (TypeScript) / test-typescript-gate (push) Has been cancelled
Test (Install) / python-minimal (3.12) (push) Has been cancelled
Test (Install) / python-minimal (3.11) (push) Has been cancelled
Test (Install) / python-extra (agno, mirage.agents.agno) (push) Has been cancelled
Test (Install) / python-extra (chroma, mirage.resource.chroma) (push) Has been cancelled
Test (Install) / python-extra (pdf, mirage.core.filetype.pdf) (push) Has been cancelled
Integ / integ (push) Has been cancelled
Integ / integ-database (push) Has been cancelled
Integ / integ-database-ts (push) Has been cancelled
Integ / integ-data (push) Has been cancelled
Integ / integ-ssh (push) Has been cancelled
Integ / integ-ssh-ts (push) Has been cancelled
Test (Python) / audit (push) Has been cancelled
Test (TypeScript) / test (push) Has been cancelled
Test (TypeScript) / python-fs-shim (push) Has been cancelled
CLI exit codes / Python CLI (push) Has been cancelled
CLI exit codes / TypeScript CLI (push) Has been cancelled
CLI exit codes / Cross-language snapshot interop (push) Has been cancelled
Test (Python) / test (push) Has been cancelled
Test (Python) / import-isolation (deepagents, openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Python) / import-isolation (deepagents, pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Integ / integ-ts (push) Has been cancelled
Integ / integ-fuse (push) Has been cancelled
Test (Install) / python-extra (databricks, mirage.resource.databricks_volume) (push) Has been cancelled
Test (Install) / python-extra (deepagents, mirage.agents.langchain) (push) Has been cancelled
Test (Install) / python-extra (email, mirage.resource.email) (push) Has been cancelled
Test (Install) / python-extra (fuse, mirage.fuse.mount) (push) Has been cancelled
Test (Install) / python-extra (hdf5, mirage.core.filetype.hdf5) (push) Has been cancelled
Test (Install) / python-extra (hf, mirage.resource.hf_buckets) (push) Has been cancelled
Test (Install) / python-extra (lancedb, mirage.resource.lancedb) (push) Has been cancelled
Test (Install) / python-extra (langfuse, mirage.resource.langfuse) (push) Has been cancelled
Test (Install) / python-extra (mongodb, mirage.resource.mongodb) (push) Has been cancelled
Test (Install) / python-extra (nextcloud, mirage.resource.nextcloud) (push) Has been cancelled
Test (Install) / python-extra (openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Install) / python-extra (openhands, mirage.agents.openhands, 3.12) (push) Has been cancelled
Test (Install) / python-extra (parquet, mirage.core.filetype.parquet) (push) Has been cancelled
Test (Install) / python-extra (postgres, mirage.resource.postgres) (push) Has been cancelled
Test (Install) / python-extra (pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Test (Install) / python-extra (qdrant, mirage.resource.qdrant) (push) Has been cancelled
Test (Install) / python-extra (redis, mirage.resource.redis) (push) Has been cancelled
Test (Install) / python-extra (s3, mirage.resource.s3) (push) Has been cancelled
Test (Install) / python-extra (ssh, mirage.resource.ssh) (push) Has been cancelled
Test (Install) / ts-minimal (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
import json
|
||||
from functools import partial
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from mirage.cache.index import RAMIndexCacheStore
|
||||
from mirage.types import PathSpec
|
||||
from mirage.utils.key_prefix import mount_key
|
||||
|
||||
|
||||
class FakeCollection:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.documents: dict[str, str] = {}
|
||||
self.chunks: dict[str, list[dict]] = {}
|
||||
self.get_calls: list[dict] = []
|
||||
self.queries: list[dict] = []
|
||||
self.contains_queries: list[dict] = []
|
||||
|
||||
async def get(self, **kwargs):
|
||||
self.get_calls.append(kwargs)
|
||||
ids = kwargs.get("ids")
|
||||
if ids == ["__path_tree__"]:
|
||||
return {"documents": [self.documents["__path_tree__"]]}
|
||||
|
||||
where = kwargs.get("where") or {}
|
||||
slug = where.get("page_slug")
|
||||
if isinstance(slug, dict):
|
||||
slug = slug.get("$eq")
|
||||
if slug is not None:
|
||||
chunks = self.chunks.get(slug, [])
|
||||
offset = kwargs.get("offset") or 0
|
||||
limit = kwargs.get("limit")
|
||||
if limit is not None:
|
||||
chunks = chunks[offset:offset + limit]
|
||||
elif offset:
|
||||
chunks = chunks[offset:]
|
||||
return {
|
||||
"documents": [item["document"] for item in chunks],
|
||||
"metadatas": [item["metadata"] for item in chunks],
|
||||
}
|
||||
|
||||
where_document = kwargs.get("where_document") or {}
|
||||
if "$contains" in where_document or "$regex" in where_document:
|
||||
self.contains_queries.append(kwargs)
|
||||
candidates = where.get("page_slug", {}).get("$in", [])
|
||||
pattern = where_document.get("$contains") or where_document.get(
|
||||
"$regex")
|
||||
docs: list[str] = []
|
||||
metadatas: list[dict] = []
|
||||
for slug_item in candidates:
|
||||
for chunk in self.chunks.get(slug_item, []):
|
||||
if pattern in chunk["document"]:
|
||||
docs.append(chunk["document"])
|
||||
metadatas.append(chunk["metadata"])
|
||||
return {"documents": docs, "metadatas": metadatas}
|
||||
|
||||
return {"documents": [], "metadatas": []}
|
||||
|
||||
async def query(self, **kwargs):
|
||||
self.queries.append(kwargs)
|
||||
return {
|
||||
"documents": [["quickstart chunk", "api chunk"]],
|
||||
"metadatas": [[{
|
||||
"page_slug": "guides/quickstart"
|
||||
}, {
|
||||
"page_slug": "api/reference"
|
||||
}]],
|
||||
"distances": [[0.1, 0.25]],
|
||||
}
|
||||
|
||||
|
||||
def path_tree_document() -> str:
|
||||
return json.dumps({
|
||||
"guides/quickstart": {
|
||||
"size": 12,
|
||||
"created_at": "2026-01-01T00:00:00Z",
|
||||
"updated_at": "2026-02-01T00:00:00Z",
|
||||
},
|
||||
"api/reference": {
|
||||
"size": None,
|
||||
"created_at": None,
|
||||
"updated_at": None,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def chroma_collection() -> FakeCollection:
|
||||
collection = FakeCollection()
|
||||
collection.documents["__path_tree__"] = path_tree_document()
|
||||
collection.chunks["guides/quickstart"] = [
|
||||
{
|
||||
"document": "second",
|
||||
"metadata": {
|
||||
"page_slug": "guides/quickstart",
|
||||
"chunk_index": 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
"document": "first",
|
||||
"metadata": {
|
||||
"page_slug": "guides/quickstart",
|
||||
"chunk_index": 1,
|
||||
},
|
||||
},
|
||||
]
|
||||
collection.chunks["api/reference"] = [{
|
||||
"document": "api",
|
||||
"metadata": {
|
||||
"page_slug": "api/reference",
|
||||
"chunk_index": 0,
|
||||
},
|
||||
}]
|
||||
return collection
|
||||
|
||||
|
||||
async def _get_collection(collection):
|
||||
return collection
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def chroma_accessor(chroma_collection) -> SimpleNamespace:
|
||||
return SimpleNamespace(
|
||||
config=SimpleNamespace(slug_field="page_slug",
|
||||
chunk_index_field="chunk_index"),
|
||||
collection=chroma_collection,
|
||||
get_collection=partial(_get_collection, chroma_collection))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def chroma_index() -> RAMIndexCacheStore:
|
||||
return RAMIndexCacheStore()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def knowledge_root() -> PathSpec:
|
||||
return PathSpec(resource_path=mount_key("/knowledge", "/knowledge"),
|
||||
virtual="/knowledge",
|
||||
directory="/knowledge")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def quickstart_path() -> PathSpec:
|
||||
return PathSpec.from_str_path(
|
||||
"/knowledge/guides/quickstart",
|
||||
mount_key("/knowledge/guides/quickstart", "/knowledge"))
|
||||
@@ -0,0 +1,80 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# 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.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
|
||||
from mirage.core.chroma._client import (PAGE_CHUNK_BATCH_SIZE,
|
||||
fetch_page_chunks, fetch_path_tree,
|
||||
page_chunks, query_contains)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fetch_path_tree(chroma_accessor):
|
||||
raw = await fetch_path_tree(chroma_accessor)
|
||||
assert "guides/quickstart" in raw
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fetch_path_tree_missing_raises(chroma_accessor,
|
||||
chroma_collection):
|
||||
del chroma_collection.documents["__path_tree__"]
|
||||
chroma_collection.get = _empty_get
|
||||
with pytest.raises(FileNotFoundError):
|
||||
await fetch_path_tree(chroma_accessor)
|
||||
|
||||
|
||||
async def _empty_get(**kwargs):
|
||||
return {"documents": []}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_page_chunks_sorted_by_chunk_index(chroma_accessor):
|
||||
chunks = await page_chunks(chroma_accessor, "guides/quickstart")
|
||||
assert [c["document"] for c in chunks] == ["first", "second"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fetch_page_chunks_joins_with_newline(chroma_accessor):
|
||||
text = await fetch_page_chunks(chroma_accessor, "guides/quickstart")
|
||||
assert text == "first\nsecond"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_page_chunks_paginates(chroma_accessor, chroma_collection):
|
||||
chroma_collection.chunks["big/page"] = [{
|
||||
"document": f"chunk-{i}",
|
||||
"metadata": {
|
||||
"page_slug": "big/page",
|
||||
"chunk_index": i
|
||||
},
|
||||
} for i in range(PAGE_CHUNK_BATCH_SIZE + 5)]
|
||||
chunks = await page_chunks(chroma_accessor, "big/page")
|
||||
assert len(chunks) == PAGE_CHUNK_BATCH_SIZE + 5
|
||||
assert chunks[0]["document"] == "chunk-0"
|
||||
assert chunks[-1]["document"] == f"chunk-{PAGE_CHUNK_BATCH_SIZE + 4}"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_query_contains_scopes_to_candidates(chroma_accessor):
|
||||
matched = await query_contains(chroma_accessor, "first",
|
||||
["guides/quickstart", "api/reference"])
|
||||
assert matched == ["guides/quickstart"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_query_contains_empty_candidates_short_circuits(
|
||||
chroma_accessor, chroma_collection):
|
||||
matched = await query_contains(chroma_accessor, "first", [])
|
||||
assert matched == []
|
||||
assert not chroma_collection.contains_queries
|
||||
@@ -0,0 +1,82 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# 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.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
|
||||
from mirage.core.chroma.find import find
|
||||
from mirage.types import FindType
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_find_all(chroma_accessor, chroma_index, knowledge_root):
|
||||
results = await find(chroma_accessor, knowledge_root, index=chroma_index)
|
||||
assert "/guides/quickstart" in results
|
||||
assert "/api/reference" in results
|
||||
assert "/guides" in results
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_find_name_matches_mount_root_start_path(chroma_accessor,
|
||||
chroma_index,
|
||||
knowledge_root):
|
||||
results = await find(chroma_accessor,
|
||||
knowledge_root,
|
||||
name="knowledge",
|
||||
index=chroma_index)
|
||||
assert results == ["/"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_find_by_name(chroma_accessor, chroma_index, knowledge_root):
|
||||
results = await find(chroma_accessor,
|
||||
knowledge_root,
|
||||
name="quick*",
|
||||
index=chroma_index)
|
||||
assert results == ["/guides/quickstart"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_find_type_file(chroma_accessor, chroma_index, knowledge_root):
|
||||
results = await find(chroma_accessor,
|
||||
knowledge_root,
|
||||
type=FindType.FILE,
|
||||
index=chroma_index)
|
||||
assert sorted(results) == ["/api/reference", "/guides/quickstart"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_find_type_directory(chroma_accessor, chroma_index,
|
||||
knowledge_root):
|
||||
results = await find(chroma_accessor,
|
||||
knowledge_root,
|
||||
type=FindType.DIRECTORY,
|
||||
index=chroma_index)
|
||||
assert "/guides" in results
|
||||
assert "/guides/quickstart" not in results
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_find_iname_case_insensitive(chroma_accessor, chroma_index,
|
||||
knowledge_root):
|
||||
results = await find(chroma_accessor,
|
||||
knowledge_root,
|
||||
iname="QUICK*",
|
||||
index=chroma_index)
|
||||
assert results == ["/guides/quickstart"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_find_missing_index_raises(chroma_accessor, knowledge_root):
|
||||
with pytest.raises(ValueError, match="missing index"):
|
||||
await find(chroma_accessor, knowledge_root, index=None)
|
||||
@@ -0,0 +1,56 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# 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.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
|
||||
from mirage.core.chroma.glob import resolve_glob
|
||||
from mirage.types import PathSpec
|
||||
from mirage.utils.key_prefix import mount_key
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_plain_path_passes_through(chroma_accessor, chroma_index,
|
||||
quickstart_path):
|
||||
result = await resolve_glob(chroma_accessor, [quickstart_path],
|
||||
chroma_index)
|
||||
assert result == [quickstart_path]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_pattern_expands_against_readdir(chroma_accessor, chroma_index):
|
||||
pattern = PathSpec(resource_path=mount_key("/knowledge/guides/quick*",
|
||||
"/knowledge"),
|
||||
virtual="/knowledge/guides/quick*",
|
||||
directory="/knowledge/guides",
|
||||
pattern="quick*",
|
||||
resolved=False)
|
||||
result = await resolve_glob(chroma_accessor, [pattern], chroma_index)
|
||||
assert [p.virtual for p in result] == ["/knowledge/guides/quickstart"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_pattern_without_match_stays_literal(chroma_accessor,
|
||||
chroma_index):
|
||||
# bash with nullglob off: the unmatched glob word stays the literal so
|
||||
# the command errors on it like GNU.
|
||||
pattern = PathSpec(resource_path=mount_key("/knowledge/guides/*.zip",
|
||||
"/knowledge"),
|
||||
virtual="/knowledge/guides/*.zip",
|
||||
directory="/knowledge/guides",
|
||||
pattern="*.zip",
|
||||
resolved=False)
|
||||
result = await resolve_glob(chroma_accessor, [pattern], chroma_index)
|
||||
assert [p.virtual for p in result] == ["/knowledge/guides/*.zip"]
|
||||
assert result[0].resolved
|
||||
assert result[0].pattern is None
|
||||
@@ -0,0 +1,80 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# 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.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
|
||||
from mirage.core.chroma.path import resolve_path, virtual_key_for
|
||||
from mirage.types import PathSpec
|
||||
from mirage.utils.key_prefix import mount_key
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_resolve_file(chroma_accessor, chroma_index, quickstart_path):
|
||||
resolved = await resolve_path(chroma_accessor, quickstart_path,
|
||||
chroma_index)
|
||||
assert not resolved.is_dir
|
||||
assert resolved.entry is not None
|
||||
assert resolved.entry.extra["slug"] == "guides/quickstart"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_resolve_directory(chroma_accessor, chroma_index):
|
||||
path = PathSpec.from_str_path("/knowledge/guides",
|
||||
mount_key("/knowledge/guides", "/knowledge"))
|
||||
resolved = await resolve_path(chroma_accessor, path, chroma_index)
|
||||
assert resolved.is_dir
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_resolve_mount_root(chroma_accessor, chroma_index,
|
||||
knowledge_root):
|
||||
resolved = await resolve_path(chroma_accessor, knowledge_root,
|
||||
chroma_index)
|
||||
assert resolved.is_dir
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_resolve_missing_raises(chroma_accessor, chroma_index):
|
||||
path = PathSpec.from_str_path(
|
||||
"/knowledge/missing", mount_key("/knowledge/missing", "/knowledge"))
|
||||
with pytest.raises(FileNotFoundError):
|
||||
await resolve_path(chroma_accessor, path, chroma_index)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_resolve_str_path_coerced(chroma_accessor, chroma_index):
|
||||
resolved = await resolve_path(chroma_accessor, "/guides/quickstart",
|
||||
chroma_index)
|
||||
assert not resolved.is_dir
|
||||
|
||||
|
||||
def test_virtual_key_for_prefixed():
|
||||
path = PathSpec.from_str_path(
|
||||
"/knowledge/guides/quickstart",
|
||||
mount_key("/knowledge/guides/quickstart", "/knowledge"))
|
||||
assert virtual_key_for(path) == "/knowledge/guides/quickstart"
|
||||
|
||||
|
||||
def test_virtual_key_for_root():
|
||||
path = PathSpec(resource_path=mount_key("/knowledge", "/knowledge"),
|
||||
virtual="/knowledge",
|
||||
directory="/knowledge")
|
||||
assert virtual_key_for(path) == "/knowledge"
|
||||
|
||||
|
||||
def test_virtual_key_for_unprefixed():
|
||||
path = PathSpec(resource_path="guides/quickstart",
|
||||
virtual="/guides/quickstart",
|
||||
directory="/guides")
|
||||
assert virtual_key_for(path) == "/guides/quickstart"
|
||||
@@ -0,0 +1,48 @@
|
||||
import pytest
|
||||
|
||||
from mirage.core.chroma import _client, read
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_page_chunks_reads_in_batches(monkeypatch, chroma_accessor,
|
||||
chroma_collection):
|
||||
monkeypatch.setattr(_client, "PAGE_CHUNK_BATCH_SIZE", 1)
|
||||
|
||||
chunks = await _client.page_chunks(chroma_accessor, "guides/quickstart")
|
||||
|
||||
assert [chunk["document"] for chunk in chunks] == ["first", "second"]
|
||||
page_calls = [
|
||||
call for call in chroma_collection.get_calls if call.get("where") == {
|
||||
"page_slug": "guides/quickstart"
|
||||
}
|
||||
]
|
||||
assert [call["limit"] for call in page_calls] == [1, 1, 1]
|
||||
assert [call["offset"] for call in page_calls] == [0, 1, 2]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_read_bytes_reassembles_sorted_chunks(chroma_accessor,
|
||||
chroma_index,
|
||||
quickstart_path):
|
||||
data = await read.read_bytes(chroma_accessor, quickstart_path,
|
||||
chroma_index)
|
||||
|
||||
assert data == b"first\nsecond"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_read_stream_yields_sorted_chunks(chroma_accessor, chroma_index,
|
||||
quickstart_path):
|
||||
chunks = [
|
||||
chunk async for chunk in read.read_stream(
|
||||
chroma_accessor, quickstart_path, chroma_index)
|
||||
]
|
||||
|
||||
assert chunks == [b"first", b"\n", b"second"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_read_bytes_rejects_directories(chroma_accessor, chroma_index,
|
||||
knowledge_root):
|
||||
with pytest.raises(IsADirectoryError):
|
||||
await read.read_bytes(chroma_accessor, knowledge_root, chroma_index)
|
||||
@@ -0,0 +1,41 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# 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.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
|
||||
from mirage.core.chroma.readdir import readdir
|
||||
from mirage.types import PathSpec
|
||||
from mirage.utils.key_prefix import mount_key
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_readdir_root_lists_top_level(chroma_accessor, chroma_index,
|
||||
knowledge_root):
|
||||
entries = await readdir(chroma_accessor, knowledge_root, chroma_index)
|
||||
assert sorted(entries) == ["/knowledge/api", "/knowledge/guides"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_readdir_subdir(chroma_accessor, chroma_index):
|
||||
path = PathSpec.from_str_path("/knowledge/guides",
|
||||
mount_key("/knowledge/guides", "/knowledge"))
|
||||
entries = await readdir(chroma_accessor, path, chroma_index)
|
||||
assert entries == ["/knowledge/guides/quickstart"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_readdir_on_file_raises(chroma_accessor, chroma_index,
|
||||
quickstart_path):
|
||||
with pytest.raises(NotADirectoryError):
|
||||
await readdir(chroma_accessor, quickstart_path, chroma_index)
|
||||
@@ -0,0 +1,51 @@
|
||||
import pytest
|
||||
|
||||
from mirage.core.chroma import search
|
||||
from mirage.types import PathSpec
|
||||
from mirage.utils.key_prefix import mount_key
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_segments_scopes_folder_to_candidate_slugs(
|
||||
chroma_accessor, chroma_index):
|
||||
result = await search.search_segments(
|
||||
chroma_accessor,
|
||||
"setup",
|
||||
[
|
||||
PathSpec(resource_path=mount_key("/knowledge/guides",
|
||||
"/knowledge"),
|
||||
virtual="/knowledge/guides",
|
||||
directory="/knowledge/guides")
|
||||
],
|
||||
chroma_index,
|
||||
top_k=3,
|
||||
)
|
||||
|
||||
assert result == b"/knowledge/guides/quickstart:0.90\nquickstart chunk\n"
|
||||
assert chroma_accessor.collection.queries[0]["where"] == {
|
||||
"page_slug": {
|
||||
"$in": ["guides/quickstart"]
|
||||
}
|
||||
}
|
||||
assert chroma_accessor.collection.queries[0]["query_texts"] == ["setup"]
|
||||
assert chroma_accessor.collection.queries[0]["n_results"] == 3
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_segments_empty_paths_searches_collection(
|
||||
chroma_accessor, chroma_index):
|
||||
result = await search.search_segments(chroma_accessor,
|
||||
"setup", [],
|
||||
chroma_index,
|
||||
mount_prefix="/knowledge/")
|
||||
|
||||
assert result == (b"/knowledge/guides/quickstart:0.90\nquickstart chunk\n"
|
||||
b"/knowledge/api/reference:0.75\napi chunk\n")
|
||||
assert "where" not in chroma_accessor.collection.queries[0]
|
||||
|
||||
|
||||
def test_validate_args():
|
||||
with pytest.raises(ValueError, match="query is required"):
|
||||
search.validate_args("", 10)
|
||||
with pytest.raises(ValueError, match="top-k must be positive"):
|
||||
search.validate_args("docs", 0)
|
||||
@@ -0,0 +1,53 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# 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.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
|
||||
from mirage.core.chroma.stat import stat, stat_name
|
||||
from mirage.types import FileType, PathSpec
|
||||
from mirage.utils.key_prefix import mount_key
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stat_file(chroma_accessor, chroma_index, quickstart_path):
|
||||
result = await stat(chroma_accessor, quickstart_path, chroma_index)
|
||||
assert result.type == FileType.TEXT
|
||||
assert result.name == "quickstart"
|
||||
assert result.size == 12
|
||||
assert result.modified == "2026-02-01T00:00:00Z"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stat_directory(chroma_accessor, chroma_index):
|
||||
path = PathSpec.from_str_path("/knowledge/guides",
|
||||
mount_key("/knowledge/guides", "/knowledge"))
|
||||
result = await stat(chroma_accessor, path, chroma_index)
|
||||
assert result.type == FileType.DIRECTORY
|
||||
assert result.name == "guides"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stat_missing_raises(chroma_accessor, chroma_index):
|
||||
path = PathSpec.from_str_path(
|
||||
"/knowledge/missing", mount_key("/knowledge/missing", "/knowledge"))
|
||||
with pytest.raises(FileNotFoundError):
|
||||
await stat(chroma_accessor, path, chroma_index)
|
||||
|
||||
|
||||
def test_stat_name_root():
|
||||
assert stat_name("/knowledge", "/knowledge/") == "/"
|
||||
|
||||
|
||||
def test_stat_name_nested():
|
||||
assert stat_name("/knowledge/guides", "/knowledge/") == "guides"
|
||||
@@ -0,0 +1,58 @@
|
||||
import base64
|
||||
import gzip
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from mirage.core.chroma import tree
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ensure_tree_builds_prefixed_entries_from_path_tree(
|
||||
chroma_accessor, chroma_index):
|
||||
await tree.ensure_tree(chroma_accessor, chroma_index, "/knowledge/")
|
||||
|
||||
root = await chroma_index.list_dir("/knowledge")
|
||||
guides = await chroma_index.list_dir("/knowledge/guides")
|
||||
api = await chroma_index.list_dir("/knowledge/api")
|
||||
quickstart = await chroma_index.get("/knowledge/guides/quickstart")
|
||||
|
||||
assert root.entries == ["/knowledge/api", "/knowledge/guides"]
|
||||
assert guides.entries == ["/knowledge/guides/quickstart"]
|
||||
assert api.entries == ["/knowledge/api/reference"]
|
||||
assert quickstart.entry.id == "guides/quickstart"
|
||||
assert quickstart.entry.size == 12
|
||||
assert quickstart.entry.remote_time == "2026-02-01T00:00:00Z"
|
||||
assert quickstart.entry.extra == {
|
||||
"slug": "guides/quickstart",
|
||||
"size": 12,
|
||||
"created_at": "2026-01-01T00:00:00Z",
|
||||
"updated_at": "2026-02-01T00:00:00Z",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ensure_tree_uses_cached_tree(chroma_accessor, chroma_index):
|
||||
await tree.ensure_tree(chroma_accessor, chroma_index, "/knowledge/")
|
||||
chroma_accessor.collection.documents["__path_tree__"] = json.dumps(
|
||||
{"other": {}})
|
||||
|
||||
await tree.ensure_tree(chroma_accessor, chroma_index, "/knowledge/")
|
||||
|
||||
assert (await
|
||||
chroma_index.get("/knowledge/guides/quickstart")).entry is not None
|
||||
assert (await chroma_index.get("/knowledge/other")).entry is None
|
||||
|
||||
|
||||
def test_parse_path_tree_accepts_gzip_base64():
|
||||
encoded = base64.b64encode(gzip.compress(b'{"docs/a": {}}')).decode()
|
||||
|
||||
assert tree.parse_path_tree(encoded) == {"docs/a": {}}
|
||||
|
||||
|
||||
def test_build_dir_entries_rejects_duplicate_and_collision():
|
||||
with pytest.raises(ValueError, match="Duplicate Chroma path"):
|
||||
tree.build_dir_entries({"a": {}, "/a/": {}}, "")
|
||||
|
||||
with pytest.raises(ValueError, match="Path collision"):
|
||||
tree.build_dir_entries({"a": {}, "a/b": {}}, "")
|
||||
@@ -0,0 +1,78 @@
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
# 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.
|
||||
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
||||
|
||||
import pytest
|
||||
|
||||
from mirage.core.chroma.walk import walk
|
||||
from mirage.types import PathSpec
|
||||
from mirage.utils.key_prefix import mount_key
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_walk_recurses_all(chroma_accessor, chroma_index,
|
||||
knowledge_root):
|
||||
results = await walk(chroma_accessor, knowledge_root, chroma_index)
|
||||
assert "/knowledge/guides/quickstart" in results
|
||||
assert "/knowledge/api/reference" in results
|
||||
assert "/knowledge/guides" in results
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_walk_include_root(chroma_accessor, chroma_index,
|
||||
knowledge_root):
|
||||
results = await walk(chroma_accessor,
|
||||
knowledge_root,
|
||||
chroma_index,
|
||||
include_root=True)
|
||||
assert results[0] == "/knowledge"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_walk_maxdepth_limits(chroma_accessor, chroma_index,
|
||||
knowledge_root):
|
||||
results = await walk(chroma_accessor,
|
||||
knowledge_root,
|
||||
chroma_index,
|
||||
maxdepth=1)
|
||||
assert "/knowledge/guides" in results
|
||||
assert "/knowledge/guides/quickstart" not in results
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_walk_strip_prefix(chroma_accessor, chroma_index,
|
||||
knowledge_root):
|
||||
results = await walk(chroma_accessor,
|
||||
knowledge_root,
|
||||
chroma_index,
|
||||
strip_prefix=True)
|
||||
assert all(not item.startswith("/knowledge") for item in results)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_walk_missing_raises(chroma_accessor, chroma_index):
|
||||
path = PathSpec.from_str_path(
|
||||
"/knowledge/missing", mount_key("/knowledge/missing", "/knowledge"))
|
||||
with pytest.raises(FileNotFoundError):
|
||||
await walk(chroma_accessor, path, chroma_index)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_walk_missing_ignored(chroma_accessor, chroma_index):
|
||||
path = PathSpec.from_str_path(
|
||||
"/knowledge/missing", mount_key("/knowledge/missing", "/knowledge"))
|
||||
results = await walk(chroma_accessor,
|
||||
path,
|
||||
chroma_index,
|
||||
ignore_missing=True)
|
||||
assert results == []
|
||||
Reference in New Issue
Block a user