Skip to content

Commit 2de9596

Browse files
committed
Test suite fixes for testing with resilientdb performance
1 parent 97488e2 commit 2de9596

4 files changed

Lines changed: 1062 additions & 2 deletions

File tree

backend/incubator-resilientdb-resilient-python-cache/example.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,12 @@ async def main():
5959
db_name=os.environ["MONGO_DB"],
6060
collection_name=os.environ["MONGO_COLLECTION"]
6161
)
62-
62+
63+
# ResilientDB configuration - reads from .env, defaults to local cluster
64+
# Format: resilientdb://host:port (uses WebSocket for block sync)
65+
resilient_base_url = os.environ.get("RESILIENTDB_BASE_URL", "resilientdb://localhost:18000")
6366
resilient_db_config = ResilientDBConfig(
64-
base_url="resilientdb://localhost:8000",
67+
base_url=resilient_base_url,
6568
http_secure=False,
6669
ws_secure=False
6770
)

backend/pytest.ini

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ markers =
2727
stroke: Canvas stroke tests
2828
socketio: Socket.IO real-time tests
2929
wallet: Wallet/signing tests
30+
requires_resilientdb: Tests that require local ResilientDB running
31+
requires_mongodb: Tests that require MongoDB connection
32+
requires_redis: Tests that require Redis connection
33+
performance: Performance and benchmark tests
3034
filterwarnings =
3135
ignore::DeprecationWarning
3236
ignore::PendingDeprecationWarning
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
"""
2+
Integration test fixtures for ResCanvas backend.
3+
4+
This conftest provides fixtures specific to integration and E2E tests,
5+
including real database connections and ResilientDB client helpers.
6+
"""
7+
8+
import pytest
9+
import os
10+
import uuid
11+
import time
12+
from typing import Dict, Any, Optional
13+
14+
15+
# ============================================================================
16+
# Environment Detection Fixtures
17+
# ============================================================================
18+
19+
def is_resilientdb_available() -> bool:
20+
"""Check if local ResilientDB is accessible."""
21+
try:
22+
import requests
23+
uri = os.getenv('LOCAL_RESILIENTDB_GRAPHQL_URI', 'http://127.0.0.1:18000/graphql')
24+
response = requests.post(
25+
uri,
26+
json={'query': '{ __typename }'},
27+
timeout=2
28+
)
29+
return response.status_code == 200
30+
except Exception:
31+
return False
32+
33+
34+
def is_mongodb_available() -> bool:
35+
"""Check if MongoDB is accessible for E2E tests."""
36+
try:
37+
from pymongo import MongoClient
38+
mongo_uri = os.getenv('MONGO_ATLAS_URI')
39+
if not mongo_uri:
40+
return False
41+
client = MongoClient(mongo_uri, serverSelectionTimeoutMS=2000)
42+
client.admin.command('ping')
43+
return True
44+
except Exception:
45+
return False
46+
47+
48+
def is_redis_available() -> bool:
49+
"""Check if Redis is accessible."""
50+
try:
51+
import redis
52+
r = redis.Redis(
53+
host=os.getenv('REDIS_HOST', 'localhost'),
54+
port=int(os.getenv('REDIS_PORT', 6379))
55+
)
56+
return r.ping()
57+
except Exception:
58+
return False
59+
60+
61+
@pytest.fixture(scope='session')
62+
def resilientdb_available():
63+
"""Session-scoped fixture indicating ResilientDB availability."""
64+
return is_resilientdb_available()
65+
66+
67+
@pytest.fixture(scope='session')
68+
def mongodb_available():
69+
"""Session-scoped fixture indicating MongoDB availability."""
70+
return is_mongodb_available()
71+
72+
73+
@pytest.fixture(scope='session')
74+
def redis_available():
75+
"""Session-scoped fixture indicating Redis availability."""
76+
return is_redis_available()
77+
78+
79+
# ============================================================================
80+
# ResilientDB E2E Fixtures
81+
# ============================================================================
82+
83+
@pytest.fixture(scope='function')
84+
def e2e_room_id():
85+
"""Generate a unique room ID for E2E tests."""
86+
return f'e2e-room-{uuid.uuid4().hex[:12]}'
87+
88+
89+
@pytest.fixture(scope='function')
90+
def e2e_user():
91+
"""Generate test user info for E2E tests."""
92+
return {
93+
'id': f'e2e-user-{uuid.uuid4().hex[:8]}',
94+
'username': f'testuser_{int(time.time())}',
95+
'public_key': 'test-public-key',
96+
}
97+
98+
99+
@pytest.fixture(scope='function')
100+
def resilientdb_client():
101+
"""
102+
Create a ResilientDB client for E2E tests.
103+
104+
This fixture creates a real client that connects to local ResilientDB.
105+
Skip the test if ResilientDB is not available.
106+
"""
107+
if not is_resilientdb_available():
108+
pytest.skip("Local ResilientDB not available")
109+
110+
from tests.integration.test_resilientdb_e2e import ResilientDBClient
111+
return ResilientDBClient()
112+
113+
114+
@pytest.fixture(scope='function')
115+
def mongodb_client():
116+
"""
117+
Create a MongoDB client for E2E tests.
118+
119+
This fixture creates a real client for verifying MongoDB sync.
120+
Skip the test if MongoDB is not available.
121+
"""
122+
if not is_mongodb_available():
123+
pytest.skip("MongoDB not available")
124+
125+
from tests.integration.test_resilientdb_e2e import MongoDBClient
126+
return MongoDBClient()
127+
128+
129+
@pytest.fixture(scope='function')
130+
def stroke_factory():
131+
"""Provide the StrokeFactory for creating test stroke data."""
132+
from tests.integration.test_resilientdb_e2e import StrokeFactory
133+
return StrokeFactory
134+
135+
136+
# ============================================================================
137+
# Latency Measurement Fixtures
138+
# ============================================================================
139+
140+
class LatencyTracker:
141+
"""Helper class for tracking latency measurements."""
142+
143+
def __init__(self):
144+
self.latencies = []
145+
146+
def record(self, latency_ms: float):
147+
"""Record a latency measurement."""
148+
self.latencies.append(latency_ms)
149+
150+
def get_stats(self) -> Dict[str, float]:
151+
"""Get latency statistics."""
152+
if not self.latencies:
153+
return {}
154+
155+
import statistics
156+
sorted_lat = sorted(self.latencies)
157+
158+
return {
159+
'count': len(self.latencies),
160+
'min': min(self.latencies),
161+
'max': max(self.latencies),
162+
'mean': statistics.mean(self.latencies),
163+
'median': statistics.median(self.latencies),
164+
'stdev': statistics.stdev(self.latencies) if len(self.latencies) > 1 else 0,
165+
'p50': statistics.median(sorted_lat),
166+
'p90': sorted_lat[int(len(sorted_lat) * 0.90) - 1] if len(sorted_lat) >= 10 else sorted_lat[-1],
167+
'p99': sorted_lat[int(len(sorted_lat) * 0.99) - 1] if len(sorted_lat) >= 100 else sorted_lat[-1],
168+
}
169+
170+
def reset(self):
171+
"""Reset latency measurements."""
172+
self.latencies = []
173+
174+
175+
@pytest.fixture(scope='function')
176+
def latency_tracker():
177+
"""Provide a latency tracker for performance tests."""
178+
return LatencyTracker()
179+
180+
181+
# ============================================================================
182+
# Test Data Cleanup Fixtures
183+
# ============================================================================
184+
185+
@pytest.fixture(scope='function')
186+
def cleanup_mongodb():
187+
"""
188+
Fixture that provides cleanup function for MongoDB test data.
189+
190+
Usage:
191+
def test_something(cleanup_mongodb):
192+
room_id = 'test-room-123'
193+
# ... test code ...
194+
cleanup_mongodb(room_id)
195+
"""
196+
rooms_to_cleanup = []
197+
198+
def _register_cleanup(room_id: str):
199+
rooms_to_cleanup.append(room_id)
200+
201+
yield _register_cleanup
202+
203+
# Cleanup after test
204+
if rooms_to_cleanup and is_mongodb_available():
205+
try:
206+
from tests.integration.test_resilientdb_e2e import MongoDBClient
207+
client = MongoDBClient()
208+
for room_id in rooms_to_cleanup:
209+
client.cleanup_room(room_id)
210+
except Exception:
211+
pass # Best effort cleanup
212+
213+
214+
# ============================================================================
215+
# Environment Configuration Fixtures
216+
# ============================================================================
217+
218+
@pytest.fixture(scope='function')
219+
def local_resilientdb_config():
220+
"""
221+
Configure environment for local ResilientDB testing.
222+
223+
This fixture sets up environment variables to point to local ResilientDB.
224+
Original values are restored after the test.
225+
"""
226+
original_uri = os.environ.get('RESILIENTDB_GRAPHQL_URI')
227+
local_uri = os.environ.get(
228+
'LOCAL_RESILIENTDB_GRAPHQL_URI',
229+
'http://127.0.0.1:18000/graphql'
230+
)
231+
232+
os.environ['RESILIENTDB_GRAPHQL_URI'] = local_uri
233+
234+
yield {
235+
'graphql_uri': local_uri,
236+
'original_uri': original_uri,
237+
}
238+
239+
# Restore original value
240+
if original_uri:
241+
os.environ['RESILIENTDB_GRAPHQL_URI'] = original_uri
242+
elif 'RESILIENTDB_GRAPHQL_URI' in os.environ:
243+
del os.environ['RESILIENTDB_GRAPHQL_URI']
244+
245+
246+
# ============================================================================
247+
# Performance Testing Markers
248+
# ============================================================================
249+
250+
def pytest_configure(config):
251+
"""Register custom markers for E2E tests."""
252+
config.addinivalue_line(
253+
"markers", "requires_resilientdb: mark test as requiring ResilientDB"
254+
)
255+
config.addinivalue_line(
256+
"markers", "requires_mongodb: mark test as requiring MongoDB"
257+
)
258+
config.addinivalue_line(
259+
"markers", "requires_redis: mark test as requiring Redis"
260+
)
261+
config.addinivalue_line(
262+
"markers", "performance: mark test as a performance/benchmark test"
263+
)

0 commit comments

Comments
 (0)