610 lines
18 KiB
Rust
Raw Normal View History

2025-04-17 15:47:17 +08:00
use event_integration_test::user_event::use_localhost_af_cloud;
use event_integration_test::EventIntegrationTest;
use flowy_ai_pub::cloud::MessageCursor;
use flowy_ai_pub::persistence::{
2025-04-18 13:20:05 +08:00
select_answer_where_match_reply_message_id, select_chat_messages, select_message,
select_message_content, total_message_count, upsert_chat_messages, ChatMessageTable,
2025-04-17 15:47:17 +08:00
};
use uuid::Uuid;
#[tokio::test]
async fn chat_message_table_insert_select_test() {
use_localhost_af_cloud().await;
let test = EventIntegrationTest::new().await;
test.sign_up_as_anon().await;
let uid = test.user_manager.get_anon_user().await.unwrap().id;
let db_conn = test.user_manager.db_connection(uid).unwrap();
let chat_id = Uuid::new_v4().to_string();
let message_id_1 = 1000;
let message_id_2 = 2000;
// Create test messages
let messages = vec![
ChatMessageTable {
message_id: message_id_1,
chat_id: chat_id.clone(),
content: "Hello, this is a test message".to_string(),
created_at: 1625097600, // 2021-07-01
author_type: 1, // User
author_id: "user_1".to_string(),
reply_message_id: None,
metadata: None,
2025-04-18 13:20:05 +08:00
is_sync: false,
2025-04-17 15:47:17 +08:00
},
ChatMessageTable {
message_id: message_id_2,
chat_id: chat_id.clone(),
content: "This is a reply to the test message".to_string(),
created_at: 1625097700, // 2021-07-01, 100 seconds later
author_type: 0, // AI
author_id: "ai".to_string(),
reply_message_id: Some(message_id_1),
metadata: Some(r#"{"source": "test"}"#.to_string()),
2025-04-18 13:20:05 +08:00
is_sync: false,
2025-04-17 15:47:17 +08:00
},
];
// Test insert_chat_messages
2025-04-18 13:20:05 +08:00
let result = upsert_chat_messages(db_conn, &messages);
2025-04-17 15:47:17 +08:00
assert!(
result.is_ok(),
"Failed to insert chat messages: {:?}",
result
);
// Test select_chat_messages
let db_conn = test.user_manager.db_connection(uid).unwrap();
let messages_result =
select_chat_messages(db_conn, &chat_id, 10, MessageCursor::Offset(0)).unwrap();
assert_eq!(messages_result.messages.len(), 2);
assert_eq!(messages_result.total_count, 2);
assert!(!messages_result.has_more);
// Verify the content of the returned messages
let first_message = messages_result
.messages
.iter()
.find(|m| m.message_id == message_id_1)
.unwrap();
assert_eq!(first_message.content, "Hello, this is a test message");
assert_eq!(first_message.author_type, 1);
let second_message = messages_result
.messages
.iter()
.find(|m| m.message_id == message_id_2)
.unwrap();
assert_eq!(
second_message.content,
"This is a reply to the test message"
);
assert_eq!(second_message.reply_message_id, Some(message_id_1));
}
#[tokio::test]
async fn chat_message_table_cursor_test() {
use_localhost_af_cloud().await;
let test = EventIntegrationTest::new().await;
test.sign_up_as_anon().await;
let uid = test.user_manager.get_anon_user().await.unwrap().id;
let db_conn = test.user_manager.db_connection(uid).unwrap();
let chat_id = Uuid::new_v4().to_string();
// Create multiple test messages with sequential IDs
let mut messages = Vec::new();
for i in 1..6 {
messages.push(ChatMessageTable {
message_id: i * 1000,
chat_id: chat_id.clone(),
content: format!("Message {}", i),
created_at: 1625097600 + (i * 100), // Increasing timestamps
author_type: 1, // User
author_id: "user_1".to_string(),
reply_message_id: None,
metadata: None,
2025-04-18 13:20:05 +08:00
is_sync: false,
2025-04-17 15:47:17 +08:00
});
}
// Insert messages
2025-04-18 13:20:05 +08:00
upsert_chat_messages(db_conn, &messages).unwrap();
2025-04-17 15:47:17 +08:00
// Test MessageCursor::Offset
let db_conn = test.user_manager.db_connection(uid).unwrap();
let result_offset = select_chat_messages(
db_conn,
&chat_id,
2, // Limit to 2 messages
MessageCursor::Offset(0),
)
.unwrap();
assert_eq!(result_offset.messages.len(), 2);
assert!(result_offset.has_more);
// Test MessageCursor::AfterMessageId
let db_conn = test.user_manager.db_connection(uid).unwrap();
let result_after = select_chat_messages(
db_conn,
&chat_id,
3, // Limit to 3 messages
MessageCursor::AfterMessageId(2000),
)
.unwrap();
assert_eq!(result_after.messages.len(), 3); // Should get message IDs 3000, 4000, 5000
assert!(result_after.messages.iter().all(|m| m.message_id > 2000));
// Test MessageCursor::BeforeMessageId
let db_conn = test.user_manager.db_connection(uid).unwrap();
let result_before = select_chat_messages(
db_conn,
&chat_id,
2, // Limit to 2 messages
MessageCursor::BeforeMessageId(4000),
)
.unwrap();
assert_eq!(result_before.messages.len(), 2); // Should get message IDs 1000, 2000, 3000
assert!(result_before.messages.iter().all(|m| m.message_id < 4000));
}
#[tokio::test]
async fn chat_message_total_count_test() {
use_localhost_af_cloud().await;
let test = EventIntegrationTest::new().await;
test.sign_up_as_anon().await;
let uid = test.user_manager.get_anon_user().await.unwrap().id;
let db_conn = test.user_manager.db_connection(uid).unwrap();
let chat_id = Uuid::new_v4().to_string();
// Create test messages
let messages = vec![
ChatMessageTable {
message_id: 1001,
chat_id: chat_id.clone(),
content: "Message 1".to_string(),
created_at: 1625097600,
author_type: 1,
author_id: "user_1".to_string(),
reply_message_id: None,
metadata: None,
2025-04-18 13:20:05 +08:00
is_sync: false,
2025-04-17 15:47:17 +08:00
},
ChatMessageTable {
message_id: 1002,
chat_id: chat_id.clone(),
content: "Message 2".to_string(),
created_at: 1625097700,
author_type: 0,
author_id: "ai".to_string(),
reply_message_id: None,
metadata: None,
2025-04-18 13:20:05 +08:00
is_sync: false,
2025-04-17 15:47:17 +08:00
},
];
// Insert messages
2025-04-18 13:20:05 +08:00
upsert_chat_messages(db_conn, &messages).unwrap();
2025-04-17 15:47:17 +08:00
// Test total_message_count
let db_conn = test.user_manager.db_connection(uid).unwrap();
let count = total_message_count(db_conn, &chat_id).unwrap();
assert_eq!(count, 2);
// Add one more message
let db_conn = test.user_manager.db_connection(uid).unwrap();
let additional_message = ChatMessageTable {
message_id: 1003,
chat_id: chat_id.clone(),
content: "Message 3".to_string(),
created_at: 1625097800,
author_type: 1,
author_id: "user_1".to_string(),
reply_message_id: None,
metadata: None,
2025-04-18 13:20:05 +08:00
is_sync: false,
2025-04-17 15:47:17 +08:00
};
2025-04-18 13:20:05 +08:00
upsert_chat_messages(db_conn, &[additional_message]).unwrap();
2025-04-17 15:47:17 +08:00
// Verify count increased
let db_conn = test.user_manager.db_connection(uid).unwrap();
let updated_count = total_message_count(db_conn, &chat_id).unwrap();
assert_eq!(updated_count, 3);
// Test count for non-existent chat
let db_conn = test.user_manager.db_connection(uid).unwrap();
let empty_count = total_message_count(db_conn, "non_existent_chat").unwrap();
assert_eq!(empty_count, 0);
}
#[tokio::test]
async fn chat_message_select_message_test() {
use_localhost_af_cloud().await;
let test = EventIntegrationTest::new().await;
test.sign_up_as_anon().await;
let uid = test.user_manager.get_anon_user().await.unwrap().id;
let db_conn = test.user_manager.db_connection(uid).unwrap();
let chat_id = Uuid::new_v4().to_string();
let message_id = 2001;
// Create test message
let message = ChatMessageTable {
message_id,
chat_id: chat_id.clone(),
content: "This is a test message for select_message".to_string(),
created_at: 1625097600,
author_type: 1,
author_id: "user_1".to_string(),
reply_message_id: None,
metadata: Some(r#"{"test_key": "test_value"}"#.to_string()),
2025-04-18 13:20:05 +08:00
is_sync: false,
2025-04-17 15:47:17 +08:00
};
// Insert message
2025-04-18 13:20:05 +08:00
upsert_chat_messages(db_conn, &[message]).unwrap();
2025-04-17 15:47:17 +08:00
// Test select_message
let db_conn = test.user_manager.db_connection(uid).unwrap();
let result = select_message(db_conn, message_id).unwrap();
assert!(result.is_some());
let retrieved_message = result.unwrap();
assert_eq!(retrieved_message.message_id, message_id);
assert_eq!(retrieved_message.chat_id, chat_id);
assert_eq!(
retrieved_message.content,
"This is a test message for select_message"
);
assert_eq!(retrieved_message.author_id, "user_1");
assert_eq!(
retrieved_message.metadata,
Some(r#"{"test_key": "test_value"}"#.to_string())
);
// Test select_message with non-existent ID
let db_conn = test.user_manager.db_connection(uid).unwrap();
let non_existent = select_message(db_conn, 9999).unwrap();
assert!(non_existent.is_none());
}
#[tokio::test]
async fn chat_message_select_content_test() {
use_localhost_af_cloud().await;
let test = EventIntegrationTest::new().await;
test.sign_up_as_anon().await;
let uid = test.user_manager.get_anon_user().await.unwrap().id;
let db_conn = test.user_manager.db_connection(uid).unwrap();
let chat_id = Uuid::new_v4().to_string();
let message_id = 3001;
let message_content = "This is the content to retrieve";
// Create test message
let message = ChatMessageTable {
message_id,
chat_id: chat_id.clone(),
content: message_content.to_string(),
created_at: 1625097600,
author_type: 1,
author_id: "user_1".to_string(),
reply_message_id: None,
metadata: None,
2025-04-18 13:20:05 +08:00
is_sync: false,
2025-04-17 15:47:17 +08:00
};
// Insert message
2025-04-18 13:20:05 +08:00
upsert_chat_messages(db_conn, &[message]).unwrap();
2025-04-17 15:47:17 +08:00
// Test select_message_content
let db_conn = test.user_manager.db_connection(uid).unwrap();
let content = select_message_content(db_conn, message_id).unwrap();
assert!(content.is_some());
assert_eq!(content.unwrap(), message_content);
// Test with non-existent message
let db_conn = test.user_manager.db_connection(uid).unwrap();
let no_content = select_message_content(db_conn, 9999).unwrap();
assert!(no_content.is_none());
}
#[tokio::test]
async fn chat_message_reply_test() {
use_localhost_af_cloud().await;
let test = EventIntegrationTest::new().await;
test.sign_up_as_anon().await;
let uid = test.user_manager.get_anon_user().await.unwrap().id;
let db_conn = test.user_manager.db_connection(uid).unwrap();
let chat_id = Uuid::new_v4().to_string();
let question_id = 4001;
let answer_id = 4002;
// Create question and answer messages
let question = ChatMessageTable {
message_id: question_id,
chat_id: chat_id.clone(),
content: "What is the question?".to_string(),
created_at: 1625097600,
author_type: 1, // User
author_id: "user_1".to_string(),
reply_message_id: None,
metadata: None,
2025-04-18 13:20:05 +08:00
is_sync: false,
2025-04-17 15:47:17 +08:00
};
let answer = ChatMessageTable {
message_id: answer_id,
chat_id: chat_id.clone(),
content: "This is the answer".to_string(),
created_at: 1625097700,
author_type: 0, // AI
author_id: "ai".to_string(),
reply_message_id: Some(question_id), // Link to question
metadata: None,
2025-04-18 13:20:05 +08:00
is_sync: false,
2025-04-17 15:47:17 +08:00
};
// Insert messages
2025-04-18 13:20:05 +08:00
upsert_chat_messages(db_conn, &[question, answer]).unwrap();
2025-04-17 15:47:17 +08:00
// Test select_message_where_match_reply_message_id
let db_conn = test.user_manager.db_connection(uid).unwrap();
2025-04-18 13:20:05 +08:00
let result = select_answer_where_match_reply_message_id(db_conn, &chat_id, question_id).unwrap();
2025-04-17 15:47:17 +08:00
assert!(result.is_some());
let reply = result.unwrap();
assert_eq!(reply.message_id, answer_id);
assert_eq!(reply.content, "This is the answer");
assert_eq!(reply.reply_message_id, Some(question_id));
// Test with non-existent reply relation
let db_conn = test.user_manager.db_connection(uid).unwrap();
2025-04-18 13:20:05 +08:00
let no_reply = select_answer_where_match_reply_message_id(
2025-04-17 15:47:17 +08:00
db_conn, &chat_id, 9999, // Non-existent question ID
)
.unwrap();
assert!(no_reply.is_none());
// Test with wrong chat_id
let db_conn = test.user_manager.db_connection(uid).unwrap();
let wrong_chat =
2025-04-18 13:20:05 +08:00
select_answer_where_match_reply_message_id(db_conn, "wrong_chat_id", question_id).unwrap();
2025-04-17 15:47:17 +08:00
assert!(wrong_chat.is_none());
}
#[tokio::test]
async fn chat_message_upsert_test() {
use_localhost_af_cloud().await;
let test = EventIntegrationTest::new().await;
test.sign_up_as_anon().await;
let uid = test.user_manager.get_anon_user().await.unwrap().id;
let db_conn = test.user_manager.db_connection(uid).unwrap();
let chat_id = Uuid::new_v4().to_string();
let message_id = 5001;
// Create initial message
let message = ChatMessageTable {
message_id,
chat_id: chat_id.clone(),
content: "Original content".to_string(),
created_at: 1625097600,
author_type: 1,
author_id: "user_1".to_string(),
reply_message_id: None,
metadata: None,
2025-04-18 13:20:05 +08:00
is_sync: false,
2025-04-17 15:47:17 +08:00
};
// Insert message
2025-04-18 13:20:05 +08:00
upsert_chat_messages(db_conn, &[message]).unwrap();
2025-04-17 15:47:17 +08:00
// Check original content
let db_conn = test.user_manager.db_connection(uid).unwrap();
let original = select_message(db_conn, message_id).unwrap().unwrap();
assert_eq!(original.content, "Original content");
// Create updated message with same ID but different content
let db_conn = test.user_manager.db_connection(uid).unwrap();
let updated_message = ChatMessageTable {
message_id, // Same ID
chat_id: chat_id.clone(),
content: "Updated content".to_string(), // New content
created_at: 1625097700, // Updated timestamp
author_type: 1,
author_id: "user_1".to_string(),
reply_message_id: Some(1000), // Added reply ID
metadata: Some(r#"{"updated": true}"#.to_string()),
2025-04-18 13:20:05 +08:00
is_sync: false,
2025-04-17 15:47:17 +08:00
};
// Upsert message
2025-04-18 13:20:05 +08:00
upsert_chat_messages(db_conn, &[updated_message]).unwrap();
2025-04-17 15:47:17 +08:00
// Verify update
let db_conn = test.user_manager.db_connection(uid).unwrap();
let result = select_message(db_conn, message_id).unwrap().unwrap();
assert_eq!(result.content, "Updated content");
assert_eq!(result.created_at, 1625097700);
assert_eq!(result.reply_message_id, Some(1000));
assert_eq!(result.metadata, Some(r#"{"updated": true}"#.to_string()));
// Count should still be 1 (update, not insert)
let db_conn = test.user_manager.db_connection(uid).unwrap();
let count = total_message_count(db_conn, &chat_id).unwrap();
assert_eq!(count, 1);
}
#[tokio::test]
async fn chat_message_select_with_large_dataset() {
use_localhost_af_cloud().await;
let test = EventIntegrationTest::new().await;
test.sign_up_as_anon().await;
let uid = test.user_manager.get_anon_user().await.unwrap().id;
let db_conn = test.user_manager.db_connection(uid).unwrap();
let chat_id = Uuid::new_v4().to_string();
// Create 100 test messages with sequential IDs
let mut messages = Vec::new();
for i in 1..=100 {
messages.push(ChatMessageTable {
message_id: i * 100,
chat_id: chat_id.clone(),
content: format!("Message {}", i),
created_at: 1625097600 + (i * 10), // Increasing timestamps
author_type: if i % 2 == 0 { 0 } else { 1 }, // Alternate between AI and User
author_id: if i % 2 == 0 {
"ai".to_string()
} else {
"user_1".to_string()
},
reply_message_id: if i > 1 && i % 2 == 0 {
Some((i - 1) * 100)
} else {
None
}, // Even messages reply to previous message
metadata: if i % 5 == 0 {
Some(format!(r#"{{"index": {}}}"#, i))
} else {
None
},
2025-04-18 13:20:05 +08:00
is_sync: false,
2025-04-17 15:47:17 +08:00
});
}
// Insert all 100 messages
2025-04-18 13:20:05 +08:00
upsert_chat_messages(db_conn, &messages).unwrap();
2025-04-17 15:47:17 +08:00
// Verify total count
let db_conn = test.user_manager.db_connection(uid).unwrap();
let count = total_message_count(db_conn, &chat_id).unwrap();
assert_eq!(count, 100, "Should have 100 messages in the database");
// Test 1: MessageCursor::Offset with small page size
let db_conn = test.user_manager.db_connection(uid).unwrap();
let page_size = 10;
let result_offset =
select_chat_messages(db_conn, &chat_id, page_size, MessageCursor::Offset(0)).unwrap();
assert_eq!(
result_offset.messages.len(),
page_size as usize,
"Should return exactly {page_size} messages"
);
assert!(
result_offset.has_more,
"Should have more messages available"
);
assert_eq!(result_offset.total_count, 100, "Total count should be 100");
// Test 2: Pagination with offset
let db_conn = test.user_manager.db_connection(uid).unwrap();
let result_page2 = select_chat_messages(
db_conn,
&chat_id,
page_size,
MessageCursor::Offset(page_size),
)
.unwrap();
assert_eq!(result_page2.messages.len(), page_size as usize);
assert!(
result_page2.messages[0].message_id != result_offset.messages[0].message_id,
"Second page should have different messages than first page"
);
// Test 3: MessageCursor::AfterMessageId (forward pagination)
let db_conn = test.user_manager.db_connection(uid).unwrap();
let middle_message_id = 5000; // Message ID from the middle
let result_after = select_chat_messages(
db_conn,
&chat_id,
page_size,
MessageCursor::AfterMessageId(middle_message_id),
)
.unwrap();
assert_eq!(result_after.messages.len(), page_size as usize);
assert!(
result_after
.messages
.iter()
.all(|m| m.message_id > middle_message_id),
"All messages should have ID greater than the cursor"
);
// Test 4: MessageCursor::BeforeMessageId (backward pagination)
let db_conn = test.user_manager.db_connection(uid).unwrap();
let result_before = select_chat_messages(
db_conn,
&chat_id,
page_size,
MessageCursor::BeforeMessageId(middle_message_id),
)
.unwrap();
assert_eq!(result_before.messages.len(), page_size as usize);
assert!(
result_before
.messages
.iter()
.all(|m| m.message_id < middle_message_id),
"All messages should have ID less than the cursor"
);
// Test 5: Large page size (retrieve all)
let db_conn = test.user_manager.db_connection(uid).unwrap();
let result_all = select_chat_messages(
db_conn,
&chat_id,
200, // More than we have
MessageCursor::Offset(0),
)
.unwrap();
assert_eq!(
result_all.messages.len(),
100,
"Should return all 100 messages"
);
assert!(!result_all.has_more, "Should not have more messages");
// Test 6: Empty result when using out of range cursor
let db_conn = test.user_manager.db_connection(uid).unwrap();
let result_out_of_range = select_chat_messages(
db_conn,
&chat_id,
page_size,
MessageCursor::AfterMessageId(10000), // After the last message
)
.unwrap();
assert_eq!(
result_out_of_range.messages.len(),
0,
"Should return no messages"
);
assert!(
!result_out_of_range.has_more,
"Should not have more messages"
);
}