|
|
| """
|
| GUVI Integration Test Script.
|
|
|
| Tests all the GUVI submission compliance features:
|
| 1. API Key Authentication (x-api-key header)
|
| 2. GUVI Input Format Support (nested message object)
|
| 3. Response Format (reply, suspiciousKeywords, agentNotes)
|
| 4. GUVI Callback (mock test)
|
|
|
| Usage:
|
| # Start the server first:
|
| uvicorn app.main:app --reload
|
|
|
| # Then run tests:
|
| python scripts/test_guvi_integration.py
|
| """
|
|
|
| import requests
|
| import json
|
| import sys
|
| from datetime import datetime
|
|
|
|
|
| BASE_URL = "http://localhost:8000/api/v1"
|
| API_KEY = "your-secure-api-key-here"
|
|
|
|
|
| GUVI_FORMAT_REQUEST = {
|
| "sessionId": "test-guvi-session-001",
|
| "message": {
|
| "sender": "scammer",
|
| "text": "Congratulations! You have won Rs. 10 lakh in our lucky draw. Share your OTP to claim prize immediately!",
|
| "timestamp": datetime.utcnow().isoformat() + "Z"
|
| },
|
| "conversationHistory": [],
|
| "metadata": {
|
| "channel": "SMS",
|
| "language": "English",
|
| "locale": "IN"
|
| }
|
| }
|
|
|
| OUR_FORMAT_REQUEST = {
|
| "message": "You won 10 lakh! Send OTP now to claim your prize!",
|
| "language": "auto"
|
| }
|
|
|
| GUVI_FOLLOWUP_REQUEST = {
|
| "sessionId": "test-guvi-session-001",
|
| "message": {
|
| "sender": "scammer",
|
| "text": "Send money to my UPI: scammer@paytm or call +919876543210",
|
| "timestamp": datetime.utcnow().isoformat() + "Z"
|
| },
|
| "conversationHistory": [
|
| {
|
| "sender": "scammer",
|
| "text": "Congratulations! You have won Rs. 10 lakh!",
|
| "timestamp": "2026-02-02T10:00:00Z"
|
| },
|
| {
|
| "sender": "user",
|
| "text": "Really? How do I claim it?",
|
| "timestamp": "2026-02-02T10:01:00Z"
|
| }
|
| ],
|
| "metadata": {
|
| "channel": "SMS",
|
| "language": "English",
|
| "locale": "IN"
|
| }
|
| }
|
|
|
|
|
| def print_header(title: str):
|
| """Print a formatted header."""
|
| print("\n" + "=" * 60)
|
| print(f" {title}")
|
| print("=" * 60)
|
|
|
|
|
| def print_result(test_name: str, passed: bool, details: str = ""):
|
| """Print test result."""
|
| status = "β
PASSED" if passed else "β FAILED"
|
| print(f"\n{status}: {test_name}")
|
| if details:
|
| print(f" Details: {details}")
|
|
|
|
|
| def test_health_check():
|
| """Test that health endpoint works without auth."""
|
| print_header("Test 1: Health Check (No Auth Required)")
|
|
|
| try:
|
| response = requests.get(f"{BASE_URL}/health")
|
| passed = response.status_code == 200
|
| data = response.json()
|
| print_result(
|
| "Health endpoint accessible",
|
| passed,
|
| f"Status: {data.get('status')}, Version: {data.get('version')}"
|
| )
|
| return passed
|
| except Exception as e:
|
| print_result("Health endpoint accessible", False, str(e))
|
| return False
|
|
|
|
|
| def test_missing_api_key():
|
| """Test that requests without API key are rejected."""
|
| print_header("Test 2: Missing API Key (Should Return 401)")
|
|
|
| try:
|
| response = requests.post(
|
| f"{BASE_URL}/honeypot/engage",
|
| json=OUR_FORMAT_REQUEST,
|
| headers={"Content-Type": "application/json"}
|
| )
|
|
|
|
|
| if response.status_code == 401:
|
| print_result("Missing API key rejected", True, "401 Unauthorized returned")
|
| return True
|
| elif response.status_code == 200:
|
| print_result(
|
| "Missing API key rejected",
|
| False,
|
| "Request succeeded - API_KEY might not be configured in .env"
|
| )
|
| return False
|
| else:
|
| print_result("Missing API key rejected", False, f"Status: {response.status_code}")
|
| return False
|
| except Exception as e:
|
| print_result("Missing API key rejected", False, str(e))
|
| return False
|
|
|
|
|
| def test_invalid_api_key():
|
| """Test that invalid API keys are rejected."""
|
| print_header("Test 3: Invalid API Key (Should Return 401)")
|
|
|
| try:
|
| response = requests.post(
|
| f"{BASE_URL}/honeypot/engage",
|
| json=OUR_FORMAT_REQUEST,
|
| headers={
|
| "Content-Type": "application/json",
|
| "x-api-key": "invalid-key-12345"
|
| }
|
| )
|
|
|
| if response.status_code == 401:
|
| print_result("Invalid API key rejected", True, "401 Unauthorized returned")
|
| return True
|
| elif response.status_code == 200:
|
| print_result(
|
| "Invalid API key rejected",
|
| False,
|
| "Request succeeded - API_KEY might not be configured"
|
| )
|
| return False
|
| else:
|
| print_result("Invalid API key rejected", False, f"Status: {response.status_code}")
|
| return False
|
| except Exception as e:
|
| print_result("Invalid API key rejected", False, str(e))
|
| return False
|
|
|
|
|
| def test_valid_api_key_our_format():
|
| """Test our standard format with valid API key."""
|
| print_header("Test 4: Valid API Key - Our Format")
|
|
|
| try:
|
| response = requests.post(
|
| f"{BASE_URL}/honeypot/engage",
|
| json=OUR_FORMAT_REQUEST,
|
| headers={
|
| "Content-Type": "application/json",
|
| "x-api-key": API_KEY
|
| }
|
| )
|
|
|
| if response.status_code == 200:
|
| data = response.json()
|
| print_result("Our format accepted", True, f"Session: {data.get('session_id', 'N/A')[:20]}...")
|
|
|
|
|
| has_status = "status" in data
|
| has_scam = "scam_detected" in data
|
| has_reply = "reply" in data
|
|
|
| print(f" - status field: {'β
' if has_status else 'β'}")
|
| print(f" - scam_detected field: {'β
' if has_scam else 'β'}")
|
| print(f" - reply field: {'β
' if has_reply else 'β'}")
|
|
|
| if data.get("scam_detected"):
|
| print(f" - Scam detected with confidence: {data.get('confidence', 0):.2f}")
|
| if data.get("reply"):
|
| print(f" - Agent reply: {data['reply'][:50]}...")
|
|
|
| return True
|
| else:
|
| print_result("Our format accepted", False, f"Status: {response.status_code}")
|
| print(f" Response: {response.text[:200]}")
|
| return False
|
| except Exception as e:
|
| print_result("Our format accepted", False, str(e))
|
| return False
|
|
|
|
|
| def test_guvi_format():
|
| """Test GUVI's exact input format."""
|
| print_header("Test 5: GUVI Input Format")
|
|
|
| try:
|
| response = requests.post(
|
| f"{BASE_URL}/honeypot/engage",
|
| json=GUVI_FORMAT_REQUEST,
|
| headers={
|
| "Content-Type": "application/json",
|
| "x-api-key": API_KEY
|
| }
|
| )
|
|
|
| if response.status_code == 200:
|
| data = response.json()
|
| print_result("GUVI format accepted", True)
|
|
|
|
|
| checks = {
|
| "status": "status" in data,
|
| "scam_detected": "scam_detected" in data,
|
| "session_id": "session_id" in data,
|
| "reply": "reply" in data,
|
| "agent_notes": "agent_notes" in data,
|
| }
|
|
|
| for field, present in checks.items():
|
| print(f" - {field}: {'β
' if present else 'β'}")
|
|
|
|
|
| intel = data.get("extracted_intelligence", {})
|
| if intel:
|
| has_keywords = "suspicious_keywords" in intel
|
| print(f" - suspicious_keywords: {'β
' if has_keywords else 'β'}")
|
| if has_keywords and intel["suspicious_keywords"]:
|
| print(f" Keywords: {intel['suspicious_keywords'][:5]}")
|
|
|
|
|
| if data.get("agent_notes"):
|
| print(f" - Agent Notes: {data['agent_notes'][:100]}...")
|
|
|
| return True
|
| else:
|
| print_result("GUVI format accepted", False, f"Status: {response.status_code}")
|
| print(f" Response: {response.text[:200]}")
|
| return False
|
| except Exception as e:
|
| print_result("GUVI format accepted", False, str(e))
|
| return False
|
|
|
|
|
| def test_guvi_followup():
|
| """Test GUVI format with conversation history."""
|
| print_header("Test 6: GUVI Format with Conversation History")
|
|
|
| try:
|
| response = requests.post(
|
| f"{BASE_URL}/honeypot/engage",
|
| json=GUVI_FOLLOWUP_REQUEST,
|
| headers={
|
| "Content-Type": "application/json",
|
| "x-api-key": API_KEY
|
| }
|
| )
|
|
|
| if response.status_code == 200:
|
| data = response.json()
|
| print_result("GUVI followup accepted", True)
|
|
|
|
|
| intel = data.get("extracted_intelligence", {})
|
| if intel:
|
| upi_ids = intel.get("upi_ids", [])
|
| phones = intel.get("phone_numbers", [])
|
|
|
| has_upi = len(upi_ids) > 0
|
| has_phone = len(phones) > 0
|
|
|
| print(f" - UPI IDs extracted: {'β
' if has_upi else 'β'} {upi_ids}")
|
| print(f" - Phone numbers extracted: {'β
' if has_phone else 'β'} {phones}")
|
|
|
| if data.get("reply"):
|
| print(f" - Reply: {data['reply'][:80]}...")
|
|
|
| return True
|
| else:
|
| print_result("GUVI followup accepted", False, f"Status: {response.status_code}")
|
| return False
|
| except Exception as e:
|
| print_result("GUVI followup accepted", False, str(e))
|
| return False
|
|
|
|
|
| def main():
|
| """Run all GUVI integration tests."""
|
| print("\n" + "=" * 60)
|
| print(" GUVI INTEGRATION TEST SUITE")
|
| print("=" * 60)
|
| print(f"\nBase URL: {BASE_URL}")
|
| print(f"API Key: {API_KEY[:10]}...")
|
|
|
| results = []
|
|
|
|
|
| results.append(("Health Check", test_health_check()))
|
| results.append(("Missing API Key", test_missing_api_key()))
|
| results.append(("Invalid API Key", test_invalid_api_key()))
|
| results.append(("Our Format", test_valid_api_key_our_format()))
|
| results.append(("GUVI Format", test_guvi_format()))
|
| results.append(("GUVI Followup", test_guvi_followup()))
|
|
|
|
|
| print_header("TEST SUMMARY")
|
| passed = sum(1 for _, r in results if r)
|
| total = len(results)
|
|
|
| for name, result in results:
|
| status = "β
" if result else "β"
|
| print(f" {status} {name}")
|
|
|
| print(f"\n Total: {passed}/{total} tests passed")
|
|
|
| if passed == total:
|
| print("\n π All tests passed! Ready for GUVI submission.")
|
| return 0
|
| else:
|
| print("\n β οΈ Some tests failed. Check configuration.")
|
| return 1
|
|
|
|
|
| if __name__ == "__main__":
|
| sys.exit(main())
|
|
|