""" External Integration Test Plugin Tests all ExternalAPI integration features: - REST API server endpoints - Incoming/outgoing webhooks - API key authentication - IPC communication - Server-Sent Events (SSE) This plugin verifies third-party integration capabilities. """ import time import json import threading from datetime import datetime from typing import Dict, List, Any from dataclasses import dataclass from core.base_plugin import BasePlugin from core.api.external_api import get_external_api from core.api.widget_api import get_widget_api, WidgetType from core.api.plugin_api import get_api @dataclass class IntegrationTestResult: """Result of an integration test.""" category: str test_name: str passed: bool duration_ms: float details: Dict = None error: str = None class ExternalIntegrationTestPlugin(BasePlugin): """ Integration test suite for ExternalAPI. Tests REST API, webhooks, authentication, IPC, and SSE functionality. """ def __init__(self): super().__init__() self.external_api = None self.widget_api = None self.plugin_api = None self.results: List[IntegrationTestResult] = [] self.widget = None self.test_port = 9999 def initialize(self): """Initialize and run integration tests.""" self.external_api = get_external_api() self.widget_api = get_widget_api() self.plugin_api = get_api() self._create_results_widget() self._run_all_tests() def _create_results_widget(self): """Create widget to display integration test results.""" self.widget = self.widget_api.create_widget( name="external_integration_test", title="🌐 External Integration Test", size=(900, 650), position=(150, 150), widget_type=WidgetType.CUSTOM ) self._update_widget_display() self.widget.show() def _update_widget_display(self): """Update widget content.""" try: from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QTextBrowser, QTabWidget, QGroupBox ) container = QWidget() layout = QVBoxLayout(container) # Header header = QLabel("🌐 External Integration Test Suite") header.setStyleSheet("font-size: 20px; font-weight: bold; color: #ff8c42;") layout.addWidget(header) # Summary passed = sum(1 for r in self.results if r.passed) total = len(self.results) summary = QLabel(f"Results: {passed}/{total} passed") summary.setStyleSheet(f"font-size: 14px; color: {'#4ecca3' if passed == total else '#ff6b6b'};") layout.addWidget(summary) # Results display self.results_browser = QTextBrowser() self.results_browser.setHtml(self._generate_results_html()) layout.addWidget(self.results_browser) # Control buttons btn_layout = QHBoxLayout() btn_run = QPushButton("Run All Tests") btn_run.clicked.connect(self._run_all_tests) btn_layout.addWidget(btn_run) btn_server = QPushButton("Test Server Start/Stop") btn_server.clicked.connect(self._test_server_lifecycle) btn_layout.addWidget(btn_server) btn_webhook = QPushButton("Test Webhooks") btn_webhook.clicked.connect(self._test_outgoing_webhook) btn_layout.addWidget(btn_webhook) layout.addLayout(btn_layout) self.widget.set_content(container) except ImportError: pass def _generate_results_html(self) -> str: """Generate results HTML.""" html = """ """ for result in self.results: status_class = "pass" if result.passed else "fail" status_icon = "✅" if result.passed else "❌" details = json.dumps(result.details) if result.details else "" if len(details) > 100: details = details[:100] + "..." html += f""" """ html += "
CategoryTestResultDurationDetails
{result.category} {result.test_name} {status_icon} {result.duration_ms:.2f}ms {details}
" return html def _record_result(self, category: str, test_name: str, passed: bool, duration_ms: float, details: Dict = None, error: str = None): """Record test result.""" result = IntegrationTestResult( category=category, test_name=test_name, passed=passed, duration_ms=duration_ms, details=details, error=error ) self.results.append(result) self._update_widget_display() # Show notification for failures if not passed: self.plugin_api.show_notification( "Integration Test Failed", f"{category}/{test_name}: {error or 'Unknown error'}", duration=3000 ) def _run_all_tests(self): """Execute all integration tests.""" self.results.clear() # REST API Server Tests self._test_server_lifecycle() self._test_endpoint_registration() self._test_endpoint_decorator() self._test_cors_configuration() # Webhook Tests self._test_incoming_webhook() self._test_outgoing_webhook() self._test_webhook_hmac() # Authentication Tests self._test_api_key_creation() self._test_api_key_revocation() # IPC Tests self._test_ipc_handler() self._test_ipc_send() # Utility Tests self._test_status_endpoint() self._test_url_generation() self._test_webhook_history() # ================================================================= # REST API Server Tests # ================================================================= def _test_server_lifecycle(self): """Test server start and stop.""" start = time.time() try: # Start server started = self.external_api.start_server( port=self.test_port, host="127.0.0.1", cors_origins=["http://localhost:3000"] ) if started: time.sleep(0.5) # Let server initialize # Verify it's running status = self.external_api.get_status() running = status.get('server_running', False) # Stop server stopped = self.external_api.stop_server() duration = (time.time() - start) * 1000 self._record_result( "REST Server", "Server Lifecycle", started and running and stopped, duration, {"started": started, "running": running, "stopped": stopped} ) else: duration = (time.time() - start) * 1000 self._record_result( "REST Server", "Server Lifecycle", False, duration, error="Server failed to start (may already be running)" ) except Exception as e: duration = (time.time() - start) * 1000 self._record_result("REST Server", "Server Lifecycle", False, duration, error=str(e)) def _test_endpoint_registration(self): """Test programmatic endpoint registration.""" start = time.time() try: def test_handler(params): return {"test": "data", "params": params} self.external_api.register_endpoint( "test/registration", test_handler, methods=["GET", "POST"], auth_required=False ) endpoints = self.external_api.get_endpoints() success = "test/registration" in endpoints duration = (time.time() - start) * 1000 self._record_result( "REST Server", "Endpoint Registration", success, duration, {"endpoints": endpoints} ) except Exception as e: duration = (time.time() - start) * 1000 self._record_result("REST Server", "Endpoint Registration", False, duration, error=str(e)) def _test_endpoint_decorator(self): """Test decorator-based endpoint registration.""" start = time.time() try: @self.external_api.endpoint("test/decorator", methods=["GET"]) def decorated_endpoint(): return {"decorated": True} endpoints = self.external_api.get_endpoints() success = "test/decorator" in endpoints duration = (time.time() - start) * 1000 self._record_result( "REST Server", "Endpoint Decorator", success, duration ) except Exception as e: duration = (time.time() - start) * 1000 self._record_result("REST Server", "Endpoint Decorator", False, duration, error=str(e)) def _test_cors_configuration(self): """Test CORS configuration.""" start = time.time() try: # CORS is configured during server start status = self.external_api.get_status() success = status.get('server_running') is not None duration = (time.time() - start) * 1000 self._record_result( "REST Server", "CORS Configuration", success, duration, {"cors_origins": ["http://localhost:3000"]} ) except Exception as e: duration = (time.time() - start) * 1000 self._record_result("REST Server", "CORS Configuration", False, duration, error=str(e)) # ================================================================= # Webhook Tests # ================================================================= def _test_incoming_webhook(self): """Test incoming webhook registration.""" start = time.time() try: def webhook_handler(payload): return {"received": payload} self.external_api.register_webhook( "test_incoming", webhook_handler, methods=["POST"], rate_limit=60 ) webhooks = self.external_api.get_webhooks() success = "test_incoming" in webhooks duration = (time.time() - start) * 1000 self._record_result( "Webhooks", "Incoming Webhook Registration", success, duration, {"webhooks": webhooks} ) except Exception as e: duration = (time.time() - start) * 1000 self._record_result("Webhooks", "Incoming Webhook", False, duration, error=str(e)) def _test_outgoing_webhook(self): """Test outgoing webhook POST.""" start = time.time() try: # Post to httpbin for testing result = self.external_api.post_webhook( "https://httpbin.org/post", {"test": "data", "timestamp": time.time()}, headers={"X-Test-Header": "test"}, timeout=10 ) success = result.get('success', False) status = result.get('status', 0) duration = (time.time() - start) * 1000 self._record_result( "Webhooks", "Outgoing Webhook POST", success and 200 <= status < 300, duration, {"status": status} ) except Exception as e: duration = (time.time() - start) * 1000 self._record_result("Webhooks", "Outgoing Webhook", False, duration, error=str(e)) def _test_webhook_hmac(self): """Test webhook HMAC signature verification.""" start = time.time() try: import hmac import hashlib secret = "test_secret_key" def secure_handler(payload): return {"secure": True} self.external_api.register_webhook( "secure_webhook", secure_handler, secret=secret, methods=["POST"] ) # Generate test signature payload = {"event": "test"} payload_str = json.dumps(payload, sort_keys=True) signature = hmac.new( secret.encode(), payload_str.encode(), hashlib.sha256 ).hexdigest() # Verify signature logic is in place webhooks = self.external_api.get_webhooks() success = "secure_webhook" in webhooks duration = (time.time() - start) * 1000 self._record_result( "Webhooks", "HMAC Signature", success, duration, {"signature_generated": True} ) except Exception as e: duration = (time.time() - start) * 1000 self._record_result("Webhooks", "HMAC Signature", False, duration, error=str(e)) # ================================================================= # Authentication Tests # ================================================================= def _test_api_key_creation(self): """Test API key creation.""" start = time.time() try: key = self.external_api.create_api_key( name="test_integration_key", permissions=["read", "write"] ) success = len(key) > 0 and isinstance(key, str) self._test_api_key = key # Save for revocation test duration = (time.time() - start) * 1000 self._record_result( "Authentication", "API Key Creation", success, duration, {"key_length": len(key)} ) except Exception as e: duration = (time.time() - start) * 1000 self._record_result("Authentication", "API Key Creation", False, duration, error=str(e)) def _test_api_key_revocation(self): """Test API key revocation.""" start = time.time() try: # Create a key to revoke key = self.external_api.create_api_key("temp_revoke_key") # Revoke it revoked = self.external_api.revoke_api_key(key) duration = (time.time() - start) * 1000 self._record_result( "Authentication", "API Key Revocation", revoked, duration ) except Exception as e: duration = (time.time() - start) * 1000 self._record_result("Authentication", "API Key Revocation", False, duration, error=str(e)) # ================================================================= # IPC Tests # ================================================================= def _test_ipc_handler(self): """Test IPC handler registration.""" start = time.time() try: self._ipc_received = False def ipc_handler(data): self._ipc_received = True self._ipc_data = data self.external_api.register_ipc_handler("test_channel", ipc_handler) duration = (time.time() - start) * 1000 self._record_result( "IPC", "Handler Registration", True, duration ) except Exception as e: duration = (time.time() - start) * 1000 self._record_result("IPC", "Handler Registration", False, duration, error=str(e)) def _test_ipc_send(self): """Test IPC message sending.""" start = time.time() try: sent = self.external_api.send_ipc( "test_channel", {"message": "test", "timestamp": time.time()} ) duration = (time.time() - start) * 1000 self._record_result( "IPC", "Send Message", sent, duration, {"received": getattr(self, '_ipc_received', False)} ) except Exception as e: duration = (time.time() - start) * 1000 self._record_result("IPC", "Send Message", False, duration, error=str(e)) # ================================================================= # Utility Tests # ================================================================= def _test_status_endpoint(self): """Test /health endpoint availability.""" start = time.time() try: status = self.external_api.get_status() success = ( 'server_running' in status and 'endpoints' in status and 'webhooks' in status ) duration = (time.time() - start) * 1000 self._record_result( "Utilities", "Status Endpoint", success, duration, status ) except Exception as e: duration = (time.time() - start) * 1000 self._record_result("Utilities", "Status Endpoint", False, duration, error=str(e)) def _test_url_generation(self): """Test URL generation.""" start = time.time() try: url = self.external_api.get_url("api/v1/test") success = url.startswith("http") and "api/v1/test" in url duration = (time.time() - start) * 1000 self._record_result( "Utilities", "URL Generation", success, duration, {"url": url} ) except Exception as e: duration = (time.time() - start) * 1000 self._record_result("Utilities", "URL Generation", False, duration, error=str(e)) def _test_webhook_history(self): """Test webhook history tracking.""" start = time.time() try: history = self.external_api.get_webhook_history(limit=10) success = isinstance(history, list) duration = (time.time() - start) * 1000 self._record_result( "Utilities", "Webhook History", success, duration, {"history_entries": len(history)} ) except Exception as e: duration = (time.time() - start) * 1000 self._record_result("Utilities", "Webhook History", False, duration, error=str(e)) def shutdown(self): """Clean up resources.""" # Stop server if running try: self.external_api.stop_server() except: pass if self.widget: self.widget.close() # Plugin entry point plugin_class = ExternalIntegrationTestPlugin