usecase e2e tests

This commit is contained in:
2026-04-26 12:45:49 +02:00
parent c03a1e93b0
commit a7f7113ce9
6 changed files with 736 additions and 0 deletions

View File

@@ -4,6 +4,7 @@ from dataclasses import asdict
from pathlib import Path
from fastapi import Depends, FastAPI, HTTPException, Query
from fastapi.responses import PlainTextResponse
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
@@ -27,6 +28,8 @@ from repo_registry.web_api.schemas import (
CandidateGraphResponse,
CandidateLeafRelink,
CandidateRejection,
CapabilityGapRequest,
CapabilityGapResponse,
CapabilityCreate,
CapabilitySummaryResponse,
CapabilityUpdate,
@@ -38,6 +41,7 @@ from repo_registry.web_api.schemas import (
IdResponse,
ObservedFactResponse,
RepositoryAbilityMapResponse,
RepositoryComparisonResponse,
RepositoryCreate,
RepositoryResponse,
RepositoryUpdate,
@@ -91,6 +95,7 @@ OPENAPI_TAGS = [
{"name": "review", "description": "Candidate graph approval and correction workflow."},
{"name": "registry", "description": "Approved ability maps and manual registry CRUD."},
{"name": "search", "description": "Agent-facing discovery endpoints."},
{"name": "discovery", "description": "Comparison, gap analysis, and export helpers."},
]
app = FastAPI(
@@ -951,6 +956,62 @@ def get_ability_map(
raise HTTPException(status_code=404, detail=str(exc)) from exc
@app.get(
"/repos/{repository_id}/export",
tags=["discovery"],
response_class=PlainTextResponse,
responses={
200: {
"content": {"application/x-yaml": {}},
"description": "YAML registry export suitable for repo-abilities.yaml.",
}
},
)
def export_repository_registry_entry(
repository_id: int,
service: RegistryService = Depends(get_service),
) -> PlainTextResponse:
try:
content = service.export_registry_entry(repository_id)
except NotFoundError as exc:
raise HTTPException(status_code=404, detail=str(exc)) from exc
return PlainTextResponse(content, media_type="application/x-yaml")
@app.get(
"/repository-comparisons",
tags=["discovery"],
response_model=RepositoryComparisonResponse,
)
def compare_repositories(
repository_ids: list[int] = Query(
...,
description="Repository ids to compare by approved abilities and capabilities.",
min_length=2,
),
service: RegistryService = Depends(get_service),
) -> dict[str, object]:
try:
return service.compare_repositories(repository_ids)
except NotFoundError as exc:
raise HTTPException(status_code=404, detail=str(exc)) from exc
@app.post(
"/capability-gaps",
tags=["discovery"],
response_model=CapabilityGapResponse,
)
def detect_capability_gaps(
payload: CapabilityGapRequest,
service: RegistryService = Depends(get_service),
) -> dict[str, object]:
try:
return service.detect_capability_gaps(**payload.model_dump())
except NotFoundError as exc:
raise HTTPException(status_code=404, detail=str(exc)) from exc
@app.get("/search", tags=["search"], response_model=list[SearchResultResponse])
def search(
q: str = Query(

View File

@@ -664,3 +664,85 @@ class CapabilitySummaryResponse(BaseModel):
description: str
confidence: float
confidence_label: str
class CapabilityGapRequest(BaseModel):
desired_ability: str
desired_capabilities: list[str]
repository_ids: list[int] | None = None
model_config = {
"json_schema_extra": {
"examples": [
{
"desired_ability": "Business Email Routing",
"desired_capabilities": [
"Classify Incoming Email",
"Route Email to Team",
],
"repository_ids": [1, 2],
}
]
}
}
class ComparedCapabilityResponse(BaseModel):
name: str
confidence: float
confidence_label: str
evidence_count: int
class ComparedAbilityRepositoryResponse(BaseModel):
repository_id: int
repository_name: str
confidence: float
confidence_label: str
capabilities: list[ComparedCapabilityResponse]
class ComparedAbilityResponse(BaseModel):
name: str
repositories: list[ComparedAbilityRepositoryResponse]
class UniqueCapabilityResponse(BaseModel):
repository_id: int
repository_name: str
ability_name: str
capability_name: str
class RepositoryComparisonResponse(BaseModel):
repositories: list[RepositoryResponse]
abilities: list[ComparedAbilityResponse]
unique_capabilities: list[UniqueCapabilityResponse]
class CapabilityGapMatchResponse(BaseModel):
capability: str
repositories: list[str]
class WeakCapabilityEvidenceResponse(BaseModel):
capability: str
repository_id: int
repository_name: str
evidence_count: int
strongest_evidence: str | None = None
confidence: float
confidence_label: str
class DuplicateCapabilityResponse(BaseModel):
capability: str
repositories: list[str]
class CapabilityGapResponse(BaseModel):
desired_ability: str
matched_capabilities: list[CapabilityGapMatchResponse]
missing_capabilities: list[str]
weakly_evidenced_capabilities: list[WeakCapabilityEvidenceResponse]
duplicate_capabilities: list[DuplicateCapabilityResponse]