from __future__ import annotations import multiprocessing import subprocess from pathlib import Path import pytest import tools.rag.rebuild_index as rebuild import tools.sync.wiki_git as wiki_git 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 root: calls.append("git_pull"), ) import tools.sync.sync_indexes as sync monkeypatch.setattr( sync, "synchronize_indexes", lambda wiki, data: calls.append("sqlite_and_neo4j") or { "documents_scanned": 1, "chunks_created": 1, "counts": {"fts_chunks": 1}, "index_version": {"version": "both"}, }, ) result = rebuild.rebuild_index( pull_git=True, wiki_root=tmp_path / "wiki", data_dir=tmp_path / "data", lock_file=tmp_path / "lock", ) assert calls == ["git_pull", "sqlite_and_neo4j"] assert result["documents_scanned"] == 1 assert result["chunks_created"] == 1 assert result["counts"]["fts_chunks"] == 1 assert result["index_version"] == {"version": "both"} def test_rebuild_pulls_and_locks_the_requested_snapshot_paths(tmp_path, monkeypatch): import tools.sync.sync_indexes as sync wiki = tmp_path / "alternate-wiki" data = tmp_path / "alternate-data" seen = [] monkeypatch.setattr(rebuild, "git_pull", lambda root: seen.append(("pull", root))) def synchronized(root, target): assert (data / ".reindex.lock").exists() seen.append(("sync", root, target)) return {"index_version": {"version": "alternate"}} monkeypatch.setattr(sync, "synchronize_indexes", synchronized) result = rebuild.rebuild_index(pull_git=True, wiki_root=wiki, data_dir=data) assert seen == [("pull", wiki), ("sync", wiki, data)] assert result["index_version"]["version"] == "alternate" def test_git_pull_uses_explicit_branch_and_fast_forward_only(tmp_path, monkeypatch): repo = tmp_path / "repo" (repo / ".git").mkdir(parents=True) captured = [] def fake_run(args, **kwargs): captured.append((args, kwargs)) output = { "remote": "git@git.example:KEMT/zpwiki.git", "branch": "master", "status": "", }.get(args[1], "") return subprocess.CompletedProcess(args, 0, stdout=output, stderr="") monkeypatch.setattr(wiki_git.subprocess, "run", fake_run) monkeypatch.setenv("EXPECTED_GITEA_BRANCH", "master") rebuild.git_pull(repo) assert captured[-2][0] == [ "git", "fetch", "--no-tags", "--no-recurse-submodules", "origin", "refs/heads/master:refs/remotes/origin/master", ] assert captured[-1][0] == ["git", "merge", "--ff-only", "refs/remotes/origin/master"] assert all( kw["cwd"] == repo and kw["timeout"] == 120 and kw["env"]["GIT_TERMINAL_PROMPT"] == "0" for _, kw in captured ) 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(wiki_git.subprocess, "run", timeout) with pytest.raises(RuntimeError, match="prekročila 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( wiki_git.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)