155 lines
4.8 KiB
Python
155 lines
4.8 KiB
Python
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,
|
|
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)
|