Skip to content

Commit 4e1df89

Browse files
committed
refactor: update conversation tracking to fetch details from OpenAI API
- Modified `ConversationServiceImpl` to include OpenAI proxy for fetching conversation details. - Removed title handling from conversation repository and API responses. - Updated API routes and tests to reflect changes in conversation data structure. - Added end-to-end tests for conversation tracking and access control.
1 parent 7e8f42b commit 4e1df89

File tree

8 files changed

+526
-155
lines changed

8 files changed

+526
-155
lines changed

crates/api/src/main.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@ async fn main() -> anyhow::Result<()> {
7474
let proxy_service = Arc::new(proxy_service);
7575

7676
// Initialize conversation service
77-
let conversation_service = Arc::new(ConversationServiceImpl::new(conversation_repo));
77+
let conversation_service = Arc::new(ConversationServiceImpl::new(
78+
conversation_repo,
79+
proxy_service.clone(),
80+
));
7881

7982
// Create application state
8083
let app_state = AppState {

crates/api/src/routes/api.rs

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,17 @@ use std::io::Read;
1515

1616
use crate::middleware::auth::AuthenticatedUser;
1717

18-
#[derive(Serialize, Deserialize)]
19-
pub struct ConversationResponse {
20-
pub id: String,
21-
pub title: Option<String>,
22-
pub created_at: String,
23-
pub updated_at: String,
24-
}
25-
2618
#[derive(Serialize, Deserialize)]
2719
pub struct ErrorResponse {
2820
pub error: String,
2921
}
3022

3123
/// Create the OpenAI API proxy router
3224
pub fn create_api_router() -> Router<crate::state::AppState> {
25+
// IMPORTANT: Specific routes MUST be in the same Router and registered BEFORE catch-all routes
26+
// Axum matches routes in order within a router
3327
Router::new()
34-
// Specific handlers for conversation endpoints (track in DB)
28+
// Specific conversation endpoints (handle these before catch-all)
3529
.route("/v1/conversations", post(create_conversation))
3630
.route("/v1/conversations", get(list_conversations))
3731
// Catch-all proxy for all other OpenAI endpoints
@@ -171,7 +165,7 @@ async fn create_conversation(
171165
tracing::debug!("Tracking conversation {} in database", conversation_id);
172166
if let Err(e) = state
173167
.conversation_service
174-
.track_conversation(conversation_id, user.user_id, None)
168+
.track_conversation(conversation_id, user.user_id)
175169
.await
176170
{
177171
tracing::error!(
@@ -234,11 +228,11 @@ async fn create_conversation(
234228
})
235229
}
236230

237-
/// List all conversations for the authenticated user (from local DB)
231+
/// List all conversations for the authenticated user (fetches details from OpenAI client)
238232
async fn list_conversations(
239233
State(state): State<crate::state::AppState>,
240234
Extension(user): Extension<AuthenticatedUser>,
241-
) -> Result<Json<Vec<ConversationResponse>>, Response> {
235+
) -> Result<Json<Vec<serde_json::Value>>, Response> {
242236
tracing::info!("list_conversations called for user_id={}", user.user_id);
243237

244238
let conversations = state
@@ -266,27 +260,7 @@ async fn list_conversations(
266260
user.user_id
267261
);
268262

269-
for conv in &conversations {
270-
tracing::debug!(
271-
"Conversation: id={}, title={:?}, created={}, updated={}",
272-
conv.id,
273-
conv.title,
274-
conv.created_at,
275-
conv.updated_at
276-
);
277-
}
278-
279-
Ok(Json(
280-
conversations
281-
.into_iter()
282-
.map(|c| ConversationResponse {
283-
id: c.id,
284-
title: c.title,
285-
created_at: c.created_at.to_rfc3339(),
286-
updated_at: c.updated_at.to_rfc3339(),
287-
})
288-
.collect(),
289-
))
263+
Ok(Json(conversations))
290264
}
291265

292266
/// Generic proxy handler that forwards all requests to OpenAI
@@ -475,7 +449,7 @@ async fn track_conversation_from_request(
475449

476450
state
477451
.conversation_service
478-
.track_conversation(&conversation_id, user_id, None)
452+
.track_conversation(&conversation_id, user_id)
479453
.await?;
480454

481455
tracing::info!(

crates/api/tests/README.md

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# End-to-End API Tests
2+
3+
## Overview
4+
5+
This directory contains comprehensive end-to-end tests for the conversation tracking functionality. The tests verify that the API correctly:
6+
7+
1. Tracks conversation IDs per user in the local database
8+
2. Fetches conversation details from OpenAI API on demand
9+
3. Maintains proper access control for conversations
10+
4. Handles edge cases like empty lists and response-triggered tracking
11+
12+
## Architecture Being Tested
13+
14+
The conversation management system works as follows:
15+
16+
```
17+
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
18+
│ Client │────────>│ Chat API │────────>│ Database │
19+
│ │ │ │ │ │
20+
│ │ │ - Track │ │ - User IDs │
21+
│ │ │ Conv IDs │ │ - Conv IDs │
22+
└─────────────┘ └─────────────┘ └─────────────┘
23+
24+
│ Fetch Details
25+
26+
┌─────────────┐
27+
│ OpenAI API │
28+
│ │
29+
│ - Titles │
30+
│ - Messages │
31+
│ - Metadata │
32+
└─────────────┘
33+
```
34+
35+
## Test Cases
36+
37+
### 1. `test_conversation_workflow`
38+
Tests the complete conversation lifecycle:
39+
- Creates a conversation via OpenAI
40+
- Adds multiple responses to the conversation
41+
- Lists conversations and verifies details are fetched from OpenAI
42+
- Confirms that conversation tracking works end-to-end
43+
44+
### 2. `test_conversation_access_control`
45+
Verifies access control mechanisms:
46+
- Creates a conversation for a user
47+
- Attempts to access it as the owner (should succeed)
48+
- Validates proper authorization checks
49+
50+
### 3. `test_empty_conversation_list`
51+
Tests edge case handling:
52+
- Lists conversations when there may be zero or many
53+
- Ensures the endpoint handles all cases gracefully
54+
55+
### 4. `test_conversation_tracking_on_response_creation`
56+
Tests automatic conversation tracking:
57+
- Creates a conversation
58+
- Adds a response (which triggers automatic tracking)
59+
- Verifies the conversation appears in the user's list
60+
- Confirms details are fetched from OpenAI
61+
62+
## Running the Tests
63+
64+
### Prerequisites
65+
66+
1. **Database**: Ensure PostgreSQL is running with the correct schema
67+
2. **Environment Variables**: Set up your `.env` file with:
68+
```
69+
OPENAI_API_KEY=your_api_key_here
70+
DATABASE_HOST=localhost
71+
DATABASE_PORT=5432
72+
DATABASE_NAME=chat_api
73+
DATABASE_USER=postgres
74+
DATABASE_PASSWORD=your_password
75+
```
76+
77+
3. **Valid Session Token**: The tests use `SESSION_TOKEN` constant - ensure you have a valid session in the database
78+
79+
### Run All Tests
80+
81+
```bash
82+
# Run all E2E tests (they make real OpenAI API calls)
83+
cargo test --test e2e_api_tests -- --ignored --nocapture
84+
85+
# Run a specific test
86+
cargo test --test e2e_api_tests test_conversation_workflow -- --ignored --nocapture
87+
```
88+
89+
### Test Output
90+
91+
The tests provide detailed output showing:
92+
- Step-by-step progress
93+
- API request/response status codes
94+
- Conversation IDs and details
95+
- Success/failure indicators (✓/✗)
96+
97+
Example output:
98+
```
99+
=== Test: Conversation Workflow ===
100+
1. Creating a conversation via OpenAI...
101+
Status: 200
102+
✓ Conversation created successfully
103+
Conversation ID: conv_abc123...
104+
105+
2. Adding first response to the conversation...
106+
Status: 200
107+
✓ First response created successfully
108+
Response ID: resp_xyz789...
109+
110+
...
111+
112+
4. Listing conversations (should fetch details from OpenAI)...
113+
Found 5 total conversations
114+
✓ Found our conversation in the list!
115+
ID: conv_abc123...
116+
Created: 2025-11-12T10:30:00Z
117+
Updated: 2025-11-12T10:35:00Z
118+
✓ Conversation details fetched from OpenAI
119+
120+
=== Test Complete ===
121+
✅ Test passed: Created conversation, added responses, and listed conversations with OpenAI details
122+
```
123+
124+
## Notes
125+
126+
- Tests are marked with `#[ignore]` because they make real API calls to OpenAI
127+
- Each test is independent and can be run separately
128+
- Tests use the actual database and OpenAI API (not mocks)
129+
- The session token must be valid and exist in your database
130+
131+
## Troubleshooting
132+
133+
**Test fails with "Session not found"**
134+
- Check that `SESSION_TOKEN` constant matches a valid session in your database
135+
- Ensure the session hasn't expired
136+
137+
**Test fails with "OpenAI API error"**
138+
- Verify your `OPENAI_API_KEY` is valid
139+
- Check your OpenAI account has available credits
140+
141+
**Test fails with "Database error"**
142+
- Ensure PostgreSQL is running
143+
- Run migrations: `cargo run --bin migrate` or start the API server once
144+
- Check database connection settings in `.env`
145+

0 commit comments

Comments
 (0)