Files
stack/tests/cli/test_llm_index_sealed.py
kert 066b304111 fix(llm): migrate before docket_complete; completion requires zero unindexable items (refs #615)
`stack llm index` read index_docket_state before anything had created
it: migrate() only ran inside index_refs, so the first sealed run on an
un-migrated database died with UndefinedTable. Build one engine in the
CLI, migrate it, and hand it down (index_refs takes an `engine` kwarg);
--force still builds nothing.

index_refs also marked a sealed docket complete when some of its refs
loaded blank or produced no chunks — a seal claiming coverage the run
never achieved. Count unindexable refs per docket and write
index_docket_state only for dockets with none.
2026-09-08 15:31:22 -04:00

152 lines
4.7 KiB
Python

"""Exercise cli/llm.py's `index` seal-skip wiring: sealed/complete dockets
are excluded from the comment listing, and only pending seals are passed
through to be re-marked."""
from __future__ import annotations
from unittest.mock import MagicMock, patch
from typer.testing import CliRunner
from cli.llm import app
runner = CliRunner()
_STATS = {
"indexed": 0,
"skipped": 0,
"chunks": 0,
"fingerprint_skipped": 5,
"hash_skipped": 0,
"docket_complete": 0,
}
@patch("llm.index._engine")
@patch(
"llm.index.docket_complete",
return_value={"CMS-2019-0111": "s1", "CMS-2020-0088": "old"},
)
@patch("llm.source.iter_comment_refs")
@patch("llm.pool.HostPool.from_config")
@patch("llm.index.index_refs")
@patch("conf.connect.bib")
@patch("llm.config.load")
def test_complete_sealed_dockets_are_not_listed(
mock_load, mock_bib, mock_index, mock_pool, mock_iter, mock_complete, _engine
):
mock_load.return_value = MagicMock()
store = MagicMock()
store.sealed_dockets.return_value = {"CMS-2019-0111": "s1", "CMS-2020-0088": "s2"}
mock_bib.return_value = store
mock_iter.return_value = iter([])
mock_index.return_value = _STATS
result = runner.invoke(app, ["index"])
assert result.exit_code == 0, result.output
kwargs = mock_iter.call_args.kwargs
assert kwargs["skip_dockets"] == {"CMS-2019-0111"} # seal matches → skipped
ikw = mock_index.call_args.kwargs
assert ikw["sealed"] == {
"CMS-2020-0088": "s2"
} # re-sealed docket will be re-marked
assert ikw["mark_complete"] is True
assert "fp_skipped=5" in result.output
@patch("llm.index._engine")
@patch("llm.index.docket_complete", return_value={})
@patch("llm.source.iter_comment_refs")
@patch("llm.pool.HostPool.from_config")
@patch("llm.index.index_refs")
@patch("conf.connect.bib")
@patch("llm.config.load")
def test_force_and_limit_disable_skips_and_completion(
mock_load, mock_bib, mock_index, mock_pool, mock_iter, mock_complete, _engine
):
mock_load.return_value = MagicMock()
store = MagicMock()
store.sealed_dockets.return_value = {"CMS-2019-0111": "s1"}
mock_bib.return_value = store
mock_iter.return_value = iter([])
mock_index.return_value = _STATS
result = runner.invoke(app, ["index", "--force", "--limit", "5"])
assert result.exit_code == 0, result.output
assert mock_iter.call_args.kwargs["skip_dockets"] == set()
assert mock_index.call_args.kwargs["mark_complete"] is False
@patch("llm.index._engine")
@patch("llm.migrate.migrate")
@patch("llm.index.docket_complete", return_value={})
@patch("llm.source.iter_comment_refs")
@patch("llm.pool.HostPool.from_config")
@patch("llm.index.index_refs")
@patch("conf.connect.bib")
@patch("llm.config.load")
def test_migrate_runs_before_docket_complete_and_engine_is_reused(
mock_load,
mock_bib,
mock_index,
mock_pool,
mock_iter,
mock_complete,
mock_migrate,
mock_engine,
):
"""index_docket_state may not exist yet on the first sealed run."""
order: list[str] = []
mock_migrate.side_effect = lambda e: order.append("migrate")
mock_complete.side_effect = lambda e, c: (order.append("complete"), {})[1]
mock_load.return_value = MagicMock()
store = MagicMock()
store.sealed_dockets.return_value = {"CMS-2019-0111": "s1"}
mock_bib.return_value = store
mock_iter.return_value = iter([])
mock_index.return_value = _STATS
result = runner.invoke(app, ["index"])
assert result.exit_code == 0, result.output
assert order == ["migrate", "complete"]
mock_engine.assert_called_once() # one engine, shared
assert mock_migrate.call_args.args[0] is mock_engine.return_value
assert mock_complete.call_args.args[0] is mock_engine.return_value
assert mock_index.call_args.kwargs["engine"] is mock_engine.return_value
@patch("llm.index._engine")
@patch("llm.migrate.migrate")
@patch("llm.index.docket_complete", return_value={})
@patch("llm.source.iter_comment_refs")
@patch("llm.pool.HostPool.from_config")
@patch("llm.index.index_refs")
@patch("conf.connect.bib")
@patch("llm.config.load")
def test_force_touches_neither_engine_nor_migrate(
mock_load,
mock_bib,
mock_index,
mock_pool,
mock_iter,
mock_complete,
mock_migrate,
mock_engine,
):
mock_load.return_value = MagicMock()
store = MagicMock()
store.sealed_dockets.return_value = {"CMS-2019-0111": "s1"}
mock_bib.return_value = store
mock_iter.return_value = iter([])
mock_index.return_value = _STATS
result = runner.invoke(app, ["index", "--force"])
assert result.exit_code == 0, result.output
mock_engine.assert_not_called()
mock_migrate.assert_not_called()
mock_complete.assert_not_called()
assert mock_index.call_args.kwargs["engine"] is None