4f950a1600
CI / lint (push) Successful in 24s
CI / build (push) Successful in 16s
CI / typecheck (push) Successful in 38s
CI / quality (push) Successful in 43s
CI / security (push) Successful in 53s
CI / benchmark-regression (push) Has been skipped
CI / integration_tests (push) Successful in 2m39s
CI / unit_tests (push) Successful in 3m58s
CI / docker (push) Successful in 1m8s
CI / e2e_tests (push) Successful in 5m21s
CI / coverage (push) Successful in 6m31s
CI / benchmark-publish (push) Successful in 19m39s
## Summary Add `agents repo index` and `agents repo status` CLI commands, wiring the existing `RepoIndexingService` backend to the CLI layer. ### Commands - **`agents repo index <resource_name>`** — Triggers full or incremental indexing of a repository resource. Options: `--full` (force full re-index), `--format text|json` - **`agents repo status <resource_name>`** — Displays indexing metadata: status, file count, token estimate, primary language, last indexed timestamp. Options: `--format text|json` ### Implementation - New CLI module `src/cleveragents/cli/commands/repo.py` (238 lines) - Registered as `repo` subcommand group in `cli/main.py` - Resolves resource by name via `ResourceRegistryService` - Calls `RepoIndexingService.index_resource()` / `refresh_index()` / `get_index_status()` - Both text (Rich panel) and JSON output formats ### Quality Gates | Session | Result | |---|---| | `nox -s lint` | PASS | | `nox -s typecheck` | PASS (0 errors) | | `nox -s unit_tests` | PASS (10,819 scenarios) | | `nox -s coverage_report` | 98% (>= 97%) | | Integration tests | 5/5 PASS | Closes #856 Reviewed-on: #982 Co-authored-by: Brent Edwards <brent.edwards@cleverthis.com> Co-committed-by: Brent Edwards <brent.edwards@cleverthis.com>
185 lines
5.5 KiB
Python
185 lines
5.5 KiB
Python
"""Robot Framework helper for repo indexing CLI integration tests.
|
|
|
|
Provides end-to-end test scenarios that exercise the RepoIndexingService
|
|
and CLI wiring together, using an in-memory SQLite database.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import sessionmaker
|
|
|
|
from cleveragents.application.services.repo_indexing_service import (
|
|
RepoIndexingService,
|
|
)
|
|
from cleveragents.application.services.resource_registry_service import (
|
|
ResourceRegistryService,
|
|
)
|
|
from cleveragents.infrastructure.database.models import Base
|
|
|
|
|
|
def _make_services() -> tuple[ResourceRegistryService, RepoIndexingService]:
|
|
"""Create fresh services backed by an in-memory database."""
|
|
engine = create_engine("sqlite:///:memory:", echo=False)
|
|
Base.metadata.create_all(engine)
|
|
factory = sessionmaker(bind=engine, expire_on_commit=False)
|
|
resource_svc = ResourceRegistryService(session_factory=factory)
|
|
resource_svc.bootstrap_builtin_types()
|
|
indexing_svc = RepoIndexingService(session_factory=factory)
|
|
return resource_svc, indexing_svc
|
|
|
|
|
|
def test_e2e_add_index_status() -> str:
|
|
"""End-to-end: add resource -> index -> check status."""
|
|
resource_svc, indexing_svc = _make_services()
|
|
|
|
# Create a temp directory with sample files
|
|
tmp_dir = tempfile.mkdtemp()
|
|
readme = Path(tmp_dir) / "README.md"
|
|
readme.write_text("# Test\nSample content.\n")
|
|
main_py = Path(tmp_dir) / "main.py"
|
|
main_py.write_text("print('hello')\n")
|
|
|
|
# Register resource
|
|
resource = resource_svc.register_resource(
|
|
type_name="git-checkout",
|
|
name="local/e2e-repo",
|
|
location=tmp_dir,
|
|
properties={"path": tmp_dir},
|
|
)
|
|
|
|
# Index
|
|
repo_index = indexing_svc.index_resource(resource.resource_id, tmp_dir)
|
|
assert repo_index.metadata.status.value == "ready"
|
|
assert repo_index.metadata.file_count >= 2
|
|
|
|
# Check status
|
|
status = indexing_svc.get_index_status(resource.resource_id)
|
|
assert status is not None
|
|
assert status.status.value == "ready"
|
|
assert status.file_count == repo_index.metadata.file_count
|
|
|
|
return "E2E add-index-status PASSED"
|
|
|
|
|
|
def test_incremental_reindex_after_change() -> str:
|
|
"""Incremental re-index after file change."""
|
|
resource_svc, indexing_svc = _make_services()
|
|
|
|
tmp_dir = tempfile.mkdtemp()
|
|
readme = Path(tmp_dir) / "README.md"
|
|
readme.write_text("# Original\n")
|
|
|
|
resource = resource_svc.register_resource(
|
|
type_name="git-checkout",
|
|
name="local/incr-repo",
|
|
location=tmp_dir,
|
|
properties={"path": tmp_dir},
|
|
)
|
|
|
|
# Initial index
|
|
idx1 = indexing_svc.index_resource(resource.resource_id, tmp_dir)
|
|
count1 = idx1.metadata.file_count
|
|
|
|
# Add a new file
|
|
new_file = Path(tmp_dir) / "app.py"
|
|
new_file.write_text("x = 1\n")
|
|
|
|
# Incremental refresh
|
|
idx2 = indexing_svc.refresh_index(resource.resource_id, tmp_dir)
|
|
assert idx2.metadata.file_count == count1 + 1
|
|
assert idx2.metadata.status.value == "ready"
|
|
|
|
return "Incremental reindex PASSED"
|
|
|
|
|
|
def test_full_reindex() -> str:
|
|
"""Full re-index replaces the entire index."""
|
|
resource_svc, indexing_svc = _make_services()
|
|
|
|
tmp_dir = tempfile.mkdtemp()
|
|
readme = Path(tmp_dir) / "README.md"
|
|
readme.write_text("# Test\n")
|
|
|
|
resource = resource_svc.register_resource(
|
|
type_name="git-checkout",
|
|
name="local/full-repo",
|
|
location=tmp_dir,
|
|
properties={"path": tmp_dir},
|
|
)
|
|
|
|
# Initial index
|
|
idx1 = indexing_svc.index_resource(resource.resource_id, tmp_dir)
|
|
|
|
# Full re-index
|
|
idx2 = indexing_svc.index_resource(resource.resource_id, tmp_dir)
|
|
assert idx2.metadata.status.value == "ready"
|
|
assert idx2.metadata.file_count == idx1.metadata.file_count
|
|
|
|
return "Full reindex PASSED"
|
|
|
|
|
|
def test_status_no_index() -> str:
|
|
"""Status returns None when no index exists."""
|
|
resource_svc, indexing_svc = _make_services()
|
|
|
|
tmp_dir = tempfile.mkdtemp()
|
|
|
|
resource = resource_svc.register_resource(
|
|
type_name="git-checkout",
|
|
name="local/no-index-repo",
|
|
location=tmp_dir,
|
|
properties={"path": tmp_dir},
|
|
)
|
|
|
|
status = indexing_svc.get_index_status(resource.resource_id)
|
|
assert status is None
|
|
|
|
return "Status no-index PASSED"
|
|
|
|
|
|
def test_remove_index() -> str:
|
|
"""Remove index cleans up all data."""
|
|
resource_svc, indexing_svc = _make_services()
|
|
|
|
tmp_dir = tempfile.mkdtemp()
|
|
readme = Path(tmp_dir) / "README.md"
|
|
readme.write_text("# Test\n")
|
|
|
|
resource = resource_svc.register_resource(
|
|
type_name="git-checkout",
|
|
name="local/remove-repo",
|
|
location=tmp_dir,
|
|
properties={"path": tmp_dir},
|
|
)
|
|
|
|
indexing_svc.index_resource(resource.resource_id, tmp_dir)
|
|
assert indexing_svc.get_index_status(resource.resource_id) is not None
|
|
|
|
removed = indexing_svc.remove_index(resource.resource_id)
|
|
assert removed is True
|
|
assert indexing_svc.get_index_status(resource.resource_id) is None
|
|
|
|
return "Remove index PASSED"
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
|
|
_dispatch = {
|
|
"e2e": test_e2e_add_index_status,
|
|
"incremental": test_incremental_reindex_after_change,
|
|
"full": test_full_reindex,
|
|
"no-index": test_status_no_index,
|
|
"remove": test_remove_index,
|
|
}
|
|
if len(sys.argv) < 2 or sys.argv[1] not in _dispatch:
|
|
# Run all tests
|
|
for fn in _dispatch.values():
|
|
print(fn())
|
|
else:
|
|
print(_dispatch[sys.argv[1]]())
|