use crate::{ database::Database, repository::Repository, models::{ conversation::{Conversation, ConversationBuilder}, participant::Participant, message::Message, }, settings::Settings, }; // Helper function to compare participants ignoring database IDs fn participants_equal_ignoring_id(a: &Participant, b: &Participant) -> bool { match (a, b) { (Participant::Me, Participant::Me) => true, (Participant::Remote { display_name: name_a, .. }, Participant::Remote { display_name: name_b, .. }) => name_a == name_b, _ => false } } fn participants_vec_equal_ignoring_id(a: &[Participant], b: &[Participant]) -> bool { if a.len() != b.len() { return false; } a.iter().zip(b.iter()).all(|(a, b)| participants_equal_ignoring_id(a, b)) } #[test] fn test_database_init() { let _ = Database::new_in_memory().unwrap(); } #[test] fn test_add_conversation() { let mut db = Database::new_in_memory().unwrap(); db.with_repository(|repository| { let guid = "test"; let test_conversation = Conversation::builder() .guid(guid) .unread_count(2) .display_name("Test Conversation") .build(); repository.insert_conversation(test_conversation.clone()).unwrap(); // Try to fetch with id now let conversation = repository.get_conversation_by_guid(guid).unwrap().unwrap(); assert_eq!(conversation.guid, "test"); // Modify the conversation and update it let modified_conversation = test_conversation.into_builder() .display_name("Modified Conversation") .build(); repository.insert_conversation(modified_conversation.clone()).unwrap(); // Make sure we still only have one conversation. let all_conversations = repository.all_conversations().unwrap(); assert_eq!(all_conversations.len(), 1); // And make sure the display name was updated let conversation = repository.get_conversation_by_guid(guid).unwrap().unwrap(); assert_eq!(conversation.display_name.unwrap(), "Modified Conversation"); }); } #[test] fn test_conversation_participants() { let mut db = Database::new_in_memory().unwrap(); db.with_repository(|repository| { let participants: Vec = vec!["one".into(), "two".into()]; let guid = uuid::Uuid::new_v4().to_string(); let conversation = ConversationBuilder::new() .guid(&guid) .display_name("Test") .participants(participants.clone()) .build(); repository.insert_conversation(conversation).unwrap(); let read_conversation = repository.get_conversation_by_guid(&guid).unwrap().unwrap(); let read_participants = read_conversation.participants; assert!(participants_vec_equal_ignoring_id(&participants, &read_participants)); // Try making another conversation with the same participants let conversation = ConversationBuilder::new() .display_name("A Different Test") .participants(participants.clone()) .build(); repository.insert_conversation(conversation).unwrap(); let read_conversation = repository.get_conversation_by_guid(&guid).unwrap().unwrap(); let read_participants: Vec = read_conversation.participants; assert!(participants_vec_equal_ignoring_id(&participants, &read_participants)); }); } #[test] fn test_all_conversations_with_participants() { let mut db = Database::new_in_memory().unwrap(); db.with_repository(|repository| { // Create two conversations with different participants let participants1: Vec = vec!["one".into(), "two".into()]; let participants2: Vec = vec!["three".into(), "four".into()]; let guid1 = uuid::Uuid::new_v4().to_string(); let conversation1 = ConversationBuilder::new() .guid(&guid1) .display_name("Test 1") .participants(participants1.clone()) .build(); let guid2 = uuid::Uuid::new_v4().to_string(); let conversation2 = ConversationBuilder::new() .guid(&guid2) .display_name("Test 2") .participants(participants2.clone()) .build(); // Insert both conversations repository.insert_conversation(conversation1).unwrap(); repository.insert_conversation(conversation2).unwrap(); // Get all conversations and verify the results let all_conversations = repository.all_conversations().unwrap(); assert_eq!(all_conversations.len(), 2); // Find and verify each conversation's participants let conv1 = all_conversations.iter().find(|c| c.guid == guid1).unwrap(); let conv2 = all_conversations.iter().find(|c| c.guid == guid2).unwrap(); assert!(participants_vec_equal_ignoring_id(&conv1.participants, &participants1)); assert!(participants_vec_equal_ignoring_id(&conv2.participants, &participants2)); }); } #[test] fn test_messages() { let mut db = Database::new_in_memory().unwrap(); db.with_repository(|repository| { // First create a conversation with participants let participants = vec!["Alice".into(), "Bob".into()]; let conversation = ConversationBuilder::new() .display_name("Test Chat") .participants(participants) .build(); let conversation_id = conversation.guid.clone(); repository.insert_conversation(conversation).unwrap(); // Create and insert a message from Me let message1 = Message::builder() .text("Hello everyone!".to_string()) .build(); // Create and insert a message from a remote participant let message2 = Message::builder() .text("Hi there!".to_string()) .sender("Alice".into()) .build(); // Insert both messages repository.insert_message(&conversation_id, message1.clone()).unwrap(); repository.insert_message(&conversation_id, message2.clone()).unwrap(); // Retrieve messages let messages = repository.get_messages_for_conversation(&conversation_id).unwrap(); assert_eq!(messages.len(), 2); // Verify first message (from Me) let retrieved_message1 = messages.iter().find(|m| m.id == message1.id).unwrap(); assert_eq!(retrieved_message1.text, "Hello everyone!"); assert!(matches!(retrieved_message1.sender, Participant::Me)); // Verify second message (from Alice) let retrieved_message2 = messages.iter().find(|m| m.id == message2.id).unwrap(); assert_eq!(retrieved_message2.text, "Hi there!"); if let Participant::Remote { display_name, .. } = &retrieved_message2.sender { assert_eq!(display_name, "Alice"); } else { panic!("Expected Remote participant. Got: {:?}", retrieved_message2.sender); } }); } #[test] fn test_message_ordering() { let mut db = Database::new_in_memory().unwrap(); db.with_repository(|repository| { // Create a conversation let conversation = ConversationBuilder::new() .display_name("Test Chat") .build(); let conversation_id = conversation.guid.clone(); repository.insert_conversation(conversation).unwrap(); // Create messages with specific timestamps let now = chrono::Utc::now().naive_utc(); let message1 = Message::builder() .text("First message".to_string()) .date(now) .build(); let message2 = Message::builder() .text("Second message".to_string()) .date(now + chrono::Duration::minutes(1)) .build(); let message3 = Message::builder() .text("Third message".to_string()) .date(now + chrono::Duration::minutes(2)) .build(); // Insert messages repository.insert_message(&conversation_id, message1).unwrap(); repository.insert_message(&conversation_id, message2).unwrap(); repository.insert_message(&conversation_id, message3).unwrap(); // Retrieve messages and verify order let messages = repository.get_messages_for_conversation(&conversation_id).unwrap(); assert_eq!(messages.len(), 3); // Messages should be ordered by date for i in 1..messages.len() { assert!(messages[i].date > messages[i-1].date); } }); } #[test] fn test_settings() { let mut db = Database::new_in_memory().unwrap(); db.with_settings(|settings| { settings.put("test", &"test".to_string()).unwrap(); assert_eq!(settings.get::("test").unwrap().unwrap(), "test"); settings.del("test").unwrap(); assert!(settings.get::("test").unwrap().is_none()); let keys = settings.list_keys().unwrap(); assert_eq!(keys.len(), 0); }); }