ReMe/tests/unit/test_reindex.py
Ziyang Guo a99459ac93
Some checks are pending
CI / Python packages / Build and verify distributions (push) Waiting to run
CI / Python quality / GitHub Actions (push) Waiting to run
CI / Python tests / Unit Tests - py3.11 (push) Waiting to run
CI / Python tests / Unit Tests - py3.12 (push) Waiting to run
CI / Windows / CLI smoke - py3.11 (push) Waiting to run
CI and Release / Docker / Build and test / amd64 (push) Waiting to run
CI and Release / Docker / Publish multi-platform tags (push) Blocked by required conditions
Security / CodeQL / Analyze javascript-typescript (push) Waiting to run
CI / Python quality / Pre-commit (push) Waiting to run
CI / Python tests / Unit Tests - py3.13 (push) Waiting to run
CI / Python tests / Unit Tests - py3.14 (push) Waiting to run
CI and Release / Docker / Build and test / arm64 (push) Waiting to run
Security / CodeQL / Analyze python (push) Waiting to run
fix(reindex): return a readable summary so Studio stops showing a placeholder (#565)
* fix(reindex): return a readable summary so Studio stops showing a placeholder

`ReindexStep` assigned the store's report dict straight to
`response.answer`. ReMe Studio's settings panel already anticipates a string
answer:

    typeof result.answer === "string" && result.answer ? result.answer : t("indexRebuilt")

(`reme_studio/app/settings-center.tsx`), so with a dict answer that branch was
unreachable and every rebuild reported the generic "index rebuilt" text. The
operator never saw which scopes ran or how many entries each indexed.

Render the report as one summary line — `Reindexed scope=all: bm25=7,
embedding=5, tag=2` for the composite scope and `Reindexed scope=bm25: 3
indexed` for a single one. The report itself still lands in metadata, so
programmatic consumers are unaffected, and Studio needs no change because its
string branch now fires.

`reindex` is the only answer-carrying consumer of this step; no test asserted
the answer content, only metadata, so nothing else moved.

* chore(reindex): tighten the renderer and deduplicate the summary test

Review on #565: the helper's docstring carried the Studio background
that already lives in the PR description; the intermediate per_scope
dict collapses into the join generator; and the all-scope delegation
test now uses distinct counts (7, 5, 2) so it alone pins both the exact
summary answer and the full metadata report, replacing the overlapping
summary test.

* refactor(reindex): share summary return formatting

---------

Co-authored-by: jinli.yl <jinli.yl@alibaba-inc.com>
2026-10-14 11:25:48 +08:00

53 lines
2 KiB
Python

"""Tests for explicit scoped index rebuilds."""
from unittest.mock import AsyncMock
import pytest
from reme.components.file_store import LocalFileStore
from reme.components.runtime_context import RuntimeContext
from reme.config import resolve_app_config
from reme.steps.index import ReindexStep
@pytest.mark.asyncio
@pytest.mark.parametrize("scope", ["bm25", "embedding", "tag"])
async def test_reindex_step_delegates_scope(scope):
"""The step forwards each individual scope without clearing the store."""
store = LocalFileStore(name=f"test_reindex_{scope}", embedding_store="")
store.reindex = AsyncMock(return_value={"indexed": 3, "scope": scope})
store.clear = AsyncMock()
response = await ReindexStep(file_store=store)(RuntimeContext(scope=scope))
store.reindex.assert_awaited_once_with(scope)
store.clear.assert_not_called()
assert response.metadata == {"indexed": 3, "scope": scope}
assert response.answer == f"Reindexed scope={scope}: 3 indexed"
@pytest.mark.asyncio
async def test_reindex_step_delegates_all_once():
"""The step delegates the composite scope once; the answer summarizes it."""
store = LocalFileStore(name="test_reindex_all", embedding_store="")
details = {
"scope": "all",
"bm25": {"scope": "bm25", "indexed": 7},
"embedding": {"scope": "embedding", "indexed": 5},
"tag": {"scope": "tag", "indexed": 2},
}
store.reindex = AsyncMock(return_value=details)
response = await ReindexStep(file_store=store)(RuntimeContext(scope="all"))
store.reindex.assert_awaited_once_with("all")
assert response.metadata == details
assert response.answer == "Reindexed scope=all: bm25=7, embedding=5, tag=2"
def test_reindex_job_schema_exposes_tag_scope():
"""The public job contract accepts every scope implemented by the file store."""
config = resolve_app_config(config="default", log_config=False)
scope = config["jobs"]["reindex"]["parameters"]["properties"]["scope"]
assert scope["enum"] == ["all", "bm25", "embedding", "tag"]