conftest.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. import uuid
  2. import asyncio
  3. import time
  4. from typing import AsyncGenerator
  5. import pytest
  6. from r2r import R2RAsyncClient, R2RClient, R2RException
  7. class RetryableR2RAsyncClient(R2RAsyncClient):
  8. """R2RAsyncClient with automatic retry logic for timeouts"""
  9. async def _make_request(self, method, endpoint, version="v3", **kwargs):
  10. retries = 0
  11. max_retries = 3
  12. delay = 1.0
  13. while True:
  14. try:
  15. return await super()._make_request(method, endpoint, version, **kwargs)
  16. except R2RException as e:
  17. if "Request failed" in str(e) and retries < max_retries:
  18. retries += 1
  19. wait_time = delay * (2 ** (retries - 1))
  20. print(f"Request timed out. Retrying ({retries}/{max_retries}) after {wait_time:.2f}s...")
  21. await asyncio.sleep(wait_time)
  22. elif "429" in str(e) and retries < max_retries:
  23. retries += 1
  24. wait_time = delay * (3 ** (retries - 1))
  25. print(f"Rate limited. Retrying ({retries}/{max_retries}) after {wait_time:.2f}s...")
  26. await asyncio.sleep(wait_time)
  27. else:
  28. raise
  29. class RetryableR2RClient(R2RClient):
  30. """R2RClient with automatic retry logic for timeouts"""
  31. def _make_request(self, method, endpoint, version="v3", **kwargs):
  32. retries = 0
  33. max_retries = 3
  34. delay = 1.0
  35. while True:
  36. try:
  37. return super()._make_request(method, endpoint, version, **kwargs)
  38. except R2RException as e:
  39. if ("Request failed" in str(e) or "timed out" in str(e)) and retries < max_retries:
  40. retries += 1
  41. wait_time = delay * (2 ** (retries - 1))
  42. print(f"Request timed out. Retrying ({retries}/{max_retries}) after {wait_time:.2f}s...")
  43. time.sleep(wait_time)
  44. elif "429" in str(e) and retries < max_retries:
  45. retries += 1
  46. wait_time = delay * (3 ** (retries - 1))
  47. print(f"Rate limited. Retrying ({retries}/{max_retries}) after {wait_time:.2f}s...")
  48. time.sleep(wait_time)
  49. else:
  50. raise
  51. class TestConfig:
  52. def __init__(self):
  53. self.base_url = "http://localhost:7272"
  54. self.index_wait_time = 1.0
  55. self.chunk_creation_wait_time = 1.0
  56. self.superuser_email = "admin@example.com"
  57. self.superuser_password = "change_me_immediately"
  58. self.test_timeout = 30 # seconds
  59. # Change this to session scope to match the client fixture
  60. @pytest.fixture(scope="session")
  61. def config() -> TestConfig:
  62. return TestConfig()
  63. @pytest.fixture(scope="session")
  64. async def client(config) -> AsyncGenerator[R2RClient, None]:
  65. """Create a shared client instance for the test session."""
  66. yield RetryableR2RClient(config.base_url)
  67. @pytest.fixture
  68. def mutable_client(config) -> R2RClient:
  69. """Create a shared client instance for the test session."""
  70. return RetryableR2RClient(config.base_url)
  71. @pytest.fixture
  72. async def aclient(config) -> AsyncGenerator[R2RAsyncClient, None]:
  73. """Create a retryable client instance for the test session."""
  74. yield RetryableR2RAsyncClient(config.base_url)
  75. @pytest.fixture
  76. async def superuser_client(
  77. mutable_client: R2RClient,
  78. config: TestConfig) -> AsyncGenerator[R2RClient, None]:
  79. """Creates a superuser client for tests requiring elevated privileges."""
  80. await mutable_client.users.login(config.superuser_email, config.superuser_password)
  81. yield mutable_client
  82. await mutable_client.users.logout()
  83. @pytest.fixture(scope="session")
  84. def test_document(client: R2RClient):
  85. """Create and yield a test document, then clean up."""
  86. random_suffix = str(uuid.uuid4())
  87. doc_id = client.documents.create(
  88. raw_text=f"{random_suffix} Test doc for collections",
  89. run_with_orchestration=False,
  90. ).results.document_id
  91. yield doc_id
  92. # Cleanup: Try deleting the document if it still exists
  93. try:
  94. client.documents.delete(id=doc_id)
  95. except R2RException:
  96. pass
  97. @pytest.fixture(scope="session")
  98. def test_collection(client: R2RClient, test_document):
  99. """Create a test collection with sample documents and clean up after
  100. tests."""
  101. collection_name = f"Test Collection {uuid.uuid4()}"
  102. collection_id = client.collections.create(name=collection_name).results.id
  103. docs = [
  104. {
  105. "text":
  106. f"Aristotle was a Greek philosopher who studied under Plato {str(uuid.uuid4())}.",
  107. "metadata": {
  108. "rating": 5,
  109. "tags": ["philosophy", "greek"],
  110. "category": "ancient",
  111. },
  112. },
  113. {
  114. "text":
  115. f"Socrates is considered a founder of Western philosophy {str(uuid.uuid4())}.",
  116. "metadata": {
  117. "rating": 3,
  118. "tags": ["philosophy", "classical"],
  119. "category": "ancient",
  120. },
  121. },
  122. {
  123. "text":
  124. f"Rene Descartes was a French philosopher. unique_philosopher {str(uuid.uuid4())}",
  125. "metadata": {
  126. "rating": 8,
  127. "tags": ["rationalism", "french"],
  128. "category": "modern",
  129. },
  130. },
  131. {
  132. "text":
  133. f"Immanuel Kant, a German philosopher, influenced Enlightenment thought {str(uuid.uuid4())}.",
  134. "metadata": {
  135. "rating": 7,
  136. "tags": ["enlightenment", "german"],
  137. "category": "modern",
  138. },
  139. },
  140. ]
  141. doc_ids = []
  142. for doc in docs:
  143. doc_id = client.documents.create(
  144. raw_text=doc["text"], metadata=doc["metadata"]).results.document_id
  145. doc_ids.append(doc_id)
  146. client.collections.add_document(collection_id, doc_id)
  147. client.collections.add_document(collection_id, test_document)
  148. yield {"collection_id": collection_id, "document_ids": doc_ids}
  149. # Cleanup after tests
  150. try:
  151. # Remove and delete all documents
  152. for doc_id in doc_ids:
  153. try:
  154. client.documents.delete(id=doc_id)
  155. except R2RException:
  156. pass
  157. # Delete the collection
  158. try:
  159. client.collections.delete(collection_id)
  160. except R2RException:
  161. pass
  162. except Exception as e:
  163. print(f"Error during test_collection cleanup: {e}")