Co-authored-by: Chang Su <chang.s.su@oracle.com> Co-authored-by: key4ng <rukeyang@gmail.com>
442 lines
15 KiB
Python
442 lines
15 KiB
Python
"""Basic CRUD tests for Response API.
|
|
|
|
Tests for Response and Conversation CRUD operations against cloud backends.
|
|
|
|
Source: Migrated from e2e_response_api/features/test_basic_crud.py
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import time
|
|
|
|
import openai
|
|
import pytest
|
|
from openai import OpenAI
|
|
from openai.types import responses
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def wait_for_background_task(
|
|
client: OpenAI, response_id: str, timeout: int = 30, poll_interval: float = 0.5
|
|
) -> responses.Response:
|
|
"""Wait for background task to complete.
|
|
|
|
Args:
|
|
client: OpenAI client
|
|
response_id: Response ID to poll
|
|
timeout: Max seconds to wait
|
|
poll_interval: Seconds between polls
|
|
|
|
Returns:
|
|
Final response data
|
|
|
|
Raises:
|
|
TimeoutError: If task doesn't complete in time
|
|
AssertionError: If task fails
|
|
"""
|
|
start_time = time.time()
|
|
|
|
while time.time() - start_time < timeout:
|
|
resp = client.responses.retrieve(response_id=response_id)
|
|
assert resp.error is None
|
|
assert resp.id == response_id
|
|
|
|
if resp.status == "completed":
|
|
return resp
|
|
elif resp.status == "failed":
|
|
raise AssertionError(f"Background task failed: {resp.error}")
|
|
elif resp.status == "cancelled":
|
|
raise AssertionError("Background task was cancelled")
|
|
|
|
time.sleep(poll_interval)
|
|
|
|
raise TimeoutError(
|
|
f"Background task {response_id} did not complete within {timeout}s"
|
|
)
|
|
|
|
|
|
# =============================================================================
|
|
# Response CRUD Tests (Memory Storage)
|
|
# =============================================================================
|
|
|
|
|
|
@pytest.mark.parametrize("setup_backend", ["openai"], indirect=True)
|
|
class TestResponseCRUD:
|
|
"""Tests for Response API CRUD operations."""
|
|
|
|
def test_create_and_get_response(self, setup_backend):
|
|
"""Test creating response and retrieving it."""
|
|
_, model, client, gateway = setup_backend
|
|
|
|
# Create response
|
|
create_resp = client.responses.create(model=model, input="Hello, world!")
|
|
assert create_resp.id is not None
|
|
assert create_resp.error is None
|
|
assert create_resp.status == "completed"
|
|
assert len(create_resp.output_text) > 0
|
|
response_id = create_resp.id
|
|
|
|
# Get response
|
|
get_resp = client.responses.retrieve(response_id=response_id)
|
|
assert get_resp.error is None
|
|
assert get_resp.id == response_id
|
|
assert get_resp.status == "completed"
|
|
|
|
input_resp = client.responses.input_items.list(response_id=get_resp.id)
|
|
assert input_resp.data is not None
|
|
assert len(input_resp.data) > 0
|
|
|
|
@pytest.mark.skip(reason="TODO: Add delete response feature")
|
|
def test_delete_response(self, setup_backend):
|
|
"""Test deleting response."""
|
|
_, model, client, gateway = setup_backend
|
|
|
|
# Create response
|
|
create_resp = client.responses.create(model=model, input="Test deletion")
|
|
assert create_resp.id is not None
|
|
assert create_resp.error is None
|
|
assert create_resp.status == "completed"
|
|
assert len(create_resp.output_text) > 0
|
|
|
|
response_id = create_resp.id
|
|
|
|
# Delete response
|
|
client.responses.delete(response_id=response_id)
|
|
|
|
# Verify it's deleted (should return 404)
|
|
with pytest.raises(openai.NotFoundError):
|
|
client.responses.retrieve(response_id=response_id)
|
|
|
|
@pytest.mark.skip(reason="TODO: Add background response feature")
|
|
def test_background_response(self, setup_backend):
|
|
"""Test background response execution."""
|
|
_, model, client, gateway = setup_backend
|
|
|
|
# Create background response
|
|
create_resp = client.responses.create(
|
|
model=model,
|
|
input="Write a short story",
|
|
background=True,
|
|
max_output_tokens=100,
|
|
)
|
|
assert create_resp.id is not None
|
|
assert create_resp.error is None
|
|
assert create_resp.status in ["in_progress", "queued"]
|
|
|
|
response_id = create_resp.id
|
|
|
|
# Wait for completion
|
|
final_data = wait_for_background_task(client, response_id, timeout=60)
|
|
assert final_data.status == "completed"
|
|
|
|
|
|
# =============================================================================
|
|
# Response CRUD Tests (Oracle Storage)
|
|
# =============================================================================
|
|
|
|
|
|
@pytest.mark.storage("oracle")
|
|
@pytest.mark.parametrize("setup_backend", ["openai"], indirect=True)
|
|
class TestResponseCRUDOracleStorage:
|
|
"""Tests for Response API CRUD operations with Oracle history backend."""
|
|
|
|
def test_create_and_get_response(self, setup_backend):
|
|
"""Test creating response and retrieving it."""
|
|
_, model, client, gateway = setup_backend
|
|
|
|
# Create response
|
|
create_resp = client.responses.create(model=model, input="Hello, world!")
|
|
assert create_resp.id is not None
|
|
assert create_resp.error is None
|
|
assert create_resp.status == "completed"
|
|
assert len(create_resp.output_text) > 0
|
|
response_id = create_resp.id
|
|
|
|
# Get response
|
|
get_resp = client.responses.retrieve(response_id=response_id)
|
|
assert get_resp.error is None
|
|
assert get_resp.id == response_id
|
|
assert get_resp.status == "completed"
|
|
|
|
input_resp = client.responses.input_items.list(response_id=get_resp.id)
|
|
assert input_resp.data is not None
|
|
assert len(input_resp.data) > 0
|
|
|
|
@pytest.mark.skip(reason="TODO: Add delete response feature")
|
|
def test_delete_response(self, setup_backend):
|
|
"""Test deleting response."""
|
|
_, model, client, gateway = setup_backend
|
|
|
|
# Create response
|
|
create_resp = client.responses.create(model=model, input="Test deletion")
|
|
assert create_resp.id is not None
|
|
assert create_resp.error is None
|
|
assert create_resp.status == "completed"
|
|
assert len(create_resp.output_text) > 0
|
|
|
|
response_id = create_resp.id
|
|
|
|
# Delete response
|
|
client.responses.delete(response_id=response_id)
|
|
|
|
# Verify it's deleted (should return 404)
|
|
with pytest.raises(openai.NotFoundError):
|
|
client.responses.retrieve(response_id=response_id)
|
|
|
|
@pytest.mark.skip(reason="TODO: Add background response feature")
|
|
def test_background_response(self, setup_backend):
|
|
"""Test background response execution."""
|
|
_, model, client, gateway = setup_backend
|
|
|
|
# Create background response
|
|
create_resp = client.responses.create(
|
|
model=model,
|
|
input="Write a short story",
|
|
background=True,
|
|
max_output_tokens=100,
|
|
)
|
|
assert create_resp.id is not None
|
|
assert create_resp.error is None
|
|
assert create_resp.status in ["in_progress", "queued"]
|
|
|
|
response_id = create_resp.id
|
|
|
|
# Wait for completion
|
|
final_data = wait_for_background_task(client, response_id, timeout=60)
|
|
assert final_data.status == "completed"
|
|
|
|
|
|
# =============================================================================
|
|
# Conversation CRUD Tests (Memory Storage)
|
|
# =============================================================================
|
|
|
|
|
|
@pytest.mark.parametrize("setup_backend", ["openai"], indirect=True)
|
|
class TestConversationCRUD:
|
|
"""Tests for Conversation API CRUD operations."""
|
|
|
|
def test_create_and_get_conversation(self, setup_backend):
|
|
"""Test creating and retrieving conversation."""
|
|
_, model, client, gateway = setup_backend
|
|
|
|
# Create conversation
|
|
create_resp = client.conversations.create(metadata={"user": "test_user"})
|
|
assert create_resp.id is not None
|
|
assert create_resp.created_at is not None
|
|
|
|
create_data = create_resp.metadata
|
|
assert create_data["user"] == "test_user"
|
|
conversation_id = create_resp.id
|
|
|
|
# Get conversation
|
|
get_resp = client.conversations.retrieve(conversation_id=conversation_id)
|
|
assert get_resp.id is not None
|
|
assert get_resp.created_at is not None
|
|
|
|
get_data = get_resp.metadata
|
|
assert get_resp.id == conversation_id
|
|
assert get_data["user"] == "test_user"
|
|
|
|
def test_update_conversation(self, setup_backend):
|
|
"""Test updating conversation metadata."""
|
|
_, model, client, gateway = setup_backend
|
|
|
|
# Create conversation
|
|
create_resp = client.conversations.create(metadata={"key1": "value1"})
|
|
assert create_resp.id is not None
|
|
assert create_resp.created_at is not None
|
|
|
|
create_data = create_resp.metadata
|
|
assert create_data["key1"] == "value1"
|
|
assert "key2" not in create_data
|
|
conversation_id = create_resp.id
|
|
|
|
# Update conversation
|
|
update_resp = client.conversations.update(
|
|
conversation_id=conversation_id,
|
|
metadata={"key1": "value1", "key2": "value2"},
|
|
)
|
|
assert update_resp.id == conversation_id
|
|
update_data = update_resp.metadata
|
|
assert update_data["key1"] == "value1"
|
|
assert update_data["key2"] == "value2"
|
|
|
|
# Verify update
|
|
get_resp = client.conversations.retrieve(conversation_id=conversation_id)
|
|
get_data = get_resp.metadata
|
|
assert get_data["key1"] == "value1"
|
|
assert get_data["key2"] == "value2"
|
|
|
|
def test_delete_conversation(self, setup_backend):
|
|
"""Test deleting conversation."""
|
|
_, model, client, gateway = setup_backend
|
|
|
|
# Create conversation
|
|
create_resp = client.conversations.create()
|
|
assert create_resp.id is not None
|
|
assert create_resp.created_at is not None
|
|
conversation_id = create_resp.id
|
|
|
|
# Delete conversation
|
|
delete_resp = client.conversations.delete(conversation_id=conversation_id)
|
|
assert delete_resp.id is not None
|
|
assert delete_resp.deleted
|
|
|
|
# Verify deletion
|
|
with pytest.raises(openai.NotFoundError):
|
|
client.conversations.retrieve(conversation_id=conversation_id)
|
|
|
|
def test_list_conversation_items(self, setup_backend):
|
|
"""Test listing conversation items."""
|
|
_, model, client, gateway = setup_backend
|
|
|
|
# Create conversation
|
|
conv_resp = client.conversations.create()
|
|
assert conv_resp.id is not None
|
|
conversation_id = conv_resp.id
|
|
|
|
# Create response with conversation
|
|
resp1 = client.responses.create(
|
|
model=model,
|
|
input="First message",
|
|
conversation=conversation_id,
|
|
max_output_tokens=50,
|
|
)
|
|
assert resp1.error is None
|
|
|
|
resp2 = client.responses.create(
|
|
model=model,
|
|
input="Second message",
|
|
conversation=conversation_id,
|
|
max_output_tokens=50,
|
|
)
|
|
assert resp2.error is None
|
|
|
|
# List items
|
|
list_resp = client.conversations.items.list(conversation_id=conversation_id)
|
|
assert list_resp is not None
|
|
assert list_resp.data is not None
|
|
|
|
list_data = list_resp.data
|
|
# Should have at least 4 items (2 inputs + 2 outputs)
|
|
assert len(list_data) >= 4
|
|
|
|
|
|
# =============================================================================
|
|
# Conversation CRUD Tests (Oracle Storage)
|
|
# =============================================================================
|
|
|
|
|
|
@pytest.mark.storage("oracle")
|
|
@pytest.mark.parametrize("setup_backend", ["openai"], indirect=True)
|
|
class TestConversationCRUDOracleStorage:
|
|
"""Tests for Conversation API CRUD operations with Oracle history backend."""
|
|
|
|
def test_create_and_get_conversation(self, setup_backend):
|
|
"""Test creating and retrieving conversation."""
|
|
_, model, client, gateway = setup_backend
|
|
|
|
# Create conversation
|
|
create_resp = client.conversations.create(metadata={"user": "test_user"})
|
|
assert create_resp.id is not None
|
|
assert create_resp.created_at is not None
|
|
|
|
create_data = create_resp.metadata
|
|
assert create_data["user"] == "test_user"
|
|
conversation_id = create_resp.id
|
|
|
|
# Get conversation
|
|
get_resp = client.conversations.retrieve(conversation_id=conversation_id)
|
|
assert get_resp.id is not None
|
|
assert get_resp.created_at is not None
|
|
|
|
get_data = get_resp.metadata
|
|
assert get_resp.id == conversation_id
|
|
assert get_data["user"] == "test_user"
|
|
|
|
def test_update_conversation(self, setup_backend):
|
|
"""Test updating conversation metadata."""
|
|
_, model, client, gateway = setup_backend
|
|
|
|
# Create conversation
|
|
create_resp = client.conversations.create(metadata={"key1": "value1"})
|
|
assert create_resp.id is not None
|
|
assert create_resp.created_at is not None
|
|
|
|
create_data = create_resp.metadata
|
|
assert create_data["key1"] == "value1"
|
|
assert "key2" not in create_data
|
|
conversation_id = create_resp.id
|
|
|
|
# Update conversation
|
|
update_resp = client.conversations.update(
|
|
conversation_id=conversation_id,
|
|
metadata={"key1": "value1", "key2": "value2"},
|
|
)
|
|
assert update_resp.id == conversation_id
|
|
update_data = update_resp.metadata
|
|
assert update_data["key1"] == "value1"
|
|
assert update_data["key2"] == "value2"
|
|
|
|
# Verify update
|
|
get_resp = client.conversations.retrieve(conversation_id=conversation_id)
|
|
get_data = get_resp.metadata
|
|
assert get_data["key1"] == "value1"
|
|
assert get_data["key2"] == "value2"
|
|
|
|
def test_delete_conversation(self, setup_backend):
|
|
"""Test deleting conversation."""
|
|
_, model, client, gateway = setup_backend
|
|
|
|
# Create conversation
|
|
create_resp = client.conversations.create()
|
|
assert create_resp.id is not None
|
|
assert create_resp.created_at is not None
|
|
conversation_id = create_resp.id
|
|
|
|
# Delete conversation
|
|
delete_resp = client.conversations.delete(conversation_id=conversation_id)
|
|
assert delete_resp.id is not None
|
|
assert delete_resp.deleted
|
|
|
|
# Verify deletion
|
|
with pytest.raises(openai.NotFoundError):
|
|
client.conversations.retrieve(conversation_id=conversation_id)
|
|
|
|
def test_list_conversation_items(self, setup_backend):
|
|
"""Test listing conversation items."""
|
|
_, model, client, gateway = setup_backend
|
|
|
|
# Create conversation
|
|
conv_resp = client.conversations.create()
|
|
assert conv_resp.id is not None
|
|
conversation_id = conv_resp.id
|
|
|
|
# Create response with conversation
|
|
resp1 = client.responses.create(
|
|
model=model,
|
|
input="First message",
|
|
conversation=conversation_id,
|
|
max_output_tokens=50,
|
|
)
|
|
assert resp1.error is None
|
|
|
|
resp2 = client.responses.create(
|
|
model=model,
|
|
input="Second message",
|
|
conversation=conversation_id,
|
|
max_output_tokens=50,
|
|
)
|
|
assert resp2.error is None
|
|
|
|
# List items
|
|
list_resp = client.conversations.items.list(conversation_id=conversation_id)
|
|
assert list_resp is not None
|
|
assert list_resp.data is not None
|
|
|
|
list_data = list_resp.data
|
|
# Should have at least 4 items (2 inputs + 2 outputs)
|
|
assert len(list_data) >= 4
|