Add Phase 10-12 implementation: multi-tenant, marketplace, observability
Major additions: - marketplace/: Agent template registry with FTS5 search, ratings, versioning - observability/: Prometheus metrics, distributed tracing, structured logging - ledger/migrations/: Database migration scripts for multi-tenant support - tests/governance/: 15 new test files for phases 6-12 (295 total tests) - bin/validate-phases: Full 12-phase validation script New features: - Multi-tenant support with tenant isolation and quota enforcement - Agent marketplace with semantic versioning and search - Observability with metrics, tracing, and log correlation - Tier-1 agent bootstrap scripts Updated components: - ledger/api.py: Extended API for tenants, marketplace, observability - ledger/schema.sql: Added tenant, project, marketplace tables - testing/framework.ts: Enhanced test framework - checkpoint/checkpoint.py: Improved checkpoint management Archived: - External integrations (Slack/GitHub/PagerDuty) moved to .archive/ - Old checkpoint files cleaned up Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
4b5b3b0a2d
commit
8c6e7831e9
@@ -0,0 +1,231 @@
|
||||
"""
|
||||
Phase 9: External Integrations Tests
|
||||
====================================
|
||||
Tests for webhook delivery and notification paths.
|
||||
|
||||
Required tests:
|
||||
- github_webhook: Verify GitHub event handling in dry-run
|
||||
- slack_notification: Verify Slack notification dispatch in dry-run
|
||||
- webhook_delivery: Verify IntegrationManager broadcast delivery
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add integrations to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "integrations"))
|
||||
|
||||
PASSED = 0
|
||||
FAILED = 0
|
||||
|
||||
|
||||
def log(msg: str, status: str = "info"):
|
||||
icons = {"pass": "\033[92m✓\033[0m", "fail": "\033[91m✗\033[0m", "info": "→"}
|
||||
print(f" {icons.get(status, '•')} {msg}")
|
||||
|
||||
|
||||
def load_module(name: str, path: Path):
|
||||
import importlib.util
|
||||
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
if spec is None or spec.loader is None:
|
||||
raise ImportError(f"Module spec missing for {name}")
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[name] = module
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def test_github_webhook():
|
||||
"""Test GitHub webhook handling in dry-run mode"""
|
||||
global PASSED, FAILED
|
||||
|
||||
print("\n[TEST] github_webhook")
|
||||
|
||||
os.environ["INTEGRATION_DRY_RUN"] = "true"
|
||||
os.environ["GITHUB_TOKEN"] = "ghp_test_token"
|
||||
os.environ["GITHUB_REPO"] = "example/repo"
|
||||
|
||||
try:
|
||||
base = load_module(
|
||||
"common.base",
|
||||
Path(__file__).parent.parent.parent / "integrations" / "common" / "base.py",
|
||||
)
|
||||
github = load_module(
|
||||
"github.github",
|
||||
Path(__file__).parent.parent.parent
|
||||
/ "integrations"
|
||||
/ "github"
|
||||
/ "github.py",
|
||||
)
|
||||
except ImportError as e:
|
||||
log(f"GitHub import failed: {e}", "fail")
|
||||
FAILED += 1
|
||||
return False
|
||||
|
||||
gh = github.GitHubIntegration()
|
||||
event = base.IntegrationEvent(
|
||||
event_type="plan_created",
|
||||
source="agent-001",
|
||||
data={"plan": {"title": "Test Plan", "objective": "Verify webhook"}},
|
||||
)
|
||||
|
||||
if gh.send_event(event):
|
||||
log("GitHub plan event handled", "pass")
|
||||
PASSED += 1
|
||||
else:
|
||||
log("GitHub plan event failed", "fail")
|
||||
FAILED += 1
|
||||
|
||||
audit = gh.get_audit_log()
|
||||
if any(entry.get("operation") == "create_pr" for entry in audit):
|
||||
log("GitHub create_pr audit recorded", "pass")
|
||||
PASSED += 1
|
||||
else:
|
||||
log("GitHub create_pr audit missing", "fail")
|
||||
FAILED += 1
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def test_slack_notification():
|
||||
"""Test Slack notification dispatch in dry-run mode"""
|
||||
global PASSED, FAILED
|
||||
|
||||
print("\n[TEST] slack_notification")
|
||||
|
||||
os.environ["INTEGRATION_DRY_RUN"] = "true"
|
||||
os.environ["SLACK_BOT_TOKEN"] = "xoxb-test-token"
|
||||
os.environ["SLACK_WEBHOOK_URL"] = "https://hooks.slack.com/test"
|
||||
|
||||
try:
|
||||
base = load_module(
|
||||
"common.base",
|
||||
Path(__file__).parent.parent.parent / "integrations" / "common" / "base.py",
|
||||
)
|
||||
slack_module = load_module(
|
||||
"slack.slack",
|
||||
Path(__file__).parent.parent.parent / "integrations" / "slack" / "slack.py",
|
||||
)
|
||||
except ImportError as e:
|
||||
log(f"Slack import failed: {e}", "fail")
|
||||
FAILED += 1
|
||||
return False
|
||||
|
||||
slack = slack_module.SlackIntegration()
|
||||
event = base.IntegrationEvent(
|
||||
event_type="violation_detected",
|
||||
source="agent-002",
|
||||
data={
|
||||
"violation": {"type": "policy", "severity": "high", "description": "Test"}
|
||||
},
|
||||
priority="high",
|
||||
)
|
||||
|
||||
if slack.send_event(event):
|
||||
log("Slack violation notification dispatched", "pass")
|
||||
PASSED += 1
|
||||
else:
|
||||
log("Slack notification failed", "fail")
|
||||
FAILED += 1
|
||||
|
||||
audit = slack.get_audit_log()
|
||||
if any(entry.get("operation") == "send_message" for entry in audit):
|
||||
log("Slack send_message audit recorded", "pass")
|
||||
PASSED += 1
|
||||
else:
|
||||
log("Slack send_message audit missing", "fail")
|
||||
FAILED += 1
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def test_webhook_delivery():
|
||||
"""Test IntegrationManager broadcast delivery"""
|
||||
global PASSED, FAILED
|
||||
|
||||
print("\n[TEST] webhook_delivery")
|
||||
|
||||
os.environ["INTEGRATION_DRY_RUN"] = "true"
|
||||
os.environ["SLACK_BOT_TOKEN"] = "xoxb-test-token"
|
||||
os.environ["GITHUB_TOKEN"] = "ghp_test_token"
|
||||
os.environ["GITHUB_REPO"] = "example/repo"
|
||||
|
||||
try:
|
||||
base = load_module(
|
||||
"common.base",
|
||||
Path(__file__).parent.parent.parent / "integrations" / "common" / "base.py",
|
||||
)
|
||||
slack_module = load_module(
|
||||
"slack.slack",
|
||||
Path(__file__).parent.parent.parent / "integrations" / "slack" / "slack.py",
|
||||
)
|
||||
github = load_module(
|
||||
"github.github",
|
||||
Path(__file__).parent.parent.parent
|
||||
/ "integrations"
|
||||
/ "github"
|
||||
/ "github.py",
|
||||
)
|
||||
except ImportError as e:
|
||||
log(f"Integration import failed: {e}", "fail")
|
||||
FAILED += 1
|
||||
return False
|
||||
|
||||
manager = base.IntegrationManager()
|
||||
manager.register(
|
||||
slack_module.SlackIntegration(
|
||||
webhook_url="https://hooks.slack.com/test",
|
||||
bot_token="xoxb-test-token",
|
||||
)
|
||||
)
|
||||
manager.register(
|
||||
github.GitHubIntegration(
|
||||
token="ghp_test_token",
|
||||
repo="example/repo",
|
||||
)
|
||||
)
|
||||
|
||||
event = base.IntegrationEvent(
|
||||
event_type="execution_complete",
|
||||
source="agent-003",
|
||||
data={"result": {"success": True, "duration": 4.2}, "commit_sha": "HEAD"},
|
||||
)
|
||||
|
||||
results = manager.broadcast(event)
|
||||
if results.get("slack") and results.get("github"):
|
||||
log("Broadcast delivered to Slack and GitHub", "pass")
|
||||
PASSED += 1
|
||||
else:
|
||||
log(f"Broadcast results missing: {results}", "fail")
|
||||
FAILED += 1
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
global PASSED, FAILED
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("PHASE 9: EXTERNAL INTEGRATIONS TESTS")
|
||||
print("=" * 60)
|
||||
|
||||
try:
|
||||
test_github_webhook()
|
||||
test_slack_notification()
|
||||
test_webhook_delivery()
|
||||
except Exception as e:
|
||||
print(f"\n\033[91mTest execution error: {e}\033[0m")
|
||||
FAILED += 1
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print(f"RESULTS: {PASSED} passed, {FAILED} failed")
|
||||
print("=" * 60 + "\n")
|
||||
|
||||
return FAILED == 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = main()
|
||||
sys.exit(0 if success else 1)
|
||||
Reference in New Issue
Block a user