7d958121ad
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 16s
CI / build (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 23s
CI / security (pull_request) Successful in 35s
CI / typecheck (pull_request) Successful in 43s
CI / unit_tests (pull_request) Successful in 2m40s
CI / integration_tests (pull_request) Successful in 3m10s
CI / docker (pull_request) Successful in 40s
CI / coverage (pull_request) Successful in 5m1s
CI / benchmark-regression (pull_request) Successful in 31m13s
Add RepoIndexingService with incremental refresh, language detection, SHA-256 hashing, and policy enforcement. Includes domain models, DB persistence, DI wiring, Behave/Robot/ASV tests, and reference docs. ISSUES CLOSED: #195
194 lines
5.8 KiB
Python
194 lines
5.8 KiB
Python
"""Robot Framework helper for repo indexing integration tests.
|
|
|
|
Verifies that the RepoIndexingService can index a directory, refresh
|
|
incrementally, enforce policy limits, and clean up on removal.
|
|
|
|
Usage:
|
|
python robot/helper_repo_indexing.py full-index
|
|
python robot/helper_repo_indexing.py incremental-refresh
|
|
python robot/helper_repo_indexing.py policy-enforcement
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import shutil
|
|
import sys
|
|
import tempfile
|
|
from collections.abc import Callable
|
|
from pathlib import Path
|
|
|
|
_SRC = str(Path(__file__).resolve().parents[1] / "src")
|
|
if _SRC not in sys.path:
|
|
sys.path.insert(0, _SRC)
|
|
|
|
from sqlalchemy import create_engine # noqa: E402
|
|
from sqlalchemy.orm import sessionmaker # noqa: E402
|
|
|
|
from cleveragents.application.services.repo_indexing_service import ( # noqa: E402
|
|
RepoIndexingService,
|
|
)
|
|
from cleveragents.infrastructure.database.models import Base # noqa: E402
|
|
|
|
_RID = "01KK0D8WNATFNEX2JMG5GKF6FQ"
|
|
|
|
|
|
def _make_service() -> RepoIndexingService:
|
|
engine = create_engine("sqlite:///:memory:", echo=False)
|
|
Base.metadata.create_all(engine)
|
|
factory = sessionmaker(bind=engine, expire_on_commit=False)
|
|
return RepoIndexingService(session_factory=factory)
|
|
|
|
|
|
def _make_sample_dir() -> str:
|
|
tmpdir = tempfile.mkdtemp()
|
|
(Path(tmpdir) / "app.py").write_text("print('hello')\n")
|
|
(Path(tmpdir) / "lib.py").write_text("def f(): return 1\n")
|
|
(Path(tmpdir) / "README.md").write_text("# App\n")
|
|
return tmpdir
|
|
|
|
|
|
def _cmd_full_index() -> int:
|
|
svc = _make_service()
|
|
tmpdir = _make_sample_dir()
|
|
try:
|
|
idx = svc.index_resource(_RID, tmpdir)
|
|
if idx.metadata.status.value != "ready":
|
|
print(f"FAIL: status={idx.metadata.status.value}")
|
|
return 1
|
|
if idx.metadata.file_count != 3:
|
|
print(f"FAIL: file_count={idx.metadata.file_count}")
|
|
return 1
|
|
if idx.metadata.primary_language != "python":
|
|
print(f"FAIL: language={idx.metadata.primary_language}")
|
|
return 1
|
|
|
|
# Verify persistence
|
|
retrieved = svc.get_index(_RID)
|
|
if retrieved is None:
|
|
print("FAIL: get_index returned None")
|
|
return 1
|
|
if retrieved.metadata.file_count != 3:
|
|
print(f"FAIL: retrieved file_count={retrieved.metadata.file_count}")
|
|
return 1
|
|
|
|
print("repo-indexing-full-ok")
|
|
return 0
|
|
finally:
|
|
shutil.rmtree(tmpdir, ignore_errors=True)
|
|
|
|
|
|
def _cmd_incremental_refresh() -> int:
|
|
svc = _make_service()
|
|
tmpdir = _make_sample_dir()
|
|
try:
|
|
# Initial index
|
|
original = svc.index_resource(_RID, tmpdir)
|
|
orig_hash = next(
|
|
(f.content_hash for f in original.files if f.path == "app.py"),
|
|
None,
|
|
)
|
|
if orig_hash is None:
|
|
print("FAIL: app.py not found in original index")
|
|
return 1
|
|
|
|
# Modify a file
|
|
(Path(tmpdir) / "app.py").write_text("print('updated')\n# more\n")
|
|
|
|
# Refresh
|
|
refreshed = svc.refresh_index(_RID, tmpdir)
|
|
new_hash = next(
|
|
(f.content_hash for f in refreshed.files if f.path == "app.py"),
|
|
None,
|
|
)
|
|
if new_hash is None:
|
|
print("FAIL: app.py not found in refreshed index")
|
|
return 1
|
|
|
|
if orig_hash == new_hash:
|
|
print("FAIL: hash should have changed")
|
|
return 1
|
|
|
|
# Unchanged file
|
|
orig_lib = next(
|
|
(f.content_hash for f in original.files if f.path == "lib.py"),
|
|
None,
|
|
)
|
|
new_lib = next(
|
|
(f.content_hash for f in refreshed.files if f.path == "lib.py"),
|
|
None,
|
|
)
|
|
if orig_lib is None or new_lib is None:
|
|
print("FAIL: lib.py not found in index")
|
|
return 1
|
|
if orig_lib != new_lib:
|
|
print("FAIL: unchanged file hash changed")
|
|
return 1
|
|
|
|
print("repo-indexing-refresh-ok")
|
|
return 0
|
|
finally:
|
|
shutil.rmtree(tmpdir, ignore_errors=True)
|
|
|
|
|
|
def _cmd_policy_enforcement() -> int:
|
|
svc = _make_service()
|
|
tmpdir = _make_sample_dir()
|
|
try:
|
|
(Path(tmpdir) / "big.dat").write_bytes(b"x" * 2_000_000)
|
|
# Max file size
|
|
idx = svc.index_resource(
|
|
"01KK0D8WNATFNEX2JMG5GKF6FR", tmpdir, max_file_size=1_000_000
|
|
)
|
|
paths = [f.path for f in idx.files]
|
|
if "big.dat" in paths:
|
|
print("FAIL: big.dat should be excluded by max_file_size")
|
|
return 1
|
|
|
|
# Include globs
|
|
idx2 = svc.index_resource(
|
|
"01KK0DAE56DZVRZKRHA53K1MQG", tmpdir, include_globs=("*.py",)
|
|
)
|
|
if idx2.metadata.file_count != 2:
|
|
print(f"FAIL: include_globs *.py got {idx2.metadata.file_count} files")
|
|
return 1
|
|
|
|
# Removal
|
|
removed = svc.remove_index("01KK0D8WNATFNEX2JMG5GKF6FR")
|
|
if not removed:
|
|
print("FAIL: remove_index returned False")
|
|
return 1
|
|
if svc.get_index("01KK0D8WNATFNEX2JMG5GKF6FR") is not None:
|
|
print("FAIL: index still exists after removal")
|
|
return 1
|
|
|
|
print("repo-indexing-policy-ok")
|
|
return 0
|
|
finally:
|
|
shutil.rmtree(tmpdir, ignore_errors=True)
|
|
|
|
|
|
_COMMANDS: dict[str, Callable[[], int]] = {
|
|
"full-index": _cmd_full_index,
|
|
"incremental-refresh": _cmd_incremental_refresh,
|
|
"policy-enforcement": _cmd_policy_enforcement,
|
|
}
|
|
|
|
|
|
def main() -> int:
|
|
if len(sys.argv) < 2:
|
|
print(
|
|
"Usage: helper_repo_indexing.py "
|
|
"<full-index|incremental-refresh|policy-enforcement>"
|
|
)
|
|
return 1
|
|
command = sys.argv[1]
|
|
handler = _COMMANDS.get(command)
|
|
if handler is None:
|
|
print(f"Unknown command: {command}")
|
|
return 1
|
|
return handler()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|