test_conversations_cli.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. """
  2. Tests for the conversations commands in the CLI.
  3. - create
  4. - list
  5. - retrieve
  6. - delete
  7. - list-users
  8. """
  9. import json
  10. import uuid
  11. import pytest
  12. from click.testing import CliRunner
  13. from cli.commands.conversations import (
  14. create,
  15. delete,
  16. list,
  17. list_users,
  18. retrieve,
  19. )
  20. from r2r import R2RAsyncClient
  21. from tests.cli.async_invoke import async_invoke
  22. def extract_json_block(output: str) -> dict:
  23. """Extract and parse the first valid JSON object found in the output."""
  24. start = output.find("{")
  25. if start == -1:
  26. raise ValueError("No JSON object start found in output")
  27. brace_count = 0
  28. for i, char in enumerate(output[start:], start=start):
  29. if char == "{":
  30. brace_count += 1
  31. elif char == "}":
  32. brace_count -= 1
  33. if brace_count == 0:
  34. json_str = output[start : i + 1].strip()
  35. return json.loads(json_str)
  36. raise ValueError("No complete JSON object found in output")
  37. @pytest.mark.asyncio
  38. async def test_conversation_lifecycle():
  39. """Test the complete lifecycle of a conversation: create, retrieve, delete."""
  40. client = R2RAsyncClient(base_url="http://localhost:7272")
  41. runner = CliRunner(mix_stderr=False)
  42. # Create conversation
  43. create_result = await async_invoke(
  44. runner,
  45. create,
  46. obj=client,
  47. )
  48. assert create_result.exit_code == 0, create_result.stdout_bytes.decode()
  49. output = create_result.stdout_bytes.decode()
  50. create_response = extract_json_block(output)
  51. conversation_id = create_response["results"]["id"]
  52. try:
  53. # Retrieve conversation
  54. retrieve_result = await async_invoke(
  55. runner, retrieve, conversation_id, obj=client
  56. )
  57. assert retrieve_result.exit_code == 0
  58. retrieve_output = retrieve_result.stdout_bytes.decode()
  59. # FIXME: This assertion fails, we need to sync Conversation and ConversationResponse
  60. # assert conversation_id in retrieve_output
  61. assert "results" in retrieve_output
  62. # List users in conversation
  63. list_users_result = await async_invoke(
  64. runner, list_users, conversation_id, obj=client
  65. )
  66. assert list_users_result.exit_code == 0
  67. finally:
  68. # Delete conversation
  69. delete_result = await async_invoke(
  70. runner, delete, conversation_id, obj=client
  71. )
  72. assert delete_result.exit_code == 0
  73. @pytest.mark.asyncio
  74. async def test_list_conversations():
  75. """Test listing conversations with various parameters."""
  76. client = R2RAsyncClient(base_url="http://localhost:7272")
  77. runner = CliRunner(mix_stderr=False)
  78. # Create test conversation first
  79. create_result = await async_invoke(runner, create, obj=client)
  80. response = extract_json_block(create_result.stdout_bytes.decode())
  81. conversation_id = response["results"]["id"]
  82. try:
  83. # Test basic list
  84. list_result = await async_invoke(runner, list, obj=client)
  85. assert list_result.exit_code == 0
  86. # Test paginated results
  87. list_paginated = await async_invoke(
  88. runner, list, "--offset", "0", "--limit", "2", obj=client
  89. )
  90. assert list_paginated.exit_code == 0
  91. finally:
  92. # Cleanup
  93. await async_invoke(runner, delete, conversation_id, obj=client)
  94. @pytest.mark.asyncio
  95. async def test_nonexistent_conversation():
  96. """Test operations on a nonexistent conversation."""
  97. client = R2RAsyncClient(base_url="http://localhost:7272")
  98. runner = CliRunner(mix_stderr=False)
  99. nonexistent_id = str(uuid.uuid4())
  100. # Test retrieve
  101. retrieve_result = await async_invoke(
  102. runner, retrieve, nonexistent_id, obj=client
  103. )
  104. assert "not found" in retrieve_result.stderr_bytes.decode().lower()
  105. # Test list_users
  106. list_users_result = await async_invoke(
  107. runner, list_users, nonexistent_id, obj=client
  108. )
  109. assert "not found" in list_users_result.stderr_bytes.decode().lower()
  110. @pytest.mark.asyncio
  111. async def test_list_conversations_pagination():
  112. """Test pagination functionality of list conversations."""
  113. client = R2RAsyncClient(base_url="http://localhost:7272")
  114. runner = CliRunner(mix_stderr=False)
  115. # Create multiple conversations
  116. conversation_ids = []
  117. for _ in range(3):
  118. create_result = await async_invoke(runner, create, obj=client)
  119. response = extract_json_block(create_result.stdout_bytes.decode())
  120. conversation_ids.append(response["results"]["id"])
  121. try:
  122. # Test with different pagination parameters
  123. list_first_page = await async_invoke(
  124. runner, list, "--offset", "0", "--limit", "2", obj=client
  125. )
  126. assert list_first_page.exit_code == 0
  127. first_page_output = list_first_page.stdout_bytes.decode()
  128. list_second_page = await async_invoke(
  129. runner, list, "--offset", "2", "--limit", "2", obj=client
  130. )
  131. assert list_second_page.exit_code == 0
  132. second_page_output = list_second_page.stdout_bytes.decode()
  133. # Verify different results on different pages
  134. assert first_page_output != second_page_output
  135. finally:
  136. # Cleanup
  137. for conversation_id in conversation_ids:
  138. await async_invoke(runner, delete, conversation_id, obj=client)