All checks were successful
Docker Build & Push SugarCRM 6.5 CE / build-and-push (push) Successful in 12s
334 lines
10 KiB
Python
Executable File
334 lines
10 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
SugarCRM 6.5 CE — Erweiterte API-Tests
|
|
========================================
|
|
Testet alle wichtigen REST v4.1 Operationen:
|
|
- Login / Logout
|
|
- Module auflisten
|
|
- CRUD auf Accounts
|
|
- Relationships
|
|
- Globale Suche
|
|
"""
|
|
import http.client
|
|
import json
|
|
import hashlib
|
|
import urllib.parse
|
|
import sys
|
|
import os
|
|
from datetime import datetime
|
|
|
|
BASE_HOST = os.environ.get("SUGARCRM_HOST", "localhost")
|
|
BASE_PORT = os.environ.get("SUGARCRM_PORT", "2080")
|
|
ENDPOINT = "/service/v4_1/rest.php"
|
|
BASE_URL = f"{BASE_HOST}:{BASE_PORT}"
|
|
|
|
USER = os.environ.get("SUGARCRM_USER", "admin")
|
|
PASSWORD = os.environ.get("SUGARCRM_PASSWORD", "admin123")
|
|
PWD_HASH = hashlib.md5(PASSWORD.encode()).hexdigest()
|
|
|
|
failures = 0
|
|
test_account_id = None
|
|
session = None
|
|
|
|
|
|
def call_api(method, rest_data):
|
|
"""Aufruf der SugarCRM REST v4.1 API"""
|
|
conn = http.client.HTTPConnection(BASE_URL, timeout=30)
|
|
body = urllib.parse.urlencode({
|
|
"method": method,
|
|
"input_type": "JSON",
|
|
"response_type": "JSON",
|
|
"rest_data": json.dumps(rest_data)
|
|
})
|
|
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
|
try:
|
|
conn.request("POST", ENDPOINT, body, headers)
|
|
resp = conn.getresponse()
|
|
if resp.status == 302:
|
|
return {"_error": f"Redirect (AdminWizard active?)"}
|
|
data = json.loads(resp.read().decode())
|
|
conn.close()
|
|
return data
|
|
except Exception as e:
|
|
return {"_error": str(e)}
|
|
|
|
|
|
def test(name, fn):
|
|
global failures
|
|
try:
|
|
result = fn()
|
|
if result:
|
|
print(f" ✅ {name}")
|
|
return result
|
|
else:
|
|
print(f" ❌ {name}")
|
|
failures += 1
|
|
return None
|
|
except Exception as e:
|
|
print(f" ❌ {name}: {e}")
|
|
failures += 1
|
|
return None
|
|
|
|
|
|
def main():
|
|
global session, test_account_id
|
|
|
|
print("=" * 60)
|
|
print("🍬 SugarCRM 6.5.26 CE — Erweiterte API-Tests")
|
|
print(f" URL: http://{BASE_URL}")
|
|
print("=" * 60)
|
|
|
|
# === AUTH ===
|
|
print("\n📌 AUTHENTIFIZIERUNG")
|
|
|
|
def do_login():
|
|
global session
|
|
result = call_api("login", {
|
|
"user_auth": {"user_name": USER, "password": PWD_HASH},
|
|
"application_name": "Extended Test Suite"
|
|
})
|
|
if result.get("_error"):
|
|
return False
|
|
if result.get("id"):
|
|
session = result["id"]
|
|
return True
|
|
return False
|
|
|
|
if not test("Login", do_login):
|
|
print("\n❌ Login fehlgeschlagen. Tests abgebrochen.")
|
|
print(" Prüfe: Läuft der Container? Admin Wizard deaktiviert?")
|
|
sys.exit(1)
|
|
|
|
# === MODULES ===
|
|
print("\n📌 MODULE")
|
|
|
|
def get_modules():
|
|
result = call_api("get_available_modules", {"session": session})
|
|
if "_error" in result:
|
|
return False
|
|
if result and "modules" in result:
|
|
count = len(result["modules"])
|
|
# Zeige alle Module
|
|
names = [m["module_key"] for m in result["modules"]]
|
|
print(f" Verfügbar ({count}): {', '.join(names)}")
|
|
return True
|
|
return False
|
|
|
|
test("Verfügbare Module", get_modules)
|
|
|
|
def get_module_fields():
|
|
result = call_api("get_module_fields", {
|
|
"session": session,
|
|
"module_name": "Accounts",
|
|
"fields": []
|
|
})
|
|
if "_error" in result:
|
|
return False
|
|
if result and "module_fields" in result:
|
|
fields = [f["name"] for f in result["module_fields"].values()]
|
|
print(f" Accounts Felder ({len(fields)}): {', '.join(fields)}")
|
|
return True
|
|
return False
|
|
|
|
test("Accounts Felder", get_module_fields)
|
|
|
|
# === CRUD: Accounts ===
|
|
print("\n📌 CRUD: ACCOUNTS")
|
|
|
|
def create_account():
|
|
global test_account_id
|
|
name = f"API Test {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
|
result = call_api("set_entry", {
|
|
"session": session,
|
|
"module_name": "Accounts",
|
|
"name_value_list": {
|
|
"name": {"name": "name", "value": name},
|
|
"account_type": {"name": "account_type", "value": "Customer"},
|
|
"industry": {"name": "industry", "value": "Technology"},
|
|
"phone_office": {"name": "phone_office", "value": "+49 30 1234567"},
|
|
"description": {"name": "description", "value": "API erstellter Test-Account"},
|
|
}
|
|
})
|
|
if result.get("id"):
|
|
test_account_id = result["id"]
|
|
print(f" Name: '{name}', ID: {test_account_id[:10]}...")
|
|
return True
|
|
return False
|
|
|
|
test("Account erstellen (set_entry)", create_account)
|
|
|
|
def read_account():
|
|
if not test_account_id:
|
|
return False
|
|
result = call_api("get_entry", {
|
|
"session": session,
|
|
"module_name": "Accounts",
|
|
"id": test_account_id,
|
|
"select_fields": ["name", "account_type", "industry", "phone_office", "description"]
|
|
})
|
|
if "_error" in result:
|
|
return False
|
|
if result and "entry_list" in result:
|
|
vals = {n["name"]: n["value"] for n in result["entry_list"][0]["name_value_list"]}
|
|
print(f" Name: {vals.get('name')}, Typ: {vals.get('account_type')}, "
|
|
f"Tel: {vals.get('phone_office')}, Branche: {vals.get('industry')}")
|
|
return True
|
|
return False
|
|
|
|
test("Account lesen (get_entry)", read_account)
|
|
|
|
def update_account():
|
|
if not test_account_id:
|
|
return False
|
|
result = call_api("set_entry", {
|
|
"session": session,
|
|
"module_name": "Accounts",
|
|
"name_value_list": {
|
|
"id": {"name": "id", "value": test_account_id},
|
|
"phone_office": {"name": "phone_office", "value": "+49 30 999888777"},
|
|
"description": {"name": "description", "value": "AKTUALISIERT via API"},
|
|
}
|
|
})
|
|
return bool(result.get("id"))
|
|
|
|
test("Account updaten (set_entry mit ID)", update_account)
|
|
|
|
def delete_account():
|
|
if not test_account_id:
|
|
return False
|
|
result = call_api("set_entry", {
|
|
"session": session,
|
|
"module_name": "Accounts",
|
|
"name_value_list": {
|
|
"id": {"name": "id", "value": test_account_id},
|
|
"deleted": {"name": "deleted", "value": "1"},
|
|
}
|
|
})
|
|
return bool(result.get("id"))
|
|
|
|
test("Account löschen (soft delete)", delete_account)
|
|
|
|
# === LIST ===
|
|
print("\n📌 LIST & FILTER")
|
|
|
|
def list_accounts():
|
|
result = call_api("get_entry_list", {
|
|
"session": session,
|
|
"module_name": "Accounts",
|
|
"query": "",
|
|
"order_by": "date_entered DESC",
|
|
"offset": 0,
|
|
"select_fields": ["name", "id", "date_entered", "account_type"],
|
|
"max_results": 5,
|
|
"deleted": 0
|
|
})
|
|
if "_error" in result:
|
|
return False
|
|
if result and "entry_list" in result:
|
|
count = result.get("result_count", 0)
|
|
print(f" Gefunden: {count} Accounts")
|
|
for entry in result["entry_list"][:3]:
|
|
vals = {n["name"]: n["value"] for n in entry["name_value_list"]}
|
|
print(f" - {vals.get('name', 'N/A')} ({vals.get('account_type', 'N/A')})")
|
|
return True
|
|
return False
|
|
|
|
test("Accounts auflisten", list_accounts)
|
|
|
|
def search_accounts():
|
|
result = call_api("search_by_module", {
|
|
"session": session,
|
|
"search_string": "API",
|
|
"modules": ["Accounts"],
|
|
"offset": 0,
|
|
"max_results": 10,
|
|
})
|
|
if result and "entry_list" in result:
|
|
count = result.get("entry_list", [])
|
|
print(f" Suche 'API': {len(count)} Treffer in Accounts")
|
|
return True
|
|
return False
|
|
|
|
test("Globale Suche nach 'API'", search_accounts)
|
|
"search_string": "API",
|
|
"modules": ["Accounts"],
|
|
"offset": 0,
|
|
"max_results": 10,
|
|
})
|
|
if result and "entry_list" in result:
|
|
count = result.get("entry_list", [])
|
|
print(f" Suche 'API': {len(count)} Treffer in Accounts")
|
|
return True
|
|
return False
|
|
|
|
test("Globale Suche nach 'API'", search_by_module)
|
|
|
|
# === RELATIONSHIPS ===
|
|
print("\n📌 RELATIONSHIPS")
|
|
|
|
def create_contact():
|
|
result = call_api("set_entry", {
|
|
"session": session,
|
|
"module_name": "Contacts",
|
|
"name_value_list": {
|
|
"first_name": {"name": "first_name", "value": "Max"},
|
|
"last_name": {"name": "last_name", "value": "Mustermann"},
|
|
"email1": {"name": "email1", "value": "max@example.com"},
|
|
}
|
|
})
|
|
if result.get("id"):
|
|
print(f" Contact ID: {result['id'][:10]}...")
|
|
return result["id"]
|
|
return None
|
|
|
|
contact_id = test("Kontakt erstellen", create_contact)
|
|
|
|
def create_account_for_rel():
|
|
result = call_api("set_entry", {
|
|
"session": session,
|
|
"module_name": "Accounts",
|
|
"name_value_list": {
|
|
"name": {"name": "name", "value": f"Rel Test {datetime.now().strftime('%H:%M')}"},
|
|
}
|
|
})
|
|
if result.get("id"):
|
|
return result["id"]
|
|
return None
|
|
|
|
account_id = test("Account für Relationship", create_account_for_rel)
|
|
|
|
def set_relationship():
|
|
if not account_id or not contact_id:
|
|
print(" ⏭️ Überspringe (kein Account/Kontakt)")
|
|
return True # Don't count as failure
|
|
result = call_api("set_relationship", {
|
|
"session": session,
|
|
"module_name": "Accounts",
|
|
"module_id": account_id,
|
|
"link_field_name": "contacts",
|
|
"related_ids": [contact_id],
|
|
})
|
|
return result.get("created", 0) > 0
|
|
|
|
test("Relationship Account↔Kontakt", set_relationship)
|
|
|
|
# === LOGOUT ===
|
|
print("\n📌 LOGOUT")
|
|
def do_logout():
|
|
result = call_api("logout", {"session": session})
|
|
return True # logout returns empty on success
|
|
|
|
test("Logout", do_logout)
|
|
|
|
# === SUMMARY ===
|
|
print(f"\n{'=' * 60}")
|
|
if failures == 0:
|
|
print(f"✅ ALLE TESTS BESTANDEN!")
|
|
else:
|
|
print(f"⚠️ {failures} Test(s) fehlgeschlagen")
|
|
print(f"{'=' * 60}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|