gateway/tests/test_phase123_basic.py
2026-04-10 22:44:08 +02:00

328 lines
17 KiB
Python

"""
Basic verification tests for Phase 1-3 implementation.
Run with: python tests/test_phase123_basic.py
Requires: gateway running on localhost:8000
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
print("=" * 60)
print("PHASE 1-3 BASIC VERIFICATION")
print("=" * 60)
errors = []
passes = []
def _check(label, condition, detail=""):
if condition:
passes.append(label)
print(f" [PASS] {label}")
else:
errors.append(f"{label}: {detail}")
print(f" [FAIL] {label}{detail}")
# ── Phase 1: Data Models ──────────────────────────────────────────────────────
print("\n--- Phase 1: Data Models ---")
try:
from modules.datamodels.datamodelUam import Mandate
m = Mandate(name="test", label="test")
_check("Mandate has isSystem field", hasattr(m, "isSystem"))
_check("Mandate isSystem default False", m.isSystem is False)
_check("Mandate no mandateType field", not hasattr(m, "mandateType"))
except Exception as e:
errors.append(f"Phase 1 DataModel: {e}")
print(f" [FAIL] Phase 1 DataModel import: {e}")
try:
from modules.datamodels.datamodelSubscription import SubscriptionStatusEnum, BUILTIN_PLANS, SubscriptionPlan
_check("PENDING status exists", hasattr(SubscriptionStatusEnum, "PENDING"))
_check("BUILTIN_PLANS has TRIAL_14D", "TRIAL_14D" in BUILTIN_PLANS)
trial = BUILTIN_PLANS["TRIAL_14D"]
_check("TRIAL_14D has maxDataVolumeMB", hasattr(trial, "maxDataVolumeMB"))
_check("TRIAL_14D maxDataVolumeMB=1024", trial.maxDataVolumeMB == 1024)
_check("TRIAL_14D has includedModules", hasattr(trial, "includedModules"))
_check("TRIAL_14D includedModules=2", trial.includedModules == 2)
_check("TRIAL_14D trialDays=14", trial.trialDays == 14)
except Exception as e:
errors.append(f"Phase 1 Subscription: {e}")
print(f" [FAIL] Phase 1 Subscription: {e}")
# ── Phase 2: Scope Fields ─────────────────────────────────────────────────────
print("\n--- Phase 2: Scope Fields on Models ---")
try:
from modules.datamodels.datamodelFiles import FileItem
fi = FileItem(fileName="test.txt", mimeType="text/plain", fileHash="abc", fileSize=100)
_check("FileItem has scope field", hasattr(fi, "scope"))
_check("FileItem scope default=personal", fi.scope == "personal")
_check("FileItem has neutralize field", hasattr(fi, "neutralize"))
_check("FileItem neutralize default=False", fi.neutralize == False)
except Exception as e:
errors.append(f"Phase 2 FileItem: {e}")
print(f" [FAIL] Phase 2 FileItem: {e}")
try:
from modules.datamodels.datamodelDataSource import DataSource
ds = DataSource(connectionId="c1", sourceType="sharepoint", path="/test", label="Test")
_check("DataSource has scope field", hasattr(ds, "scope"))
_check("DataSource scope default=personal", ds.scope == "personal")
_check("DataSource has neutralize field", hasattr(ds, "neutralize"))
_check("DataSource neutralize default=False", ds.neutralize == False)
except Exception as e:
errors.append(f"Phase 2 DataSource: {e}")
print(f" [FAIL] Phase 2 DataSource: {e}")
try:
from modules.datamodels.datamodelKnowledge import FileContentIndex
fci = FileContentIndex(userId="u1", fileName="test.txt", mimeType="text/plain")
_check("FileContentIndex has scope field", hasattr(fci, "scope"))
_check("FileContentIndex scope default=personal", fci.scope == "personal")
_check("FileContentIndex has neutralizationStatus", hasattr(fci, "neutralizationStatus"))
_check("FileContentIndex neutralizationStatus default=None", fci.neutralizationStatus is None)
except Exception as e:
errors.append(f"Phase 2 FileContentIndex: {e}")
print(f" [FAIL] Phase 2 FileContentIndex: {e}")
# ── Phase 2: RAG Scope Filtering ──────────────────────────────────────────────
print("\n--- Phase 2: RAG Scope Logic ---")
try:
from modules.interfaces.interfaceDbKnowledge import KnowledgeObjects
_check("KnowledgeObjects has _getScopedFileIds", hasattr(KnowledgeObjects, "_getScopedFileIds"))
_check("KnowledgeObjects has _buildScopeFilter", hasattr(KnowledgeObjects, "_buildScopeFilter"))
import inspect
sig = inspect.signature(KnowledgeObjects._getScopedFileIds)
params = list(sig.parameters.keys())
_check("_getScopedFileIds has isSysAdmin param", "isSysAdmin" in params)
sig2 = inspect.signature(KnowledgeObjects.semanticSearch)
params2 = list(sig2.parameters.keys())
_check("semanticSearch has scope param", "scope" in params2)
_check("semanticSearch has isSysAdmin param", "isSysAdmin" in params2)
except Exception as e:
errors.append(f"Phase 2 RAG: {e}")
print(f" [FAIL] Phase 2 RAG: {e}")
# ── Phase 3: Neutralization Methods ───────────────────────────────────────────
print("\n--- Phase 3: Neutralization Integration ---")
try:
from modules.workflows.workflowManager import WorkflowManager
_check("WorkflowManager has _neutralizePromptIfRequired", hasattr(WorkflowManager, "_neutralizePromptIfRequired"))
_check("WorkflowManager has _rehydrateResponseIfNeeded", hasattr(WorkflowManager, "_rehydrateResponseIfNeeded"))
import inspect
sig_n = inspect.signature(WorkflowManager._neutralizePromptIfRequired)
_check("_neutralizePromptIfRequired is async", inspect.iscoroutinefunction(WorkflowManager._neutralizePromptIfRequired))
sig_r = inspect.signature(WorkflowManager._rehydrateResponseIfNeeded)
_check("_rehydrateResponseIfNeeded is async", inspect.iscoroutinefunction(WorkflowManager._rehydrateResponseIfNeeded))
except Exception as e:
errors.append(f"Phase 3 WorkflowManager: {e}")
print(f" [FAIL] Phase 3 WorkflowManager: {e}")
# ── Phase 3: Fail-Safe Logic ──────────────────────────────────────────────────
print("\n--- Phase 3: Fail-Safe Logic ---")
try:
import ast
with open(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"modules", "workflows", "methods", "methodContext", "actions", "neutralizeData.py"), "r") as f:
source = f.read()
_check("neutralizeData.py has 'SKIPPING' fail-safe", "SKIPPING" in source)
_check("neutralizeData.py has 'do NOT pass original' comment", "do NOT pass original" in source.lower() or "not passing original" in source.lower())
_check("neutralizeData.py uses continue for skip", "continue" in source)
except Exception as e:
errors.append(f"Phase 3 Fail-Safe: {e}")
print(f" [FAIL] Phase 3 Fail-Safe: {e}")
# ── Phase 2: Route Endpoints ──────────────────────────────────────────────────
print("\n--- Phase 2: API Endpoints ---")
try:
import ast
with open(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"modules", "routes", "routeDataFiles.py"), "r") as f:
source = f.read()
_check("routeDataFiles has PATCH scope endpoint", "updateFileScope" in source)
_check("routeDataFiles has PATCH neutralize endpoint", "updateFileNeutralize" in source)
_check("routeDataFiles checks global sysAdmin", "hasSysAdminRole" in source or "sysadmin" in source.lower())
except Exception as e:
errors.append(f"Phase 2 Routes: {e}")
print(f" [FAIL] Phase 2 Routes: {e}")
# ── Phase 1: Store Endpoints ──────────────────────────────────────────────────
print("\n--- Phase 1: Store Endpoints ---")
try:
with open(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"modules", "routes", "routeStore.py"), "r") as f:
source = f.read()
_check("routeStore has listUserMandates", "listUserMandates" in source or "list_user_mandates" in source)
_check("routeStore has getSubscriptionInfo", "getSubscriptionInfo" in source or "get_subscription_info" in source)
_check("routeStore has orphan control", "orphan" in source.lower() or "last" in source.lower())
except Exception as e:
errors.append(f"Phase 1 Store: {e}")
print(f" [FAIL] Phase 1 Store: {e}")
# ── Phase 1: Provisioning ─────────────────────────────────────────────────────
print("\n--- Phase 1: Provisioning ---")
try:
with open(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"modules", "interfaces", "interfaceDbApp.py"), "r") as f:
source = f.read()
_check("interfaceDbApp has _provisionMandateForUser", "_provisionMandateForUser" in source)
_check("interfaceDbApp has _activatePendingSubscriptions", "_activatePendingSubscriptions" in source)
_check("interfaceDbApp has deleteMandate cascade", "deleteMandate" in source and "cascade" in source.lower())
except Exception as e:
errors.append(f"Phase 1 Provisioning: {e}")
print(f" [FAIL] Phase 1 Provisioning: {e}")
# ── Phase 1: Registration Routes ──────────────────────────────────────────────
print("\n--- Phase 1: Registration ---")
try:
with open(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"modules", "routes", "routeSecurityLocal.py"), "r") as f:
source = f.read()
_check("routeSecurityLocal has registrationType", "registrationType" in source)
_check("routeSecurityLocal has companyName", "companyName" in source)
_check("routeSecurityLocal has onboarding endpoint", "onboarding" in source)
except Exception as e:
errors.append(f"Phase 1 Registration: {e}")
print(f" [FAIL] Phase 1 Registration: {e}")
# ── Phase 1: Migration ────────────────────────────────────────────────────────
print("\n--- Phase 1: Migration ---")
try:
with open(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"modules", "migration", "migrateRootUsers.py"), "r") as f:
source = f.read()
_check("Migration script exists", True)
_check("Migration has _isMigrationCompleted", "_isMigrationCompleted" in source)
_check("Migration has migrateRootUsers", "migrateRootUsers" in source)
except Exception as e:
errors.append(f"Phase 1 Migration: {e}")
print(f" [FAIL] Phase 1 Migration: {e}")
# ── Fix 1: OnboardingWizard Integration ────────────────────────────────────────
print("\n--- Fix 1: OnboardingWizard Integration ---")
try:
loginPath = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"..", "frontend_nyla", "src", "pages", "Login.tsx")
with open(loginPath, "r", encoding="utf-8") as f:
source = f.read()
_check("Login.tsx imports OnboardingWizard", "OnboardingWizard" in source)
_check("Login.tsx has showOnboardingWizard state", "showOnboardingWizard" in source)
_check("Login.tsx checks isNewUser", "isNewUser" in source)
except Exception as e:
errors.append(f"Fix 1: {e}")
print(f" [FAIL] Fix 1: {e}")
# ── Fix 2: CommCoach UDB Integration ──────────────────────────────────────────
print("\n--- Fix 2: CommCoach UDB Integration ---")
try:
dossierPath = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"..", "frontend_nyla", "src", "pages", "views", "commcoach", "CommcoachDossierView.tsx")
with open(dossierPath, "r", encoding="utf-8") as f:
source = f.read()
_check("CommCoach imports UnifiedDataBar", "UnifiedDataBar" in source)
_check("CommCoach imports FilesTab", "FilesTab" in source)
_check("CommCoach no longer imports getDocumentsApi", "getDocumentsApi" not in source)
_check("CommCoach has UDB sidebar", "udbSidebar" in source or "UnifiedDataBar" in source)
except Exception as e:
errors.append(f"Fix 2: {e}")
print(f" [FAIL] Fix 2: {e}")
# ── Fix 3: Neutralization Backend Endpoints ───────────────────────────────────
print("\n--- Fix 3: Neutralization Backend Endpoints ---")
try:
routePath = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"modules", "features", "neutralization", "routeFeatureNeutralizer.py")
with open(routePath, "r") as f:
source = f.read()
_check("Neutralization has deleteAttribute endpoint", "deleteAttribute" in source or "delete_attribute" in source)
_check("Neutralization has retrigger endpoint", "retrigger" in source)
_check("Neutralization has single attribute delete", "single" in source or "attributeId" in source)
except Exception as e:
errors.append(f"Fix 3: {e}")
print(f" [FAIL] Fix 3: {e}")
# ── Fix 4: Central AI Neutralization ──────────────────────────────────────────
print("\n--- Fix 4: Central AI Neutralization ---")
try:
aiPath = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"modules", "serviceCenter", "services", "serviceAi", "mainServiceAi.py")
with open(aiPath, "r") as f:
source = f.read()
_check("AiService has _shouldNeutralize", "_shouldNeutralize" in source)
_check("AiService has _neutralizeRequest", "_neutralizeRequest" in source)
_check("AiService has _rehydrateResponse", "_rehydrateResponse" in source)
_check("callAi uses neutralization", "_shouldNeutralize" in source and "_neutralizeRequest" in source)
except Exception as e:
errors.append(f"Fix 4: {e}")
print(f" [FAIL] Fix 4: {e}")
# ── Fix 5: Voice Settings User Level ──────────────────────────────────────────
print("\n--- Fix 5: Voice Settings User Level ---")
try:
from modules.datamodels.datamodelUam import UserVoicePreferences
uvp = UserVoicePreferences(userId="u1")
_check("UserVoicePreferences model exists", True)
_check("UserVoicePreferences has sttLanguage", hasattr(uvp, "sttLanguage"))
_check("UserVoicePreferences default sttLanguage=de-DE", uvp.sttLanguage == "de-DE")
_check("UserVoicePreferences has ttsVoice", hasattr(uvp, "ttsVoice"))
except Exception as e:
errors.append(f"Fix 5: {e}")
print(f" [FAIL] Fix 5: {e}")
try:
voiceUserPath = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"modules", "routes", "routeVoiceUser.py",
)
with open(voiceUserPath, "r") as f:
source = f.read()
_check("Voice preferences GET endpoint", '"/preferences"' in source and "getVoicePreferences" in source)
_check("Voice preferences PUT endpoint", "updateVoicePreferences" in source)
except Exception as e:
errors.append(f"Fix 5 Routes: {e}")
print(f" [FAIL] Fix 5 Routes: {e}")
# ── Fix 6: RAG mandate-wide scope ─────────────────────────────────────────────
print("\n--- Fix 6: RAG mandate-wide scope ---")
try:
knowledgePath = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"modules", "serviceCenter", "services", "serviceKnowledge", "mainServiceKnowledge.py")
with open(knowledgePath, "r") as f:
source = f.read()
_check("buildAgentContext passes mandateId to semanticSearch", "mandateId=mandateId" in source)
_check("buildAgentContext has isSysAdmin param", "isSysAdmin" in source)
except Exception as e:
errors.append(f"Fix 6: {e}")
print(f" [FAIL] Fix 6: {e}")
# ── Summary ───────────────────────────────────────────────────────────────────
print("\n" + "=" * 60)
print(f"RESULTS: {len(passes)} passed, {len(errors)} failed")
print("=" * 60)
if errors:
print("\nFAILURES:")
for e in errors:
print(f" - {e}")
sys.exit(1)
else:
print("\nALL CHECKS PASSED!")
sys.exit(0)