Add tests for Kommunikation Sync implementation and verification scripts

- Implemented comprehensive tests for the Kommunikation Sync functionality, covering base64 encoding, marker parsing, creation, type detection, and integration scenarios.
- Added a verification script to check for unique IDs in Advoware communications, ensuring stability and integrity of the IDs.
- Created utility scripts for code validation, notification testing, and PUT response detail analysis to enhance development and testing processes.
- Updated README with details on new tools and their usage.
This commit is contained in:
2026-02-08 23:05:56 +00:00
parent a157d3fa1d
commit 7856dd1d68
37 changed files with 438 additions and 271 deletions

View File

@@ -0,0 +1,466 @@
#!/usr/bin/env python3
"""
Test: Deaktivierung via gueltigBis + reihenfolgeIndex-Verhalten
================================================================
Ziele:
1. Teste ob abgelaufene Adressen (gueltigBis < heute) ausgeblendet werden
2. Teste ob man reihenfolgeIndex beim POST setzen kann
3. Teste ob neue Adressen automatisch ans Ende rutschen
4. Teste ob man reihenfolgeIndex via PUT ändern kann (Sortierung)
"""
import asyncio
import sys
import os
from datetime import datetime, timedelta
# Add parent directory to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from services.advoware import AdvowareAPI
# Test-Konfiguration
TEST_BETNR = 104860
# ANSI Color codes
BOLD = '\033[1m'
RED = '\033[91m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
BLUE = '\033[94m'
RESET = '\033[0m'
def print_header(text):
print(f"\n{BOLD}{'='*80}{RESET}")
print(f"{BOLD}{text}{RESET}")
print(f"{BOLD}{'='*80}{RESET}\n")
def print_success(text):
print(f"{GREEN}{text}{RESET}")
def print_error(text):
print(f"{RED}{text}{RESET}")
def print_warning(text):
print(f"{YELLOW}{text}{RESET}")
def print_info(text):
print(f"{BLUE} {text}{RESET}")
class SimpleLogger:
"""Minimal logger für AdvowareAPI"""
def info(self, msg): pass
def error(self, msg): print_error(msg)
def debug(self, msg): pass
def warning(self, msg): pass
class SimpleContext:
"""Minimal context für AdvowareAPI"""
def __init__(self):
self.logger = SimpleLogger()
def log_info(self, msg): pass
def log_error(self, msg): print_error(msg)
def log_debug(self, msg): pass
async def test_1_create_expired_address():
"""Test 1: Erstelle Adresse mit gueltigBis in der Vergangenheit"""
print_header("TEST 1: Adresse mit gueltigBis in Vergangenheit (abgelaufen)")
context = SimpleContext()
advo = AdvowareAPI(context=context)
# Datum in der Vergangenheit
expired_date = "2023-12-31T23:59:59"
address_data = {
"strasse": "Abgelaufene Straße 99",
"plz": "99999",
"ort": "Vergangenheit",
"land": "DE",
"bemerkung": "TEST-ABGELAUFEN: Diese Adresse ist seit 2023 ungültig",
"gueltigVon": "2020-01-01T00:00:00",
"gueltigBis": expired_date # ← In der Vergangenheit!
}
print_info(f"Erstelle Adresse mit gueltigBis: {expired_date} (vor 2+ Jahren)")
try:
result = await advo.api_call(
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
method='POST',
json_data=address_data
)
if result and len(result) > 0:
addr = result[0]
print_success(f"✓ Adresse erstellt: rowId={addr.get('rowId')}")
print_info(f" gueltigBis: {addr.get('gueltigBis')}")
print_info(f" reihenfolgeIndex: {addr.get('reihenfolgeIndex')}")
return addr.get('bemerkung')
else:
print_error("POST lieferte keine Response")
return None
except Exception as e:
print_error(f"Fehler: {e}")
import traceback
traceback.print_exc()
return None
async def test_2_check_if_expired_address_visible():
"""Test 2: Prüfe ob abgelaufene Adresse in GET sichtbar ist"""
print_header("TEST 2: Ist abgelaufene Adresse in GET sichtbar?")
context = SimpleContext()
advo = AdvowareAPI(context=context)
try:
all_addresses = await advo.api_call(
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
method='GET'
)
print_info(f"Gesamtanzahl Adressen: {len(all_addresses)}")
# Suche abgelaufene Adresse
expired_found = None
active_count = 0
expired_count = 0
today = datetime.now()
for addr in all_addresses:
bemerkung = addr.get('bemerkung') or ''
gueltig_bis = addr.get('gueltigBis')
if 'TEST-ABGELAUFEN' in bemerkung:
expired_found = addr
print_success(f"\n✓ Abgelaufene Test-Adresse gefunden!")
print_info(f" Index: {addr.get('reihenfolgeIndex')}")
print_info(f" gueltigBis: {gueltig_bis}")
print_info(f" Straße: {addr.get('strasse')}")
# Zähle aktive vs. abgelaufene
if gueltig_bis:
try:
bis_date = datetime.fromisoformat(gueltig_bis.replace('Z', '+00:00'))
if bis_date < today:
expired_count += 1
else:
active_count += 1
except:
pass
print(f"\n{BOLD}Statistik:{RESET}")
print(f" Aktive Adressen (gueltigBis > heute): {active_count}")
print(f" Abgelaufene Adressen (gueltigBis < heute): {expired_count}")
print(f" Ohne gueltigBis: {len(all_addresses) - active_count - expired_count}")
if expired_found:
print_error("\n❌ WICHTIG: Abgelaufene Adressen werden NICHT gefiltert!")
print_warning("⚠ GET /Adressen zeigt ALLE Adressen, auch abgelaufene")
print_info("💡 Filtern nach gueltigBis muss CLIENT-seitig erfolgen")
return True
else:
print_success("\n✓ Abgelaufene Adresse nicht sichtbar (wird gefiltert)")
return False
except Exception as e:
print_error(f"Fehler: {e}")
import traceback
traceback.print_exc()
return None
async def test_3_create_with_explicit_reihenfolgeIndex():
"""Test 3: Versuche reihenfolgeIndex beim POST zu setzen"""
print_header("TEST 3: Kann man reihenfolgeIndex beim POST setzen?")
context = SimpleContext()
advo = AdvowareAPI(context=context)
# Versuche mit explizitem Index
address_data = {
"reihenfolgeIndex": 999, # ← Versuche expliziten Index
"strasse": "Test Index 999",
"plz": "88888",
"ort": "Indextest",
"land": "DE",
"bemerkung": "TEST-INDEX: Versuch mit explizitem reihenfolgeIndex=999"
}
print_info("Versuche POST mit reihenfolgeIndex=999...")
try:
result = await advo.api_call(
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
method='POST',
json_data=address_data
)
if result and len(result) > 0:
addr = result[0]
actual_index = addr.get('reihenfolgeIndex')
print_info(f"Response reihenfolgeIndex: {actual_index}")
# Hole alle Adressen und prüfe wo sie gelandet ist
all_addresses = await advo.api_call(
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
method='GET'
)
found = None
for a in all_addresses:
if (a.get('bemerkung') or '').startswith('TEST-INDEX'):
found = a
break
if found:
real_index = found.get('reihenfolgeIndex')
print_info(f"GET zeigt reihenfolgeIndex: {real_index}")
if real_index == 999:
print_success("\n✓ reihenfolgeIndex kann explizit gesetzt werden!")
print_warning("⚠ ABER: Das könnte bestehende Adressen verschieben!")
elif real_index == 0:
print_warning("\n⚠ POST gibt reihenfolgeIndex=0 zurück")
print_info("→ Echter Index wird erst nach GET sichtbar")
else:
print_error(f"\n❌ reihenfolgeIndex={real_index} ignoriert Vorgabe (999)")
print_success("✓ Index wird automatisch vergeben (ans Ende)")
return real_index
except Exception as e:
print_error(f"Fehler: {e}")
import traceback
traceback.print_exc()
return None
async def test_4_create_multiple_check_ordering():
"""Test 4: Erstelle mehrere Adressen und prüfe Reihenfolge"""
print_header("TEST 4: Mehrere neue Adressen - werden sie ans Ende gereiht?")
context = SimpleContext()
advo = AdvowareAPI(context=context)
print_info("Hole aktuelle Adressen...")
all_before = await advo.api_call(
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
method='GET'
)
max_index_before = max([a.get('reihenfolgeIndex', 0) for a in all_before])
count_before = len(all_before)
print_info(f" Anzahl vorher: {count_before}")
print_info(f" Höchster Index: {max_index_before}")
# Erstelle 3 neue Adressen
print_info("\nErstelle 3 neue Adressen...")
created_ids = []
for i in range(1, 4):
address_data = {
"strasse": f"Reihenfolge-Test {i}",
"plz": f"7777{i}",
"ort": f"Stadt-{i}",
"land": "DE",
"bemerkung": f"TEST-REIHENFOLGE-{i}"
}
try:
result = await advo.api_call(
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
method='POST',
json_data=address_data
)
if result and len(result) > 0:
created_ids.append(f"TEST-REIHENFOLGE-{i}")
print_success(f" ✓ Adresse {i} erstellt")
except Exception as e:
print_error(f" ✗ Fehler bei Adresse {i}: {e}")
# Hole alle Adressen erneut
print_info("\nHole Adressen erneut...")
all_after = await advo.api_call(
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
method='GET'
)
count_after = len(all_after)
print_info(f" Anzahl nachher: {count_after}")
print_info(f" Neue Adressen: {count_after - count_before}")
# Finde unsere Test-Adressen
print(f"\n{BOLD}Reihenfolge der neuen Test-Adressen:{RESET}")
test_addresses = []
for addr in all_after:
bemerkung = addr.get('bemerkung') or ''
if 'TEST-REIHENFOLGE-' in bemerkung:
test_addresses.append({
'bemerkung': bemerkung,
'index': addr.get('reihenfolgeIndex'),
'strasse': addr.get('strasse')
})
test_addresses.sort(key=lambda x: x['index'])
for t in test_addresses:
print(f" Index {t['index']:2d}: {t['bemerkung']} ({t['strasse']})")
# Analyse
if len(test_addresses) >= 3:
indices = [t['index'] for t in test_addresses[-3:]] # Letzten 3
if indices == sorted(indices) and indices[-1] > max_index_before:
print_success("\n✓✓✓ Neue Adressen werden automatisch ANS ENDE gereiht!")
print_success("✓ Indices sind aufsteigend und fortlaufend")
print_info(f" Neue Indices: {indices}")
else:
print_warning(f"\n⚠ Unerwartete Reihenfolge: {indices}")
return test_addresses
async def test_5_try_change_reihenfolgeIndex_via_put():
"""Test 5: Versuche reihenfolgeIndex via PUT zu ändern"""
print_header("TEST 5: Kann man reihenfolgeIndex via PUT ändern?")
context = SimpleContext()
advo = AdvowareAPI(context=context)
try:
# Finde Test-Adresse
all_addresses = await advo.api_call(
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
method='GET'
)
test_addr = None
for addr in all_addresses:
bemerkung = addr.get('bemerkung') or ''
if 'TEST-REIHENFOLGE-1' in bemerkung:
test_addr = addr
break
if not test_addr:
print_error("Test-Adresse nicht gefunden")
return False
current_index = test_addr.get('reihenfolgeIndex')
new_index = 1 # Versuche an erste Position zu setzen
print_info(f"Aktueller Index: {current_index}")
print_info(f"Versuche Index zu ändern auf: {new_index}")
# PUT mit neuem reihenfolgeIndex
update_data = {
"reihenfolgeIndex": new_index,
"strasse": test_addr.get('strasse'),
"plz": test_addr.get('plz'),
"ort": test_addr.get('ort'),
"land": test_addr.get('land')
}
await advo.api_call(
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen/{current_index}',
method='PUT',
json_data=update_data
)
print_success("✓ PUT erfolgreich")
# Prüfe Ergebnis
print_info("\nPrüfe neuen Index...")
all_after = await advo.api_call(
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
method='GET'
)
for addr in all_after:
bemerkung = addr.get('bemerkung') or ''
if 'TEST-REIHENFOLGE-1' in bemerkung:
result_index = addr.get('reihenfolgeIndex')
print_info(f"Index nach PUT: {result_index}")
if result_index == new_index:
print_success("\n✓✓✓ reihenfolgeIndex KANN via PUT geändert werden!")
print_warning("⚠ Das könnte andere Adressen verschieben!")
else:
print_error(f"\n❌ reihenfolgeIndex NICHT änderbar (bleibt {result_index})")
print_success("✓ Index ist READ-ONLY bei PUT")
return result_index == new_index
except Exception as e:
print_error(f"Fehler: {e}")
import traceback
traceback.print_exc()
return None
async def main():
print(f"\n{BOLD}╔══════════════════════════════════════════════════════════════╗{RESET}")
print(f"{BOLD}║ Deaktivierung + reihenfolgeIndex Tests für Adressen ║{RESET}")
print(f"{BOLD}╚══════════════════════════════════════════════════════════════╝{RESET}\n")
print(f"Test-Konfiguration:")
print(f" BetNr: {TEST_BETNR}")
print(f" Datum: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
# Test 1: Abgelaufene Adresse erstellen
await test_1_create_expired_address()
# Test 2: Ist abgelaufene Adresse sichtbar?
visible = await test_2_check_if_expired_address_visible()
# Test 3: Expliziter reihenfolgeIndex
await test_3_create_with_explicit_reihenfolgeIndex()
# Test 4: Mehrere Adressen - Reihenfolge
await test_4_create_multiple_check_ordering()
# Test 5: reihenfolgeIndex ändern via PUT
changeable = await test_5_try_change_reihenfolgeIndex_via_put()
# Finale Zusammenfassung
print(f"\n{BOLD}╔══════════════════════════════════════════════════════════════╗{RESET}")
print(f"{BOLD}║ FINALE ERKENNTNISSE ║{RESET}")
print(f"{BOLD}╚══════════════════════════════════════════════════════════════╝{RESET}\n")
print(f"{BOLD}1. Deaktivierung via gueltigBis:{RESET}")
if visible:
print_error(" ❌ Abgelaufene Adressen werden NICHT automatisch gefiltert")
print_warning(" ⚠ GET /Adressen zeigt alle Adressen (auch abgelaufen)")
print_info(" 💡 Soft-Delete via gueltigBis ist möglich")
print_info(" 💡 Aber: Filtern muss CLIENT-seitig erfolgen")
print_info(" 💡 Strategie: In EspoCRM als 'inactive' markieren wenn gueltigBis < heute")
else:
print_success(" ✓ Abgelaufene Adressen werden automatisch ausgeblendet")
print_success(" ✓ gueltigBis eignet sich perfekt für Soft-Delete")
print(f"\n{BOLD}2. reihenfolgeIndex Verhalten:{RESET}")
print_info(" • Neue Adressen werden automatisch ans Ende gereiht")
print_info(" • Index wird vom System vergeben (fortlaufend)")
if changeable:
print_warning(" ⚠ reihenfolgeIndex kann via PUT geändert werden")
print_warning(" ⚠ Vorsicht: Könnte andere Adressen verschieben")
else:
print_success(" ✓ reihenfolgeIndex ist READ-ONLY bei PUT (stabil)")
print(f"\n{BOLD}3. Sync-Empfehlungen:{RESET}")
print_success(" ✓ Nutze 'bemerkung' für EspoCRM-ID Matching (stabil)")
print_success(" ✓ Nutze 'gueltigBis' für Soft-Delete (setze auf gestern)")
print_success(" ✓ Nutze 'reihenfolgeIndex' nur für PUT (nicht für Matching)")
print_info(" 💡 Workflow: GET → parse bemerkung → match → PUT via Index")
print(f"\n{YELLOW}⚠️ ACHTUNG: Test-Adressen mit 'TEST-' im bemerkung-Feld{RESET}")
print(f"{YELLOW} sollten manuell bereinigt werden.{RESET}\n")
if __name__ == "__main__":
asyncio.run(main())