generated from coulomb/repo-seed
Transfered deep scope functionality from the custodian
This commit is contained in:
138
tests/test_scope_generator.py
Normal file
138
tests/test_scope_generator.py
Normal file
@@ -0,0 +1,138 @@
|
||||
from repo_registry.core.service import RegistryService
|
||||
from repo_registry.repo_ingestion.git import GitIngestionService
|
||||
from repo_registry.scope.generator import SCOPE_SECTIONS, ScopeGenerator
|
||||
from repo_registry.scope.validator import ScopeValidator
|
||||
from repo_registry.storage.sqlite import RegistryStore
|
||||
|
||||
|
||||
def make_service(tmp_path):
|
||||
store = RegistryStore(tmp_path / "registry.sqlite3")
|
||||
store.initialize()
|
||||
return RegistryService(store, ingestion=GitIngestionService(tmp_path / "checkouts"))
|
||||
|
||||
|
||||
def test_scope_generator_renders_canonical_sections_and_capability_blocks(tmp_path):
|
||||
service = make_service(tmp_path)
|
||||
repository = service.register_repository(
|
||||
name="Repo Registry",
|
||||
url="https://example.test/coulomb/repo-registry.git",
|
||||
description="Generates repository scope files from approved characteristics.",
|
||||
)
|
||||
service.update_scope(
|
||||
repository.id,
|
||||
name="Repo Scoping",
|
||||
description="Generates and validates SCOPE.md files for registered repositories.",
|
||||
confidence=0.95,
|
||||
)
|
||||
ability_id = service.add_ability(
|
||||
repository.id,
|
||||
name="Maintain Repository Scope",
|
||||
description="Keeps repository utility and boundaries understandable.",
|
||||
primary_class="repository-intelligence",
|
||||
attributes=["scope", "capability-mapping"],
|
||||
)
|
||||
capability_id = service.add_capability(
|
||||
repository.id,
|
||||
ability_id,
|
||||
name="Generate SCOPE.md",
|
||||
description="Renders SCOPE.md from approved repository characteristics.",
|
||||
primary_class="api",
|
||||
attributes=["scope", "generation"],
|
||||
)
|
||||
service.add_feature(
|
||||
repository.id,
|
||||
capability_id,
|
||||
name="Preview generated SCOPE.md",
|
||||
type="business-usecase",
|
||||
primary_class="business-usecase",
|
||||
attributes=["scope", "preview"],
|
||||
location="src/repo_registry/scope/generator.py",
|
||||
)
|
||||
|
||||
content = ScopeGenerator(service).generate("repo-registry")
|
||||
|
||||
assert content.startswith("# SCOPE\n")
|
||||
for section in SCOPE_SECTIONS:
|
||||
assert f"## {section}" in content
|
||||
assert "Generates and validates SCOPE.md files" in content
|
||||
assert "Maintain Repository Scope" in content
|
||||
assert "Preview generated SCOPE.md" in content
|
||||
assert "src/repo_registry/scope/generator.py" in content
|
||||
assert "```capability" in content
|
||||
assert "type: api" in content
|
||||
assert "title: Generate SCOPE.md" in content
|
||||
assert "keywords: [api, scope, generation, business-usecase, preview]" in content
|
||||
|
||||
|
||||
def test_scope_generator_marks_missing_curator_owned_sections(tmp_path):
|
||||
service = make_service(tmp_path)
|
||||
service.register_repository(
|
||||
name="Sparse Repo",
|
||||
url="https://example.test/sparse.git",
|
||||
description="Sparse repo.",
|
||||
)
|
||||
|
||||
content = ScopeGenerator(service).generate("sparse")
|
||||
|
||||
assert "## Out of Scope" in content
|
||||
assert "<!-- needs curator input -->" in content
|
||||
assert "<!-- No approved capabilities yet. -->" in content
|
||||
|
||||
|
||||
def test_scope_validator_validates_generated_scope_and_diffs_sections(tmp_path):
|
||||
service = make_service(tmp_path)
|
||||
repository = service.register_repository(
|
||||
name="Validator Repo",
|
||||
url="https://example.test/validator-repo.git",
|
||||
description="Validates generated scope files.",
|
||||
)
|
||||
ability_id = service.add_ability(repository.id, name="Validate Scope Files")
|
||||
service.add_capability(
|
||||
repository.id,
|
||||
ability_id,
|
||||
name="Diff SCOPE.md",
|
||||
description="Compares generated and existing scope sections.",
|
||||
primary_class="api",
|
||||
attributes=["scope", "diff"],
|
||||
)
|
||||
generator = ScopeGenerator(service)
|
||||
validator = ScopeValidator(generator)
|
||||
path = tmp_path / "SCOPE.md"
|
||||
path.write_text(generator.generate("validator-repo"), encoding="utf-8")
|
||||
|
||||
validation = validator.validate(path)
|
||||
diff = validator.diff("validator-repo", path)
|
||||
|
||||
assert validation.ok
|
||||
assert validation.issues == []
|
||||
assert not diff.needs_update
|
||||
assert {section.status for section in diff.sections} == {"ok"}
|
||||
|
||||
path.write_text(
|
||||
path.read_text(encoding="utf-8").replace("## Core Idea", "## Core Thought"),
|
||||
encoding="utf-8",
|
||||
)
|
||||
diff = validator.diff("validator-repo", path)
|
||||
assert diff.needs_update
|
||||
assert next(section for section in diff.sections if section.section == "Core Idea").status == "missing"
|
||||
|
||||
|
||||
def test_scope_validator_warns_when_provided_capabilities_section_is_missing(tmp_path):
|
||||
path = tmp_path / "SCOPE.md"
|
||||
path.write_text(
|
||||
"\n\n".join(
|
||||
f"## {section}\n\nplaceholder"
|
||||
for section in SCOPE_SECTIONS
|
||||
if section != "Provided Capabilities"
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
result = ScopeValidator().validate(path)
|
||||
|
||||
assert any(
|
||||
issue.check == "C5c"
|
||||
and issue.severity == "warn"
|
||||
and "Provided Capabilities" in issue.message
|
||||
for issue in result.issues
|
||||
)
|
||||
@@ -18,6 +18,7 @@ def test_openapi_groups_agent_facing_endpoints():
|
||||
"analysis",
|
||||
"review",
|
||||
"registry",
|
||||
"scope",
|
||||
"search",
|
||||
"discovery",
|
||||
}
|
||||
@@ -252,6 +253,15 @@ def test_openapi_contract_snapshot_for_stable_agent_paths():
|
||||
"/repos/{repository_id}/export": {
|
||||
"get": {"tags": ["discovery"], "success_schema": "application/x-yaml"}
|
||||
},
|
||||
"/repos/{repo_slug}/scope": {
|
||||
"get": {"tags": ["scope"], "success_schema": None}
|
||||
},
|
||||
"/repos/{repo_slug}/scope/diff": {
|
||||
"get": {"tags": ["scope"], "success_schema": "object"}
|
||||
},
|
||||
"/repos/{repo_slug}/scope/write": {
|
||||
"post": {"tags": ["scope"], "success_schema": "object"}
|
||||
},
|
||||
"/repos/{repository_id}/expectation-gaps": {
|
||||
"get": {"tags": ["review"], "success_schema": "list[ExpectationGapResponse]"},
|
||||
"post": {"tags": ["review"], "success_schema": "ExpectationGapResponse"},
|
||||
@@ -455,6 +465,100 @@ def test_api_manual_registry_loop(tmp_path):
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
|
||||
def test_api_generates_diffs_and_writes_scope_md(tmp_path):
|
||||
source = tmp_path / "scope-repo"
|
||||
source.mkdir()
|
||||
|
||||
def override_settings():
|
||||
return Settings(
|
||||
database_path=str(tmp_path / "scope-api.sqlite3"),
|
||||
checkout_root=str(tmp_path / "checkouts"),
|
||||
)
|
||||
|
||||
app.dependency_overrides[get_settings] = override_settings
|
||||
client = TestClient(app)
|
||||
try:
|
||||
repository = client.post(
|
||||
"/repos",
|
||||
json={
|
||||
"name": "Scope Repo",
|
||||
"url": str(source),
|
||||
"description": "Generates SCOPE.md through the API.",
|
||||
},
|
||||
).json()
|
||||
ability_id = client.post(
|
||||
f"/repos/{repository['id']}/abilities",
|
||||
json={
|
||||
"name": "Maintain Repository Scope",
|
||||
"description": "Keeps repository utility understandable.",
|
||||
},
|
||||
).json()["id"]
|
||||
client.post(
|
||||
f"/repos/{repository['id']}/capabilities",
|
||||
json={
|
||||
"ability_id": ability_id,
|
||||
"name": "Generate SCOPE.md",
|
||||
"description": "Renders SCOPE.md from approved characteristics.",
|
||||
"primary_class": "api",
|
||||
"attributes": ["scope", "generation"],
|
||||
},
|
||||
)
|
||||
|
||||
preview = client.get("/repos/scope-repo/scope")
|
||||
assert preview.status_code == 200
|
||||
assert preview.headers["content-type"].startswith("text/markdown")
|
||||
assert "# SCOPE" in preview.text
|
||||
assert "title: Generate SCOPE.md" in preview.text
|
||||
|
||||
diff = client.get("/repos/scope-repo/scope/diff")
|
||||
assert diff.status_code == 200
|
||||
assert diff.json()["needs_update"] is True
|
||||
assert {section["status"] for section in diff.json()["sections"]} == {"missing"}
|
||||
|
||||
write = client.post("/repos/scope-repo/scope/write")
|
||||
assert write.status_code == 200
|
||||
assert write.json() == {"written": True, "path": str(source / "SCOPE.md")}
|
||||
assert (source / "SCOPE.md").read_text(encoding="utf-8").startswith("# SCOPE")
|
||||
|
||||
current = client.get("/repos/scope-repo/scope/diff")
|
||||
assert current.status_code == 200
|
||||
assert current.json()["needs_update"] is False
|
||||
assert {section["status"] for section in current.json()["sections"]} == {"ok"}
|
||||
|
||||
empty = client.post(
|
||||
"/repos",
|
||||
json={
|
||||
"name": "Empty Scope",
|
||||
"url": "https://example.test/empty-scope.git",
|
||||
"description": "No approved characteristics yet.",
|
||||
},
|
||||
).json()
|
||||
assert client.get("/repos/empty-scope/scope").status_code == 404
|
||||
|
||||
remote = client.post(
|
||||
"/repos",
|
||||
json={
|
||||
"name": "Remote Scope",
|
||||
"url": "https://example.test/remote-scope.git",
|
||||
"description": "Has no known local checkout path.",
|
||||
},
|
||||
).json()
|
||||
remote_ability = client.post(
|
||||
f"/repos/{remote['id']}/abilities",
|
||||
json={"name": "Remote Scope Generation"},
|
||||
).json()["id"]
|
||||
client.post(
|
||||
f"/repos/{remote['id']}/capabilities",
|
||||
json={
|
||||
"ability_id": remote_ability,
|
||||
"name": "Generate Remote SCOPE.md",
|
||||
},
|
||||
)
|
||||
assert client.post("/repos/remote-scope/scope/write").status_code == 409
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
|
||||
def test_api_compare_gap_and_export_use_cases(tmp_path):
|
||||
def override_settings():
|
||||
return Settings(
|
||||
|
||||
Reference in New Issue
Block a user