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

This commit is contained in:
wehub-resource-sync
2026-07-13 12:30:44 +08:00
commit bcbd1bdb22
5748 changed files with 562488 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
# ========= 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. =========
+340
View File
@@ -0,0 +1,340 @@
# ========= 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. =========
from unittest.mock import AsyncMock, MagicMock
import pytest
from mirage.core.mongodb._client import (get_index_stats, get_indexes,
get_validator, is_view,
iter_documents, iter_inserts)
class _AsyncIter:
def __init__(self, items):
self._items = list(items)
def __aiter__(self):
return self
async def __anext__(self):
if not self._items:
raise StopAsyncIteration
return self._items.pop(0)
def _build_mock_client(docs):
cursor = MagicMock()
cursor.sort = MagicMock(return_value=cursor)
cursor.batch_size = MagicMock(return_value=cursor)
cursor.__aiter__ = lambda self: _AsyncIter(docs).__aiter__()
col = MagicMock()
col.find = MagicMock(return_value=cursor)
db = MagicMock()
db.__getitem__.return_value = col
client = MagicMock()
client.__getitem__.return_value = db
return client, col, cursor
@pytest.mark.asyncio
async def test_iter_documents_yields_each_doc_in_order():
docs = [{"_id": i, "v": i * 10} for i in range(3)]
client, col, cursor = _build_mock_client(docs)
out = []
async for doc in iter_documents(client, "db1", "coll1", batch_size=100):
out.append(doc)
assert out == docs
col.find.assert_called_once_with({}, None)
cursor.batch_size.assert_called_once_with(100)
cursor.sort.assert_not_called()
@pytest.mark.asyncio
async def test_iter_documents_applies_filter_projection_and_sort():
docs = [{"_id": 1, "x": 5}]
client, col, cursor = _build_mock_client(docs)
out = []
async for doc in iter_documents(client,
"db1",
"coll1",
filter={"x": {
"$gt": 0
}},
projection={"x": 1},
sort=[("_id", -1)],
batch_size=50):
out.append(doc)
assert out == docs
col.find.assert_called_once_with({"x": {"$gt": 0}}, {"x": 1})
cursor.sort.assert_called_once_with([("_id", -1)])
cursor.batch_size.assert_called_once_with(50)
@pytest.mark.asyncio
async def test_iter_documents_empty_yields_nothing():
client, _, _ = _build_mock_client([])
out = []
async for doc in iter_documents(client, "db1", "coll1"):
out.append(doc)
assert out == []
def _build_indexes_client(spec, indexes):
spec_cursor = MagicMock()
spec_cursor.__aiter__ = lambda self: _AsyncIter([spec]
if spec else []).__aiter__(
)
idx_cursor = MagicMock()
idx_cursor.__aiter__ = lambda self: _AsyncIter(indexes).__aiter__()
col = MagicMock()
col.list_indexes = AsyncMock(return_value=idx_cursor)
db = MagicMock()
db.list_collections = AsyncMock(return_value=spec_cursor)
db.__getitem__.return_value = col
client = MagicMock()
client.__getitem__.return_value = db
return client, col, db
@pytest.mark.asyncio
async def test_is_view_true_when_spec_type_view():
client, _, db = _build_indexes_client(
spec={
"name": "myview",
"type": "view"
},
indexes=[],
)
assert await is_view(client, "db1", "myview") is True
db.list_collections.assert_awaited_once_with(filter={"name": "myview"})
@pytest.mark.asyncio
async def test_is_view_false_for_regular_collection():
client, _, _ = _build_indexes_client(
spec={
"name": "coll1",
"type": "collection"
},
indexes=[],
)
assert await is_view(client, "db1", "coll1") is False
@pytest.mark.asyncio
async def test_is_view_false_when_collection_absent():
client, _, _ = _build_indexes_client(spec=None, indexes=[])
assert await is_view(client, "db1", "missing") is False
@pytest.mark.asyncio
async def test_get_indexes_returns_indexes_for_collection():
indexes = [{"name": "_id_", "key": {"_id": 1}}]
client, col, db = _build_indexes_client(
spec={
"name": "coll1",
"type": "collection"
},
indexes=indexes,
)
out = await get_indexes(client, "db1", "coll1")
assert out == indexes
db.list_collections.assert_awaited_once_with(filter={"name": "coll1"})
col.list_indexes.assert_awaited_once_with()
def _build_validator_client(spec):
spec_cursor = MagicMock()
spec_cursor.__aiter__ = lambda self: _AsyncIter([spec]
if spec else []).__aiter__(
)
db = MagicMock()
db.list_collections = AsyncMock(return_value=spec_cursor)
client = MagicMock()
client.__getitem__.return_value = db
return client, db
@pytest.mark.asyncio
async def test_get_validator_returns_json_schema_when_present():
spec = {
"name": "movies",
"options": {
"validator": {
"$jsonSchema": {
"bsonType": "object",
"required": ["title"]
}
}
}
}
client, db = _build_validator_client(spec)
out = await get_validator(client, "db1", "movies")
assert out == {"bsonType": "object", "required": ["title"]}
db.list_collections.assert_awaited_once_with(filter={"name": "movies"})
@pytest.mark.asyncio
async def test_get_validator_returns_none_without_validator():
client, _ = _build_validator_client({"name": "movies", "options": {}})
assert await get_validator(client, "db1", "movies") is None
@pytest.mark.asyncio
async def test_get_validator_returns_none_when_collection_missing():
client, _ = _build_validator_client(None)
assert await get_validator(client, "db1", "ghost") is None
def _build_indexstats_client(rows):
cursor = MagicMock()
cursor.__aiter__ = lambda self: _AsyncIter(rows).__aiter__()
col = MagicMock()
col.aggregate = AsyncMock(return_value=cursor)
db = MagicMock()
db.__getitem__.return_value = col
client = MagicMock()
client.__getitem__.return_value = db
return client, col
@pytest.mark.asyncio
async def test_get_index_stats_returns_map_keyed_by_name():
rows = [
{
"name": "_id_",
"accesses": {
"ops": 1234,
"since": "2026-01-01"
}
},
{
"name": "title_text",
"accesses": {
"ops": 5678,
"since": "2026-02-01"
}
},
]
client, col = _build_indexstats_client(rows)
out = await get_index_stats(client, "db1", "coll1")
assert out["title_text"] == {"ops": 5678, "since": "2026-02-01"}
assert out["_id_"] == {"ops": 1234, "since": "2026-01-01"}
col.aggregate.assert_awaited_once_with([{"$indexStats": {}}])
@pytest.mark.asyncio
async def test_get_index_stats_empty_when_view():
client, _ = _build_indexstats_client([])
out = await get_index_stats(client, "db1", "myview")
assert out == {}
@pytest.mark.asyncio
async def test_get_indexes_returns_empty_for_view_without_listing():
client, col, db = _build_indexes_client(
spec={
"name": "myview",
"type": "view"
},
indexes=[],
)
out = await get_indexes(client, "db1", "myview")
assert out == []
db.list_collections.assert_awaited_once_with(filter={"name": "myview"})
col.list_indexes.assert_not_called()
class _AsyncChangeStream:
def __init__(self, changes):
self._iter = _AsyncIter(changes)
async def __aenter__(self):
return self
async def __aexit__(self, *exc):
return False
def __aiter__(self):
return self._iter
def _build_watch_client(changes):
stream = _AsyncChangeStream(changes)
col = MagicMock()
col.watch = AsyncMock(return_value=stream)
db = MagicMock()
db.__getitem__.return_value = col
client = MagicMock()
client.__getitem__.return_value = db
return client, col
@pytest.mark.asyncio
async def test_iter_inserts_yields_full_documents():
changes = [
{
"operationType": "insert",
"fullDocument": {
"_id": 1,
"v": "a"
}
},
{
"operationType": "insert",
"fullDocument": {
"_id": 2,
"v": "b"
}
},
]
client, col = _build_watch_client(changes)
out = []
async for doc in iter_inserts(client, "db1", "coll1"):
out.append(doc)
assert out == [{"_id": 1, "v": "a"}, {"_id": 2, "v": "b"}]
col.watch.assert_awaited_once_with([{
"$match": {
"operationType": "insert"
}
}])
@pytest.mark.asyncio
async def test_iter_inserts_skips_changes_without_full_document():
changes = [
{
"operationType": "insert",
"fullDocument": {
"_id": 1
}
},
{
"operationType": "drop"
},
{
"operationType": "insert",
"fullDocument": {
"_id": 2
}
},
]
client, _ = _build_watch_client(changes)
out = []
async for doc in iter_inserts(client, "db1", "coll1"):
out.append(doc)
assert out == [{"_id": 1}, {"_id": 2}]
+182
View File
@@ -0,0 +1,182 @@
# ========= 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 json
from unittest.mock import AsyncMock, patch
import pytest
from bson import ObjectId
from mirage.accessor.mongodb import MongoDBAccessor
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.core.mongodb.read import read
from mirage.resource.mongodb.config import MongoDBConfig
from mirage.types import PathSpec
DOCS_PATH = "/sample_mflix/collections/movies/documents.jsonl"
SCHEMA_PATH = "/sample_mflix/collections/movies/schema.json"
DBJSON_PATH = "/sample_mflix/database.json"
async def _gen(items):
for item in items:
yield item
@pytest.fixture
def index():
return RAMIndexCacheStore()
@pytest.fixture
def accessor():
return MongoDBAccessor(config=MongoDBConfig(
uri="mongodb://localhost:27017"))
def _patched_iter(docs):
return patch("mirage.core.mongodb.stream.iter_documents",
new=lambda *args, **kwargs: _gen(docs))
def _path(s: str) -> PathSpec:
return PathSpec(virtual=s, directory=s, resource_path=s.strip("/"))
@pytest.fixture(autouse=True)
def _stub_existence_checks():
with patch(
"mirage.core.mongodb.read.database_exists",
new_callable=AsyncMock,
return_value=True,
), patch(
"mirage.core.mongodb.read.entity_exists",
new_callable=AsyncMock,
return_value=True,
):
yield
@pytest.mark.asyncio
async def test_read_documents_returns_extended_json_jsonl(accessor, index):
oid = ObjectId()
docs = [
{
"_id": oid,
"title": "Movie 1"
},
{
"_id": ObjectId(),
"title": "Movie 2"
},
]
with _patched_iter(docs):
result = await read(accessor, _path(DOCS_PATH), index)
lines = result.decode().strip().split("\n")
assert len(lines) == 2
first = json.loads(lines[0])
assert first["title"] == "Movie 1"
assert first["_id"] == {"$oid": str(oid)}
@pytest.mark.asyncio
async def test_read_documents_no_doc_cap(accessor, index):
docs = [{"_id": ObjectId(), "x": i} for i in range(50)]
with _patched_iter(docs):
result = await read(accessor, _path(DOCS_PATH), index)
lines = result.decode().strip().split("\n")
assert len(lines) == 50
@pytest.mark.asyncio
async def test_read_documents_empty(accessor, index):
with _patched_iter([]):
result = await read(accessor, _path(DOCS_PATH), index)
assert result == b""
@pytest.mark.asyncio
async def test_read_schema_json_returns_jsonschema_payload(accessor, index):
payload = {
"database": "sample_mflix",
"name": "movies",
"kind": "collection",
"validator": None,
"fields": [],
"primary_key": "_id",
"indexes": [],
"document_count": 0,
"sampled": 100,
}
with patch("mirage.core.mongodb.read.build_collection_schema_json",
new=AsyncMock(return_value=payload)):
result = await read(accessor, _path(SCHEMA_PATH), index)
parsed = json.loads(result.decode())
assert parsed == payload
@pytest.mark.asyncio
async def test_read_database_json_returns_payload(accessor, index):
payload = {
"database": "sample_mflix",
"collections": [{
"name": "movies",
"document_count": 100
}],
"views": [{
"name": "top_rated"
}],
}
with patch("mirage.core.mongodb.read.build_database_json",
new=AsyncMock(return_value=payload)):
result = await read(accessor, _path(DBJSON_PATH), index)
parsed = json.loads(result.decode())
assert parsed == payload
@pytest.mark.asyncio
async def test_read_unknown_path_raises(accessor, index):
with pytest.raises(FileNotFoundError):
await read(accessor, _path("/garbage/path"), index)
@pytest.mark.asyncio
async def test_read_kind_dir_path_raises(accessor, index):
with pytest.raises(FileNotFoundError):
await read(accessor, _path("/sample_mflix/collections"), index)
@pytest.mark.asyncio
async def test_read_documents_missing_collection_raises(accessor, index):
with patch(
"mirage.core.mongodb.read.entity_exists",
new_callable=AsyncMock,
return_value=False,
):
with pytest.raises(FileNotFoundError):
await read(
accessor,
_path("/sample_mflix/collections/ghost/documents.jsonl"),
index)
@pytest.mark.asyncio
async def test_read_database_json_missing_db_raises(accessor, index):
with patch(
"mirage.core.mongodb.read.database_exists",
new_callable=AsyncMock,
return_value=False,
):
with pytest.raises(FileNotFoundError):
await read(accessor, _path("/ghost/database.json"), index)
+184
View File
@@ -0,0 +1,184 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.mongodb import MongoDBAccessor
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.core.mongodb.readdir import is_dir_name, readdir
from mirage.resource.mongodb.config import MongoDBConfig
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
@pytest.fixture
def index():
return RAMIndexCacheStore()
@pytest.fixture
def accessor():
return MongoDBAccessor(config=MongoDBConfig(
uri="mongodb://localhost:27017"))
def _path(s: str) -> PathSpec:
return PathSpec(virtual=s, directory=s, resource_path=s.strip("/"))
@pytest.fixture(autouse=True)
def _stub_existence_checks():
with patch(
"mirage.core.mongodb.readdir.database_exists",
new_callable=AsyncMock,
return_value=True,
), patch(
"mirage.core.mongodb.readdir.entity_exists",
new_callable=AsyncMock,
return_value=True,
):
yield
@pytest.mark.asyncio
async def test_readdir_root_lists_databases(accessor, index):
with patch(
"mirage.core.mongodb.readdir.list_databases",
new_callable=AsyncMock,
return_value=["db1", "db2"],
):
result = await readdir(accessor, _path("/"), index)
assert "/db1" in result
assert "/db2" in result
@pytest.mark.asyncio
async def test_readdir_database_returns_fixed_children(accessor, index):
result = await readdir(accessor, _path("/sample_mflix"), index)
assert result == [
"/sample_mflix/database.json",
"/sample_mflix/collections",
"/sample_mflix/views",
]
@pytest.mark.asyncio
async def test_readdir_collections_dir_lists_collections_only(accessor, index):
with patch(
"mirage.core.mongodb.readdir.list_collections",
new_callable=AsyncMock,
return_value=["movies", "users"],
) as mock_list:
result = await readdir(accessor, _path("/sample_mflix/collections"),
index)
assert "/sample_mflix/collections/movies" in result
assert "/sample_mflix/collections/users" in result
mock_list.assert_awaited_once_with(accessor.client,
"sample_mflix",
kind="collection")
@pytest.mark.asyncio
async def test_readdir_views_dir_lists_views_only(accessor, index):
with patch(
"mirage.core.mongodb.readdir.list_collections",
new_callable=AsyncMock,
return_value=["top_rated"],
) as mock_list:
result = await readdir(accessor, _path("/sample_mflix/views"), index)
assert result == ["/sample_mflix/views/top_rated"]
mock_list.assert_awaited_once_with(accessor.client,
"sample_mflix",
kind="view")
@pytest.mark.asyncio
async def test_readdir_collection_entity_lists_schema_and_documents(
accessor, index):
result = await readdir(accessor, _path("/sample_mflix/collections/movies"),
index)
assert result == [
"/sample_mflix/collections/movies/schema.json",
"/sample_mflix/collections/movies/documents.jsonl",
]
@pytest.mark.asyncio
async def test_readdir_view_entity_lists_schema_and_documents(accessor, index):
result = await readdir(accessor, _path("/sample_mflix/views/top_rated"),
index)
assert result == [
"/sample_mflix/views/top_rated/schema.json",
"/sample_mflix/views/top_rated/documents.jsonl",
]
@pytest.mark.asyncio
async def test_readdir_unknown_path_raises(accessor, index):
with pytest.raises(FileNotFoundError):
await readdir(accessor, _path("/db/something/extra"), index)
@pytest.mark.asyncio
async def test_readdir_root_index_caches_databases(accessor, index):
mock_list = AsyncMock(return_value=["db1"])
with patch("mirage.core.mongodb.readdir.list_databases", new=mock_list):
first = await readdir(accessor, _path("/"), index)
second = await readdir(accessor, _path("/"), index)
assert first == second
assert mock_list.await_count == 1
@pytest.mark.asyncio
async def test_readdir_database_raises_when_db_missing(accessor, index):
with patch(
"mirage.core.mongodb.readdir.database_exists",
new_callable=AsyncMock,
return_value=False,
):
with pytest.raises(FileNotFoundError):
await readdir(accessor, _path("/ghost"), index)
@pytest.mark.asyncio
async def test_readdir_entity_raises_when_collection_missing(accessor, index):
with patch(
"mirage.core.mongodb.readdir.entity_exists",
new_callable=AsyncMock,
return_value=False,
):
with pytest.raises(FileNotFoundError):
await readdir(accessor, _path("/sample_mflix/collections/ghost"),
index)
@pytest.mark.asyncio
async def test_readdir_prefix_carries_through(accessor, index):
p = PathSpec(resource_path=mount_key("/mongo/sample_mflix", "/mongo"),
virtual="/mongo/sample_mflix",
directory="/mongo/sample_mflix")
result = await readdir(accessor, p, index)
assert result == [
"/mongo/sample_mflix/database.json",
"/mongo/sample_mflix/collections",
"/mongo/sample_mflix/views",
]
def test_is_dir_name_classifies_by_extension():
assert is_dir_name("/db/collections") is True
assert is_dir_name("/db/database.json") is False
assert is_dir_name("/db/collections/books/documents.jsonl") is False
+122
View File
@@ -0,0 +1,122 @@
# ========= 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. =========
from unittest.mock import AsyncMock, MagicMock
import pytest
from bson import Decimal128, ObjectId
from mirage.core.mongodb._sampler import sample_field_types
class _AsyncIter:
def __init__(self, items):
self._items = list(items)
def __aiter__(self):
return self
async def __anext__(self):
if not self._items:
raise StopAsyncIteration
return self._items.pop(0)
def _col(docs):
col = MagicMock()
col.aggregate = AsyncMock(return_value=_AsyncIter(docs))
return col
@pytest.mark.asyncio
async def test_sample_field_types_tallies_types_and_presence():
docs = [
{
"_id": 1,
"title": "Hi",
"year": 2020,
"tags": ["a", "b"]
},
{
"_id": 2,
"title": "Bye",
"year": "2021",
"tags": ["c"]
},
{
"_id": 3,
"title": "Yo"
},
]
out = await sample_field_types(_col(docs), sample_size=3)
by_path = {f["path"]: f for f in out}
assert "_id" not in by_path
assert by_path["title"]["presence"] == 1.0
assert by_path["title"]["types"] == {"string": 1.0}
assert by_path["year"]["presence"] == pytest.approx(2 / 3)
assert set(by_path["year"]["types"].keys()) == {"int", "string"}
assert by_path["tags"]["types"] == {"array<string>": pytest.approx(2 / 3)}
@pytest.mark.asyncio
async def test_sample_field_types_recognizes_fixed_numeric_arrays():
docs = [{"_id": i, "embedding": [0.1] * 1024} for i in range(5)]
out = await sample_field_types(_col(docs), sample_size=5)
by_path = {f["path"]: f for f in out}
assert by_path["embedding"]["types"] == {"array<double>(1024)": 1.0}
@pytest.mark.asyncio
async def test_sample_field_types_walks_nested_objects():
docs = [
{
"_id": 1,
"metadata": {
"tag": "a",
"ratings": [1.0, 2.0]
}
},
{
"_id": 2,
"metadata": {
"tag": "b",
"extra": ObjectId()
}
},
]
out = await sample_field_types(_col(docs), sample_size=2)
paths = {f["path"] for f in out}
assert "metadata.tag" in paths
assert "metadata.ratings" in paths
assert "metadata.extra" in paths
@pytest.mark.asyncio
async def test_sample_field_types_handles_bson_scalars():
docs = [{
"_id": 1,
"dec": Decimal128("1.23"),
"oid": ObjectId("65f0000000000000000000a1")
}]
out = await sample_field_types(_col(docs), sample_size=1)
by_path = {f["path"]: f for f in out}
assert by_path["dec"]["types"] == {"decimal": 1.0}
assert by_path["oid"]["types"] == {"objectId": 1.0}
@pytest.mark.asyncio
async def test_sample_field_types_empty_sample_returns_empty_list():
out = await sample_field_types(_col([]), sample_size=5)
assert out == []
@@ -0,0 +1,119 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.mongodb import MongoDBAccessor
from mirage.core.mongodb._schema_json import build_collection_schema_json
from mirage.resource.mongodb.config import MongoDBConfig
@pytest.fixture
def accessor():
return MongoDBAccessor(config=MongoDBConfig(
uri="mongodb://localhost:27017"))
@pytest.mark.asyncio
async def test_build_collection_schema_json_assembles_all_sections(accessor):
fields = [{"path": "title", "presence": 1.0, "types": {"string": 1.0}}]
indexes = [{"name": "_id_", "key": {"_id": 1}}]
stats = {"_id_": {"ops": 42, "since": "2026-03-01T00:00:00Z"}}
with (
patch("mirage.core.mongodb._schema_json.get_validator",
new=AsyncMock(return_value={"bsonType": "object"})),
patch("mirage.core.mongodb._schema_json.sample_field_types",
new=AsyncMock(return_value=fields)),
patch("mirage.core.mongodb._schema_json.get_indexes",
new=AsyncMock(return_value=indexes)),
patch("mirage.core.mongodb._schema_json.get_index_stats",
new=AsyncMock(return_value=stats)),
patch("mirage.core.mongodb._schema_json.count_documents",
new=AsyncMock(return_value=999)),
patch("mirage.core.mongodb._schema_json.is_view",
new=AsyncMock(return_value=False)),
):
out = await build_collection_schema_json(accessor, "db1", "movies")
assert out["database"] == "db1"
assert out["name"] == "movies"
assert out["kind"] == "collection"
assert out["validator"] == {"bsonType": "object"}
assert out["fields"] == fields
assert out["primary_key"] == "_id"
assert out["document_count"] == 999
assert out["sampled"] == 100
assert len(out["indexes"]) == 1
enriched = out["indexes"][0]
assert enriched["name"] == "_id_"
assert enriched["keys"] == {"_id": 1}
assert enriched["type"] == "btree"
assert enriched["stats"] == {"ops": 42, "since": "2026-03-01T00:00:00Z"}
@pytest.mark.asyncio
async def test_build_collection_schema_json_text_index_tagged(accessor):
indexes = [{
"name": "title_text",
"key": {
"_fts": "text",
"_ftsx": 1
},
"textIndexVersion": 3,
}]
with (
patch("mirage.core.mongodb._schema_json.get_validator",
new=AsyncMock(return_value=None)),
patch("mirage.core.mongodb._schema_json.sample_field_types",
new=AsyncMock(return_value=[])),
patch("mirage.core.mongodb._schema_json.get_indexes",
new=AsyncMock(return_value=indexes)),
patch("mirage.core.mongodb._schema_json.get_index_stats",
new=AsyncMock(return_value={})),
patch("mirage.core.mongodb._schema_json.count_documents",
new=AsyncMock(return_value=0)),
patch("mirage.core.mongodb._schema_json.is_view",
new=AsyncMock(return_value=False)),
):
out = await build_collection_schema_json(accessor, "db1", "articles")
assert out["indexes"][0]["type"] == "text"
assert out["indexes"][0]["stats"] == {}
@pytest.mark.asyncio
async def test_build_collection_schema_json_view_skips_indexes(accessor):
fields = [{"path": "title", "presence": 1.0, "types": {"string": 1.0}}]
with (
patch("mirage.core.mongodb._schema_json.get_validator",
new=AsyncMock(return_value=None)),
patch("mirage.core.mongodb._schema_json.sample_field_types",
new=AsyncMock(return_value=fields)),
patch("mirage.core.mongodb._schema_json.get_indexes",
new=AsyncMock(side_effect=AssertionError(
"get_indexes must not be called for views"))),
patch("mirage.core.mongodb._schema_json.get_index_stats",
new=AsyncMock(side_effect=AssertionError(
"get_index_stats must not be called for views"))),
patch("mirage.core.mongodb._schema_json.count_documents",
new=AsyncMock(return_value=40)),
patch("mirage.core.mongodb._schema_json.is_view",
new=AsyncMock(return_value=True)),
):
out = await build_collection_schema_json(accessor, "db1", "myview")
assert out["kind"] == "view"
assert out["indexes"] == []
assert out["validator"] is None
assert out["document_count"] == 40
assert out["fields"] == fields
@@ -0,0 +1,92 @@
# ========= 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 os
from pathlib import Path
import pytest
from dotenv import load_dotenv
from mirage.accessor.mongodb import MongoDBAccessor
from mirage.core.mongodb._schema_json import build_collection_schema_json
from mirage.resource.mongodb.config import MongoDBConfig
pytestmark = pytest.mark.skipif(
os.environ.get("MIRAGE_RUN_INTEGRATION_MONGO") != "1",
reason="integration test (set MIRAGE_RUN_INTEGRATION_MONGO=1 to enable)",
)
def _load_env() -> str:
repo_root = Path(__file__).resolve().parents[4]
load_dotenv(repo_root / ".env.development")
uri = os.environ.get("MONGODB_URI")
if not uri:
pytest.skip("MONGODB_URI not set in .env.development")
return uri
@pytest.fixture
def accessor():
return MongoDBAccessor(config=MongoDBConfig(uri=_load_env()))
@pytest.mark.asyncio
async def test_schema_captures_jsonschema_validator(accessor):
s = await build_collection_schema_json(accessor, "mirage_test",
"with_validator")
assert s["kind"] == "collection"
assert s["validator"]["bsonType"] == "object"
assert "title" in s["validator"]["required"]
assert "year" in s["validator"]["required"]
@pytest.mark.asyncio
async def test_schema_recognizes_fixed_length_embedding_array(accessor):
s = await build_collection_schema_json(accessor, "mirage_test",
"embeddings")
vec = next(f for f in s["fields"] if f["path"] == "vector")
assert vec["types"] == {"array<double>(1024)": 1.0}
@pytest.mark.asyncio
async def test_schema_tags_text_index_and_returns_indexstats(accessor):
s = await build_collection_schema_json(accessor, "mirage_test",
"text_indexed")
by_name = {idx["name"]: idx for idx in s["indexes"]}
assert by_name["title_body_text"]["type"] == "text"
assert "ops" in by_name["title_body_text"]["stats"]
assert by_name["_id_"]["type"] == "btree"
@pytest.mark.asyncio
async def test_schema_view_marks_kind_and_skips_indexes(accessor):
s = await build_collection_schema_json(accessor, "mirage_test",
"high_rated_films")
assert s["kind"] == "view"
assert s["indexes"] == []
assert s["document_count"] == 40
@pytest.mark.asyncio
async def test_schema_heterogeneous_collection_surfaces_mixed_types(accessor):
s = await build_collection_schema_json(accessor,
"mirage_test",
"heterogeneous",
sample_size=200)
by_path = {f["path"]: f for f in s["fields"]}
assert "metadata.tag" in by_path
score = by_path["score"]
assert set(score["types"].keys()).issubset({"string", "int", "null"})
assert sum(score["types"].values()) == pytest.approx(score["presence"])
+138
View File
@@ -0,0 +1,138 @@
# ========= 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. =========
from mirage.core.mongodb.scope import detect_scope
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def test_root():
scope = detect_scope("/")
assert scope.level == "root"
assert scope.database is None
assert scope.kind is None
assert scope.name is None
def test_database():
scope = detect_scope("/sample_mflix")
assert scope.level == "database"
assert scope.database == "sample_mflix"
assert scope.kind is None
assert scope.name is None
def test_database_json():
scope = detect_scope("/sample_mflix/database.json")
assert scope.level == "database_json"
assert scope.database == "sample_mflix"
def test_collections_kind_dir():
scope = detect_scope("/sample_mflix/collections")
assert scope.level == "kind_dir"
assert scope.database == "sample_mflix"
assert scope.kind == "collection"
def test_views_kind_dir():
scope = detect_scope("/sample_mflix/views")
assert scope.level == "kind_dir"
assert scope.database == "sample_mflix"
assert scope.kind == "view"
def test_collection_entity_dir():
scope = detect_scope("/sample_mflix/collections/movies")
assert scope.level == "entity"
assert scope.database == "sample_mflix"
assert scope.kind == "collection"
assert scope.name == "movies"
def test_view_entity_dir():
scope = detect_scope("/sample_mflix/views/top_rated")
assert scope.level == "entity"
assert scope.database == "sample_mflix"
assert scope.kind == "view"
assert scope.name == "top_rated"
def test_collection_schema_json():
scope = detect_scope("/sample_mflix/collections/movies/schema.json")
assert scope.level == "schema_json"
assert scope.database == "sample_mflix"
assert scope.kind == "collection"
assert scope.name == "movies"
def test_collection_documents_jsonl():
scope = detect_scope("/sample_mflix/collections/movies/documents.jsonl")
assert scope.level == "documents"
assert scope.database == "sample_mflix"
assert scope.kind == "collection"
assert scope.name == "movies"
def test_view_documents_jsonl():
scope = detect_scope("/sample_mflix/views/top_rated/documents.jsonl")
assert scope.level == "documents"
assert scope.kind == "view"
assert scope.name == "top_rated"
def test_unknown_leaf_under_entity():
scope = detect_scope("/sample_mflix/collections/movies/weird.txt")
assert scope.level == "unknown"
def test_unknown_top_segment_under_db():
scope = detect_scope("/sample_mflix/randomdir")
assert scope.level == "unknown"
def test_pathspec_with_prefix_root():
p = PathSpec(
resource_path=mount_key("/mongo/", "/mongo"),
virtual="/mongo/",
directory="/mongo/",
)
scope = detect_scope(p)
assert scope.level == "root"
def test_pathspec_with_prefix_database():
p = PathSpec(
resource_path=mount_key("/mongo/sample_mflix", "/mongo"),
virtual="/mongo/sample_mflix",
directory="/mongo/",
)
scope = detect_scope(p)
assert scope.level == "database"
assert scope.database == "sample_mflix"
def test_pathspec_with_prefix_documents():
p = PathSpec(
resource_path=mount_key(
"/mongo/sample_mflix/collections/movies/documents.jsonl",
"/mongo"),
virtual="/mongo/sample_mflix/collections/movies/documents.jsonl",
directory="/mongo/sample_mflix/collections/movies/",
)
scope = detect_scope(p)
assert scope.level == "documents"
assert scope.database == "sample_mflix"
assert scope.kind == "collection"
assert scope.name == "movies"
+139
View File
@@ -0,0 +1,139 @@
# ========= 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 asyncio
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from mirage.core.mongodb.search import search_collection, search_database
class _AsyncIter:
def __init__(self, items):
self._items = list(items)
def __aiter__(self):
return self
async def __anext__(self):
if not self._items:
raise StopAsyncIteration
return self._items.pop(0)
def _agg_iter(items):
return _AsyncIter(items)
def _build_search_client(sampled_docs, matched_docs):
col = MagicMock()
col.aggregate = AsyncMock(return_value=_agg_iter(sampled_docs))
cursor = MagicMock()
cursor.limit = MagicMock(return_value=cursor)
cursor.to_list = AsyncMock(return_value=matched_docs)
col.find = MagicMock(return_value=cursor)
db = MagicMock()
db.__getitem__.return_value = col
client = MagicMock()
client.__getitem__.return_value = db
return client, col
@pytest.mark.asyncio
async def test_search_collection_unions_string_fields_across_sampled_docs():
sampled = [
{
"_id": 1,
"title": "Hello"
},
{
"_id": 2,
"body": "World"
},
{
"_id": 3,
"metadata": {
"tag": "x"
}
},
]
matched = [{"_id": 2, "body": "World matches"}]
client, col = _build_search_client(sampled, matched)
with patch("mirage.core.mongodb.search.get_indexes",
new=AsyncMock(return_value=[])):
out = await search_collection(client,
"db1",
"coll1",
"World",
limit=10)
assert out == matched
filter_arg = col.find.call_args[0][0]
or_fields = {list(clause.keys())[0] for clause in filter_arg["$or"]}
assert {"title", "body", "metadata.tag"}.issubset(or_fields)
@pytest.mark.asyncio
async def test_search_collection_uses_text_when_textIndexVersion_present():
indexes = [{
"name": "title_text",
"key": {
"_fts": "text",
"_ftsx": 1
},
"weights": {
"title": 1
},
"textIndexVersion": 3,
}]
client, col = _build_search_client([], [{"_id": 1}])
with patch("mirage.core.mongodb.search.get_indexes",
new=AsyncMock(return_value=indexes)):
await search_collection(client, "db1", "coll1", "query", limit=10)
assert col.find.call_args[0][0] == {"$text": {"$search": "query"}}
@pytest.mark.asyncio
async def test_search_collection_no_string_fields_returns_no_results():
sampled = [{"_id": 1, "n": 42}, {"_id": 2, "n": 7}]
client, col = _build_search_client(sampled, [])
with patch("mirage.core.mongodb.search.get_indexes",
new=AsyncMock(return_value=[])):
out = await search_collection(client,
"db1",
"coll1",
"anything",
limit=10)
assert out == []
assert col.find.call_args is None
@pytest.mark.asyncio
async def test_search_database_runs_collections_concurrently():
barrier = asyncio.Barrier(3)
async def slow_search(client, database, col, pattern, limit):
await barrier.wait()
return [{"_id": col, "v": col}]
with patch("mirage.core.mongodb.search.list_collections",
new=AsyncMock(return_value=["a", "b", "c"])):
with patch("mirage.core.mongodb.search.search_collection",
new=slow_search):
out = await asyncio.wait_for(
search_database(None, "db", "p", 10),
timeout=2.0,
)
assert {col for _, col, _ in out} == {"a", "b", "c"}
+200
View File
@@ -0,0 +1,200 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.mongodb import MongoDBAccessor
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.core.mongodb.stat import stat
from mirage.resource.mongodb.config import MongoDBConfig
from mirage.types import FileType, PathSpec
@pytest.fixture
def index():
return RAMIndexCacheStore()
@pytest.fixture
def accessor():
return MongoDBAccessor(config=MongoDBConfig(
uri="mongodb://localhost:27017"))
def _path(s: str) -> PathSpec:
return PathSpec(virtual=s, directory=s, resource_path=s.strip("/"))
@pytest.fixture(autouse=True)
def _stub_existence_checks():
with patch(
"mirage.core.mongodb.stat.database_exists",
new_callable=AsyncMock,
return_value=True,
), patch(
"mirage.core.mongodb.stat.entity_exists",
new_callable=AsyncMock,
return_value=True,
):
yield
@pytest.mark.asyncio
async def test_stat_root(accessor, index):
result = await stat(accessor, _path("/"), index)
assert result.type == FileType.DIRECTORY
assert result.name == "/"
@pytest.mark.asyncio
async def test_stat_database(accessor, index):
result = await stat(accessor, _path("/sample_mflix"), index)
assert result.type == FileType.DIRECTORY
assert result.name == "sample_mflix"
assert result.extra["database"] == "sample_mflix"
@pytest.mark.asyncio
async def test_stat_collections_kind_dir(accessor, index):
result = await stat(accessor, _path("/sample_mflix/collections"), index)
assert result.type == FileType.DIRECTORY
assert result.name == "collections"
assert result.extra["kind"] == "collection"
@pytest.mark.asyncio
async def test_stat_views_kind_dir(accessor, index):
result = await stat(accessor, _path("/sample_mflix/views"), index)
assert result.type == FileType.DIRECTORY
assert result.name == "views"
assert result.extra["kind"] == "view"
@pytest.mark.asyncio
async def test_stat_entity_collection(accessor, index):
with patch("mirage.core.mongodb.stat.count_documents",
new=AsyncMock(return_value=23519)):
result = await stat(accessor,
_path("/sample_mflix/collections/movies"), index)
assert result.type == FileType.DIRECTORY
assert result.name == "movies"
assert result.extra["kind"] == "collection"
assert result.extra["document_count"] == 23519
@pytest.mark.asyncio
async def test_stat_documents_collection_full_metadata(accessor, index):
fake_indexes = [{"name": "_id_", "key": {"_id": 1}}]
with (
patch("mirage.core.mongodb.stat.is_view",
new=AsyncMock(return_value=False)),
patch("mirage.core.mongodb.stat.count_documents",
new=AsyncMock(return_value=42)),
patch("mirage.core.mongodb.stat.get_indexes",
new=AsyncMock(return_value=fake_indexes)),
):
result = await stat(
accessor,
_path("/sample_mflix/collections/movies/documents.jsonl"), index)
assert result.type == FileType.TEXT
assert result.name == "documents.jsonl"
assert result.extra["kind"] == "collection"
assert result.extra["document_count"] == 42
assert result.extra["indexes"] == [{"name": "_id_", "keys": {"_id": 1}}]
@pytest.mark.asyncio
async def test_stat_documents_view_skips_indexes(accessor, index):
with (
patch(
"mirage.core.mongodb.stat.count_documents",
new=AsyncMock(return_value=17),
),
patch(
"mirage.core.mongodb.stat.get_indexes",
new=AsyncMock(side_effect=AssertionError(
"get_indexes must not be called for views")),
),
):
result = await stat(
accessor, _path("/sample_mflix/views/my_view/documents.jsonl"),
index)
assert result.type == FileType.TEXT
assert result.extra["kind"] == "view"
assert result.extra["indexes"] == []
assert result.extra["document_count"] == 17
@pytest.mark.asyncio
async def test_stat_schema_json(accessor, index):
result = await stat(accessor,
_path("/sample_mflix/collections/movies/schema.json"),
index)
assert result.type == FileType.TEXT
assert result.name == "schema.json"
assert result.extra["kind"] == "collection"
assert result.extra["name"] == "movies"
@pytest.mark.asyncio
async def test_stat_database_json(accessor, index):
result = await stat(accessor, _path("/sample_mflix/database.json"), index)
assert result.type == FileType.TEXT
assert result.name == "database.json"
assert result.extra["database"] == "sample_mflix"
@pytest.mark.asyncio
async def test_stat_unknown_path_raises(accessor, index):
with pytest.raises(FileNotFoundError):
await stat(accessor, _path("/db/something/extra/leaf"), index)
@pytest.mark.asyncio
async def test_stat_database_missing_raises(accessor, index):
with patch(
"mirage.core.mongodb.stat.database_exists",
new_callable=AsyncMock,
return_value=False,
):
with pytest.raises(FileNotFoundError):
await stat(accessor, _path("/ghost"), index)
@pytest.mark.asyncio
async def test_stat_collection_missing_raises(accessor, index):
with patch(
"mirage.core.mongodb.stat.entity_exists",
new_callable=AsyncMock,
return_value=False,
):
with pytest.raises(FileNotFoundError):
await stat(accessor, _path("/sample_mflix/collections/ghost"),
index)
@pytest.mark.asyncio
async def test_stat_documents_under_missing_collection_raises(accessor, index):
with patch(
"mirage.core.mongodb.stat.entity_exists",
new_callable=AsyncMock,
return_value=False,
):
with pytest.raises(FileNotFoundError):
await stat(
accessor,
_path("/sample_mflix/collections/ghost/documents.jsonl"),
index)
+342
View File
@@ -0,0 +1,342 @@
# ========= 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 datetime as dt
import json
from unittest.mock import AsyncMock, patch
import pytest
from bson import Decimal128, ObjectId
from mirage.accessor.mongodb import MongoDBAccessor
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.core.mongodb.stream import read_stream, read_tail, watch_stream
from mirage.resource.mongodb.config import MongoDBConfig
from mirage.types import PathSpec
DOCS_PATH = "/db1/collections/coll1/documents.jsonl"
VIEW_DOCS_PATH = "/db1/views/myview/documents.jsonl"
async def _gen(items):
for item in items:
yield item
@pytest.fixture
def index():
return RAMIndexCacheStore()
@pytest.fixture
def accessor():
return MongoDBAccessor(config=MongoDBConfig(
uri="mongodb://localhost:27017"))
def _patched_iter(docs):
return patch("mirage.core.mongodb.stream.iter_documents",
new=lambda *args, **kwargs: _gen(docs))
def _patched_watch(docs):
return patch("mirage.core.mongodb.stream.iter_inserts",
new=lambda *args, **kwargs: _gen(docs))
def _path(s: str) -> PathSpec:
return PathSpec(virtual=s, directory=s, resource_path=s.strip("/"))
async def _collect(gen):
chunks = []
async for chunk in gen:
chunks.append(chunk)
return b"".join(chunks)
@pytest.mark.asyncio
async def test_read_stream_yields_one_jsonl_line_per_doc(accessor, index):
oid_a, oid_b = ObjectId(), ObjectId()
docs = [{"_id": oid_a, "title": "A"}, {"_id": oid_b, "title": "B"}]
with _patched_iter(docs):
data = await _collect(read_stream(accessor, _path(DOCS_PATH), index))
lines = [line for line in data.decode().split("\n") if line]
assert len(lines) == 2
first = json.loads(lines[0])
assert first["title"] == "A"
assert first["_id"] == {"$oid": str(oid_a)}
@pytest.mark.asyncio
async def test_read_stream_preserves_bson_types_via_extended_json(
accessor, index):
docs = [{
"_id":
ObjectId("65f0000000000000000000a1"),
"date":
dt.datetime(2026, 5, 15, 12, 30, 45, tzinfo=dt.timezone.utc),
"decimal":
Decimal128("123.456"),
}]
with _patched_iter(docs):
data = await _collect(read_stream(accessor, _path(DOCS_PATH), index))
parsed = json.loads(data.decode().strip())
assert parsed["_id"] == {"$oid": "65f0000000000000000000a1"}
assert "$date" in parsed["date"]
assert parsed["decimal"] == {"$numberDecimal": "123.456"}
@pytest.mark.asyncio
async def test_read_stream_empty_yields_nothing(accessor, index):
with _patched_iter([]):
chunks = []
async for chunk in read_stream(accessor, _path(DOCS_PATH), index):
chunks.append(chunk)
assert chunks == []
@pytest.mark.asyncio
async def test_read_stream_short_circuits_when_consumer_closes(
accessor, index):
consumed: list[int] = []
async def _instrumented(*_args, **_kwargs):
for i in range(1000):
consumed.append(i)
yield {"_id": ObjectId(), "i": i}
with patch("mirage.core.mongodb.stream.iter_documents", new=_instrumented):
gen = read_stream(accessor, _path(DOCS_PATH), index)
await gen.__anext__()
await gen.aclose()
assert len(consumed) <= 2
@pytest.mark.asyncio
async def test_read_stream_works_on_view_documents_path(accessor, index):
oid = ObjectId()
docs = [{"_id": oid, "v": 1}]
with _patched_iter(docs):
data = await _collect(
read_stream(accessor, _path(VIEW_DOCS_PATH), index))
parsed = json.loads(data.decode().strip())
assert parsed["v"] == 1
assert parsed["_id"] == {"$oid": str(oid)}
@pytest.mark.asyncio
async def test_read_stream_directory_path_raises(accessor, index):
with _patched_iter([]):
with pytest.raises(FileNotFoundError):
async for _ in read_stream(accessor, _path("/db1"), index):
pass
@pytest.mark.asyncio
async def test_read_stream_schema_json_path_raises(accessor, index):
with _patched_iter([]):
with pytest.raises(FileNotFoundError):
async for _ in read_stream(
accessor, _path("/db1/collections/coll1/schema.json"),
index):
pass
@pytest.mark.asyncio
async def test_read_stream_elides_configured_top_level_field(index):
cfg = MongoDBConfig(
uri="mongodb://localhost:27017",
elide_fields={"db1.coll1": ["vector"]},
)
acc = MongoDBAccessor(config=cfg)
oid = ObjectId()
docs = [{"_id": oid, "title": "hi", "vector": [0.1, 0.2, 0.3, 0.4, 0.5]}]
with _patched_iter(docs):
data = await _collect(read_stream(acc, _path(DOCS_PATH), index))
parsed = json.loads(data.decode().strip())
assert parsed["title"] == "hi"
assert "vector" not in parsed
assert parsed["_id"] == {"$oid": str(oid)}
@pytest.mark.asyncio
async def test_read_stream_elides_configured_nested_path(index):
cfg = MongoDBConfig(
uri="mongodb://localhost:27017",
elide_fields={"db1.coll1": ["metadata.embedding"]},
)
acc = MongoDBAccessor(config=cfg)
docs = [{
"_id": ObjectId(),
"metadata": {
"tag": "alpha",
"embedding": [0.1] * 1024,
},
}]
with _patched_iter(docs):
data = await _collect(read_stream(acc, _path(DOCS_PATH), index))
parsed = json.loads(data.decode().strip())
assert parsed["metadata"]["tag"] == "alpha"
assert "embedding" not in parsed["metadata"]
@pytest.mark.asyncio
async def test_read_stream_elision_isolated_to_configured_collection(index):
cfg = MongoDBConfig(
uri="mongodb://localhost:27017",
elide_fields={"db1.other_coll": ["vector"]},
)
acc = MongoDBAccessor(config=cfg)
docs = [{"_id": ObjectId(), "vector": [1.0, 2.0]}]
with _patched_iter(docs):
data = await _collect(read_stream(acc, _path(DOCS_PATH), index))
parsed = json.loads(data.decode().strip())
assert parsed["vector"] == [1.0, 2.0]
@pytest.mark.asyncio
async def test_watch_stream_yields_one_jsonl_line_per_insert(accessor, index):
oid_a, oid_b = ObjectId(), ObjectId()
docs = [{"_id": oid_a, "title": "A"}, {"_id": oid_b, "title": "B"}]
with _patched_watch(docs):
data = await _collect(watch_stream(accessor, _path(DOCS_PATH), index))
lines = [line for line in data.decode().split("\n") if line]
assert len(lines) == 2
assert json.loads(lines[0])["_id"] == {"$oid": str(oid_a)}
assert json.loads(lines[1])["title"] == "B"
@pytest.mark.asyncio
async def test_watch_stream_preserves_bson_types(accessor, index):
docs = [{
"_id":
ObjectId("65f0000000000000000000a1"),
"date":
dt.datetime(2026, 5, 15, 12, 30, 45, tzinfo=dt.timezone.utc),
"decimal":
Decimal128("123.456"),
}]
with _patched_watch(docs):
data = await _collect(watch_stream(accessor, _path(DOCS_PATH), index))
parsed = json.loads(data.decode().strip())
assert parsed["_id"] == {"$oid": "65f0000000000000000000a1"}
assert "$date" in parsed["date"]
assert parsed["decimal"] == {"$numberDecimal": "123.456"}
@pytest.mark.asyncio
async def test_watch_stream_empty_yields_nothing(accessor, index):
with _patched_watch([]):
chunks = []
async for chunk in watch_stream(accessor, _path(DOCS_PATH), index):
chunks.append(chunk)
assert chunks == []
@pytest.mark.asyncio
async def test_watch_stream_short_circuits_when_consumer_closes(
accessor, index):
consumed: list[int] = []
async def _instrumented(*_args, **_kwargs):
for i in range(1000):
consumed.append(i)
yield {"_id": ObjectId(), "i": i}
with patch("mirage.core.mongodb.stream.iter_inserts", new=_instrumented):
gen = watch_stream(accessor, _path(DOCS_PATH), index)
await gen.__anext__()
await gen.aclose()
assert len(consumed) <= 2
@pytest.mark.asyncio
async def test_watch_stream_directory_path_raises(accessor, index):
with _patched_watch([]):
with pytest.raises(FileNotFoundError):
async for _ in watch_stream(accessor, _path("/db1"), index):
pass
@pytest.mark.asyncio
async def test_read_tail_returns_docs_in_ascending_order(accessor):
docs = [{"_id": 5, "name": "e"}, {"_id": 4, "name": "d"}]
with patch(
"mirage.core.mongodb.stream.find_documents",
new_callable=AsyncMock,
return_value=list(docs),
) as fake:
data = await read_tail(accessor, _path(DOCS_PATH), 2)
lines = data.decode().splitlines()
assert '"_id": 4' in lines[0]
assert '"_id": 5' in lines[1]
assert data.endswith(b"\n")
assert fake.await_args.kwargs["limit"] == 2
assert fake.await_args.kwargs["sort"] == [("_id", -1)]
@pytest.mark.asyncio
async def test_read_tail_caps_limit_at_max_doc_limit(accessor):
with patch(
"mirage.core.mongodb.stream.find_documents",
new_callable=AsyncMock,
return_value=[],
) as fake:
data = await read_tail(accessor, _path(DOCS_PATH), 10**9)
assert data == b""
assert fake.await_args.kwargs["limit"] == accessor.config.max_doc_limit
@pytest.mark.asyncio
async def test_read_tail_applies_elision(index):
cfg = MongoDBConfig(
uri="mongodb://localhost:27017",
elide_fields={"db1.coll1": ["vector"]},
)
acc = MongoDBAccessor(config=cfg)
docs = [{"_id": 1, "title": "hi", "vector": [0.1, 0.2]}]
with patch(
"mirage.core.mongodb.stream.find_documents",
new_callable=AsyncMock,
return_value=docs,
):
data = await read_tail(acc, _path(DOCS_PATH), 1)
parsed = json.loads(data.decode().strip())
assert parsed["title"] == "hi"
assert "vector" not in parsed
@pytest.mark.asyncio
async def test_read_tail_rejects_non_documents_path(accessor):
with pytest.raises(FileNotFoundError):
await read_tail(accessor, _path("/db1/collections/coll1/schema.json"),
5)
@pytest.mark.asyncio
async def test_watch_stream_applies_elision(index):
cfg = MongoDBConfig(
uri="mongodb://localhost:27017",
elide_fields={"db1.coll1": ["vector"]},
)
acc = MongoDBAccessor(config=cfg)
oid = ObjectId()
docs = [{"_id": oid, "title": "live", "vector": [0.1, 0.2, 0.3]}]
with _patched_watch(docs):
data = await _collect(watch_stream(acc, _path(DOCS_PATH), index))
parsed = json.loads(data.decode().strip())
assert parsed["title"] == "live"
assert "vector" not in parsed
assert parsed["_id"] == {"$oid": str(oid)}
@@ -0,0 +1,102 @@
# ========= 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 json
import os
import time
from pathlib import Path
import pytest
from dotenv import load_dotenv
from mirage.accessor.mongodb import MongoDBAccessor
from mirage.core.mongodb.stream import read_stream
from mirage.resource.mongodb.config import MongoDBConfig
from mirage.types import PathSpec
pytestmark = pytest.mark.skipif(
os.environ.get("MIRAGE_RUN_INTEGRATION_MONGO") != "1",
reason="integration test (set MIRAGE_RUN_INTEGRATION_MONGO=1 to enable)",
)
def _load_env() -> str:
repo_root = Path(__file__).resolve().parents[4]
load_dotenv(repo_root / ".env.development")
uri = os.environ.get("MONGODB_URI")
if not uri:
pytest.skip("MONGODB_URI not set in .env.development")
return uri
@pytest.fixture
def accessor():
uri = _load_env()
return MongoDBAccessor(config=MongoDBConfig(uri=uri))
async def _collect_lines(gen) -> list[str]:
chunks = []
async for chunk in gen:
chunks.append(chunk)
text = b"".join(chunks).decode()
return [line for line in text.split("\n") if line]
@pytest.mark.asyncio
async def test_extended_json_for_bson_types(accessor):
path = PathSpec(resource_path="mirage_test/bson_types.jsonl",
virtual="/mirage_test/bson_types.jsonl",
directory="/mirage_test/bson_types.jsonl")
lines = await _collect_lines(read_stream(accessor, path))
assert len(lines) == 5
by_label = {json.loads(line)["label"]: json.loads(line) for line in lines}
scalars = by_label["scalars"]
assert scalars["_id"] == {"$oid": "65f0000000000000000000a1"}
assert scalars["decimal"] == {"$numberDecimal": "123456789.987654321"}
assert scalars["int64"] == 1099511627776
assert scalars["null"] is None
temporal = by_label["temporal"]
assert "$date" in temporal["date_utc"]
binary_and_regex = by_label["binary_and_regex"]
assert "$binary" in binary_and_regex["binary"]
nested = by_label["nested"]
assert nested["metadata"]["nested"]["leaf"] == {
"$oid": "65f0000000000000000000b1"
}
@pytest.mark.asyncio
async def test_streams_full_5000_docs_without_cap(accessor):
path = PathSpec(resource_path="mirage_test/streaming_large.jsonl",
virtual="/mirage_test/streaming_large.jsonl",
directory="/mirage_test/streaming_large.jsonl")
count = 0
async for chunk in read_stream(accessor, path, batch_size=500):
count += chunk.count(b"\n")
assert count == 5000
@pytest.mark.asyncio
async def test_short_circuit_when_only_first_doc_consumed(accessor):
path = PathSpec(resource_path="mirage_test/streaming_large.jsonl",
virtual="/mirage_test/streaming_large.jsonl",
directory="/mirage_test/streaming_large.jsonl")
start = time.monotonic()
gen = read_stream(accessor, path, batch_size=100)
first = await gen.__anext__()
await gen.aclose()
elapsed = time.monotonic() - start
assert first
assert elapsed < 5.0