from __future__ import annotations import multiprocessing import subprocess from pathlib import Path import pytest import scripts.rebuild_index as rebuild def hold_lock(lock_path: str, ready, release) -> None: with rebuild.acquire_reindex_lock(Path(lock_path)): ready.set() release.wait(timeout=10) def test_only_one_process_can_reindex(tmp_path: Path) -> None: lock_file = tmp_path / "reindex.lock" context = multiprocessing.get_context("fork") ready = context.Event() release = context.Event() process = context.Process( target=hold_lock, args=(str(lock_file), ready, release), ) process.start() try: assert ready.wait(timeout=5) with pytest.raises( rebuild.ReindexInProgressError, match="už prebieha", ): with rebuild.acquire_reindex_lock(lock_file): pass finally: release.set() process.join(timeout=5) assert process.exitcode == 0 with rebuild.acquire_reindex_lock(lock_file): pass def test_rebuild_runs_pipeline_in_correct_order( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: calls: list[str] = [] monkeypatch.setattr( rebuild, "git_pull", lambda: calls.append("git_pull"), ) monkeypatch.setattr( rebuild, "scan_pages", lambda: calls.append("scan") or [{"path": "a"}], ) monkeypatch.setattr( rebuild, "build_chunks", lambda: calls.append("chunks") or [{"chunk_id": "a::0"}], ) monkeypatch.setattr( rebuild, "build_database", lambda: calls.append("database") or { "documents": 1, "chunks": 1, "fts_chunks": 1, "tags": 0, "categories": 0, }, ) result = rebuild.rebuild_index( pull_git=True, lock_file=tmp_path / "lock", ) assert calls == ["git_pull", "scan", "chunks", "database"] assert result["documents_scanned"] == 1 assert result["chunks_created"] == 1 assert result["counts"]["fts_chunks"] == 1 def test_git_pull_uses_fast_forward_only( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: repo = tmp_path / "repo" (repo / ".git").mkdir(parents=True) captured = {} def fake_run(args, **kwargs): captured["args"] = args captured["kwargs"] = kwargs return subprocess.CompletedProcess(args, 0, stdout="OK", stderr="") monkeypatch.setattr(rebuild.subprocess, "run", fake_run) rebuild.git_pull(repo) assert captured["args"] == ["git", "pull", "--ff-only"] assert captured["kwargs"]["cwd"] == repo assert captured["kwargs"]["timeout"] == rebuild.GIT_PULL_TIMEOUT_SECONDS def test_git_pull_rejects_non_repository(tmp_path: Path) -> None: directory = tmp_path / "not-repo" directory.mkdir() with pytest.raises(RuntimeError, match="Nie je to git repozitár"): rebuild.git_pull(directory) def test_git_pull_reports_timeout( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: repo = tmp_path / "repo" (repo / ".git").mkdir(parents=True) def timeout(*args, **kwargs): raise subprocess.TimeoutExpired(cmd="git pull", timeout=120) monkeypatch.setattr(rebuild.subprocess, "run", timeout) with pytest.raises(RuntimeError, match="prekročil limit"): rebuild.git_pull(repo) def test_git_pull_reports_nonzero_exit( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: repo = tmp_path / "repo" (repo / ".git").mkdir(parents=True) monkeypatch.setattr( rebuild.subprocess, "run", lambda *args, **kwargs: subprocess.CompletedProcess( args[0], 1, stdout="", stderr="chyba", ), ) with pytest.raises(RuntimeError, match="návratovým kódom 1"): rebuild.git_pull(repo)