#!/usr/bin/env python3 """ Test deterministic cloning by creating multiple sessions and comparing data hashes. """ import asyncio import hashlib import json from typing import List, Dict import httpx DEMO_API_URL = "http://localhost:8018" INTERNAL_API_KEY = "test-internal-key" async def create_demo_session(tier: str = "professional") -> dict: """Create a demo session""" async with httpx.AsyncClient() as client: response = await client.post( f"{DEMO_API_URL}/api/demo/sessions", json={"demo_account_type": tier} ) return response.json() async def get_all_data_from_service( service_url: str, tenant_id: str ) -> dict: """Fetch all data for a tenant from a service""" async with httpx.AsyncClient() as client: response = await client.get( f"{service_url}/internal/demo/export/{tenant_id}", headers={"X-Internal-API-Key": INTERNAL_API_KEY} ) return response.json() def calculate_data_hash(data: dict) -> str: """ Calculate SHA-256 hash of data, excluding audit timestamps. """ # Remove non-deterministic fields clean_data = remove_audit_fields(data) # Sort keys for consistency json_str = json.dumps(clean_data, sort_keys=True) return hashlib.sha256(json_str.encode()).hexdigest() def remove_audit_fields(data: dict) -> dict: """Remove created_at, updated_at fields recursively""" if isinstance(data, dict): return { k: remove_audit_fields(v) for k, v in data.items() if k not in ["created_at", "updated_at", "id"] # IDs are UUIDs } elif isinstance(data, list): return [remove_audit_fields(item) for item in data] else: return data async def test_determinism(tier: str = "professional", iterations: int = 10): """ Test that cloning is deterministic across multiple sessions. """ print(f"Testing determinism for {tier} tier ({iterations} iterations)...") services = [ ("inventory", "http://inventory-service:8002"), ("production", "http://production-service:8003"), ("recipes", "http://recipes-service:8004"), ] hashes_by_service = {svc[0]: [] for svc in services} for i in range(iterations): # Create session session = await create_demo_session(tier) tenant_id = session["virtual_tenant_id"] # Get data from each service for service_name, service_url in services: data = await get_all_data_from_service(service_url, tenant_id) data_hash = calculate_data_hash(data) hashes_by_service[service_name].append(data_hash) # Cleanup async with httpx.AsyncClient() as client: await client.delete(f"{DEMO_API_URL}/api/demo/sessions/{session['session_id']}") if (i + 1) % 10 == 0: print(f" Completed {i + 1}/{iterations} iterations") # Check consistency all_consistent = True for service_name, hashes in hashes_by_service.items(): unique_hashes = set(hashes) if len(unique_hashes) == 1: print(f"āœ… {service_name}: All {iterations} hashes identical") else: print(f"āŒ {service_name}: {len(unique_hashes)} different hashes found!") all_consistent = False if all_consistent: print("\nāœ… DETERMINISM TEST PASSED") return 0 else: print("\nāŒ DETERMINISM TEST FAILED") return 1 if __name__ == "__main__": exit_code = asyncio.run(test_determinism()) exit(exit_code)