test(features): fix BDD scenarios for progressive disclosure and resource tests
Add 'When I load the agent skills folder' step to scenarios that use load-based testing without explicit loading. The Given step creates the temp folder but sets loaded_skill=None; the loader must be created explicitly before calling discover/activate/deactivate/list_resources. Also restored scripts/check-adr-compliance.py which was removed by git filter-repo --invert-paths during security token purge. ISSUES CLOSED: #9454
This commit is contained in:
@@ -295,6 +295,7 @@ Feature: Agent Skills Loader
|
||||
Step 1: do this.
|
||||
Step 2: do that.
|
||||
"""
|
||||
When I load the agent skills folder
|
||||
When I call discover on the agent skill
|
||||
Then the discover result should have name "local/disclosure-skill"
|
||||
And the discover result should have description "Progressive disclosure test skill."
|
||||
@@ -311,6 +312,7 @@ Feature: Agent Skills Loader
|
||||
Step 1: do this.
|
||||
Step 2: do that.
|
||||
"""
|
||||
When I load the agent skills folder
|
||||
When I call discover on the agent skill
|
||||
And I call activate on the agent skill
|
||||
Then the activate result body should contain "Step 1: do this."
|
||||
@@ -327,6 +329,7 @@ Feature: Agent Skills Loader
|
||||
Use the scripts.
|
||||
"""
|
||||
And the folder contains a script file "helper.py" with content "# helper"
|
||||
When I load the agent skills folder
|
||||
When I call discover on the agent skill
|
||||
And I list available resources
|
||||
Then the resources list should include "helper.py"
|
||||
@@ -341,6 +344,7 @@ Feature: Agent Skills Loader
|
||||
---
|
||||
Step 1: do this.
|
||||
"""
|
||||
When I load the agent skills folder
|
||||
When I call discover on the agent skill
|
||||
And I call activate on the agent skill
|
||||
And I call deactivate on the agent skill
|
||||
@@ -690,6 +694,7 @@ Feature: Agent Skills Loader
|
||||
Instructions.
|
||||
"""
|
||||
And the folder contains an asset file "config.json" with content "{}"
|
||||
When I load the agent skills folder
|
||||
When I call discover on the agent skill
|
||||
Then the discover result should have 1 resource slot(s)
|
||||
And the discover result resource slot "assets" should have access "read_only"
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python3
|
||||
"""ADR compliance checker for CleverAgents.
|
||||
|
||||
Verifies that code follows the architectural decisions recorded in ADRs.
|
||||
|
||||
ADR-002: Asyncio Concurrency Model
|
||||
- Async functions should use 'async def', not threading
|
||||
- No direct thread usage in application layer
|
||||
|
||||
ADR-003: Dependency Injection Framework
|
||||
- Services should receive dependencies via __init__
|
||||
- No direct instantiation of infrastructure in application layer
|
||||
|
||||
ADR-007: Repository Pattern for Persistence
|
||||
- Database access only through repository classes
|
||||
- No direct SQLAlchemy session usage in services
|
||||
|
||||
Usage:
|
||||
python scripts/check-adr-compliance.py
|
||||
"""
|
||||
|
||||
import ast
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def check_adr002_async_model(src_dir: Path) -> list[str]:
|
||||
"""Check ADR-002: Asyncio Concurrency Model.
|
||||
|
||||
Verifies no direct threading usage in application code.
|
||||
"""
|
||||
violations: list[str] = []
|
||||
app_dir = src_dir / "application"
|
||||
if not app_dir.exists():
|
||||
return violations
|
||||
|
||||
for py_file in app_dir.rglob("*.py"):
|
||||
content = py_file.read_text()
|
||||
if "import threading" in content or "from threading import" in content:
|
||||
violations.append(
|
||||
f"ADR-002 violation: {py_file.relative_to(src_dir)} "
|
||||
"imports threading directly. Use asyncio instead."
|
||||
)
|
||||
return violations
|
||||
|
||||
|
||||
def check_adr003_dependency_injection(src_dir: Path) -> list[str]:
|
||||
"""Check ADR-003: Dependency Injection Framework.
|
||||
|
||||
Verifies services use constructor injection.
|
||||
"""
|
||||
violations: list[str] = []
|
||||
services_dir = src_dir / "application" / "services"
|
||||
if not services_dir.exists():
|
||||
return violations
|
||||
|
||||
for py_file in services_dir.rglob("*.py"):
|
||||
if py_file.name.startswith("__"):
|
||||
continue
|
||||
try:
|
||||
tree = ast.parse(py_file.read_text())
|
||||
except SyntaxError:
|
||||
continue
|
||||
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.ClassDef):
|
||||
# Check if class has __init__ with parameters (DI)
|
||||
has_init = False
|
||||
for item in node.body:
|
||||
if isinstance(item, ast.FunctionDef) and item.name == "__init__":
|
||||
has_init = True
|
||||
# Check for at least one parameter beyond self
|
||||
if len(item.args.args) < 2:
|
||||
violations.append(
|
||||
f"ADR-003 note: {py_file.relative_to(src_dir)}:"
|
||||
f"{node.lineno} class {node.name} has __init__ "
|
||||
"with no injected dependencies"
|
||||
)
|
||||
if not has_init and node.body:
|
||||
# Services without __init__ may be okay if they're utility classes
|
||||
pass
|
||||
return violations
|
||||
|
||||
|
||||
def check_adr007_repository_pattern(src_dir: Path) -> list[str]:
|
||||
"""Check ADR-007: Repository Pattern for Persistence.
|
||||
|
||||
Verifies no direct SQLAlchemy session usage in service layer.
|
||||
"""
|
||||
violations: list[str] = []
|
||||
services_dir = src_dir / "application" / "services"
|
||||
if not services_dir.exists():
|
||||
return violations
|
||||
|
||||
for py_file in services_dir.rglob("*.py"):
|
||||
content = py_file.read_text()
|
||||
# Check for direct session imports
|
||||
if "from sqlalchemy" in content:
|
||||
violations.append(
|
||||
f"ADR-007 violation: {py_file.relative_to(src_dir)} "
|
||||
"imports SQLAlchemy directly. Use repository pattern instead."
|
||||
)
|
||||
if "session.query" in content or "session.execute" in content:
|
||||
violations.append(
|
||||
f"ADR-007 violation: {py_file.relative_to(src_dir)} "
|
||||
"uses session directly. Access data through repositories."
|
||||
)
|
||||
return violations
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""Run all ADR compliance checks."""
|
||||
src_dir = Path("src/cleveragents")
|
||||
if not src_dir.exists():
|
||||
print("Error: src/cleveragents directory not found")
|
||||
return 1
|
||||
|
||||
all_violations: list[str] = []
|
||||
|
||||
print("Checking ADR-002: Asyncio Concurrency Model...")
|
||||
all_violations.extend(check_adr002_async_model(src_dir))
|
||||
|
||||
print("Checking ADR-003: Dependency Injection Framework...")
|
||||
all_violations.extend(check_adr003_dependency_injection(src_dir))
|
||||
|
||||
print("Checking ADR-007: Repository Pattern for Persistence...")
|
||||
all_violations.extend(check_adr007_repository_pattern(src_dir))
|
||||
|
||||
if all_violations:
|
||||
print(f"\nFound {len(all_violations)} ADR compliance issue(s):")
|
||||
for violation in all_violations:
|
||||
print(f" - {violation}")
|
||||
return 1
|
||||
|
||||
print("\nAll ADR compliance checks passed.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user