implements settings, conversation dbus encoding
This commit is contained in:
@@ -130,6 +130,53 @@ impl<'a> Repository<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_messages(&mut self, conversation_guid: &str, in_messages: Vec<Message>) -> Result<()> {
|
||||
use crate::schema::messages::dsl::*;
|
||||
use crate::schema::conversation_messages::dsl::*;
|
||||
|
||||
// Local insertable struct for the join table
|
||||
#[derive(Insertable)]
|
||||
#[diesel(table_name = crate::schema::conversation_messages)]
|
||||
struct InsertableConversationMessage {
|
||||
pub conversation_id: String,
|
||||
pub message_id: String,
|
||||
}
|
||||
|
||||
if in_messages.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Build the collections of insertable records
|
||||
let mut db_messages: Vec<MessageRecord> = Vec::with_capacity(in_messages.len());
|
||||
let mut conv_msg_records: Vec<InsertableConversationMessage> = Vec::with_capacity(in_messages.len());
|
||||
|
||||
for message in in_messages {
|
||||
// Handle participant if message has a remote sender
|
||||
let sender = message.sender.clone();
|
||||
let mut db_message: MessageRecord = message.into();
|
||||
db_message.sender_participant_id = self.get_or_create_participant(&sender);
|
||||
|
||||
conv_msg_records.push(InsertableConversationMessage {
|
||||
conversation_id: conversation_guid.to_string(),
|
||||
message_id: db_message.id.clone(),
|
||||
});
|
||||
|
||||
db_messages.push(db_message);
|
||||
}
|
||||
|
||||
// Batch insert or replace messages
|
||||
diesel::replace_into(messages)
|
||||
.values(&db_messages)
|
||||
.execute(self.connection)?;
|
||||
|
||||
// Batch insert the conversation-message links
|
||||
diesel::replace_into(conversation_messages)
|
||||
.values(&conv_msg_records)
|
||||
.execute(self.connection)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_messages_for_conversation(&mut self, conversation_guid: &str) -> Result<Vec<Message>> {
|
||||
use crate::schema::messages::dsl::*;
|
||||
use crate::schema::conversation_messages::dsl::*;
|
||||
|
||||
@@ -26,13 +26,13 @@ fn participants_vec_equal_ignoring_id(a: &[Participant], b: &[Participant]) -> b
|
||||
a.iter().zip(b.iter()).all(|(a, b)| participants_equal_ignoring_id(a, b))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_database_init() {
|
||||
#[tokio::test]
|
||||
async fn test_database_init() {
|
||||
let _ = Database::new_in_memory().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_conversation() {
|
||||
#[tokio::test]
|
||||
async fn test_add_conversation() {
|
||||
let mut db = Database::new_in_memory().unwrap();
|
||||
db.with_repository(|repository| {
|
||||
let guid = "test";
|
||||
@@ -62,11 +62,11 @@ fn test_add_conversation() {
|
||||
// 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");
|
||||
});
|
||||
}).await;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conversation_participants() {
|
||||
#[tokio::test]
|
||||
async fn test_conversation_participants() {
|
||||
let mut db = Database::new_in_memory().unwrap();
|
||||
db.with_repository(|repository| {
|
||||
let participants: Vec<Participant> = vec!["one".into(), "two".into()];
|
||||
@@ -97,11 +97,11 @@ fn test_conversation_participants() {
|
||||
let read_participants: Vec<Participant> = read_conversation.participants;
|
||||
|
||||
assert!(participants_vec_equal_ignoring_id(&participants, &read_participants));
|
||||
});
|
||||
}).await;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_all_conversations_with_participants() {
|
||||
#[tokio::test]
|
||||
async fn test_all_conversations_with_participants() {
|
||||
let mut db = Database::new_in_memory().unwrap();
|
||||
db.with_repository(|repository| {
|
||||
// Create two conversations with different participants
|
||||
@@ -136,11 +136,11 @@ fn test_all_conversations_with_participants() {
|
||||
|
||||
assert!(participants_vec_equal_ignoring_id(&conv1.participants, &participants1));
|
||||
assert!(participants_vec_equal_ignoring_id(&conv2.participants, &participants2));
|
||||
});
|
||||
}).await;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_messages() {
|
||||
#[tokio::test]
|
||||
async fn test_messages() {
|
||||
let mut db = Database::new_in_memory().unwrap();
|
||||
db.with_repository(|repository| {
|
||||
// First create a conversation with participants
|
||||
@@ -185,11 +185,11 @@ fn test_messages() {
|
||||
} else {
|
||||
panic!("Expected Remote participant. Got: {:?}", retrieved_message2.sender);
|
||||
}
|
||||
});
|
||||
}).await;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_message_ordering() {
|
||||
#[tokio::test]
|
||||
async fn test_message_ordering() {
|
||||
let mut db = Database::new_in_memory().unwrap();
|
||||
db.with_repository(|repository| {
|
||||
// Create a conversation
|
||||
@@ -229,11 +229,93 @@ fn test_message_ordering() {
|
||||
for i in 1..messages.len() {
|
||||
assert!(messages[i].date > messages[i-1].date);
|
||||
}
|
||||
});
|
||||
}).await;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_settings() {
|
||||
#[tokio::test]
|
||||
async fn test_insert_messages_batch() {
|
||||
let mut db = Database::new_in_memory().unwrap();
|
||||
db.with_repository(|repository| {
|
||||
// Create a conversation with two remote participants
|
||||
let participants: Vec<Participant> = vec!["Alice".into(), "Bob".into()];
|
||||
let conversation = ConversationBuilder::new()
|
||||
.display_name("Batch Chat")
|
||||
.participants(participants.clone())
|
||||
.build();
|
||||
let conversation_id = conversation.guid.clone();
|
||||
repository.insert_conversation(conversation).unwrap();
|
||||
|
||||
// Prepare a batch of messages with increasing timestamps
|
||||
let now = chrono::Utc::now().naive_utc();
|
||||
let message1 = Message::builder()
|
||||
.text("Hi".to_string())
|
||||
.date(now)
|
||||
.build();
|
||||
|
||||
let message2 = Message::builder()
|
||||
.text("Hello".to_string())
|
||||
.sender("Alice".into())
|
||||
.date(now + chrono::Duration::seconds(1))
|
||||
.build();
|
||||
|
||||
let message3 = Message::builder()
|
||||
.text("How are you?".to_string())
|
||||
.sender("Bob".into())
|
||||
.date(now + chrono::Duration::seconds(2))
|
||||
.build();
|
||||
|
||||
let message4 = Message::builder()
|
||||
.text("Great!".to_string())
|
||||
.date(now + chrono::Duration::seconds(3))
|
||||
.build();
|
||||
|
||||
let original_messages = vec![
|
||||
message1.clone(),
|
||||
message2.clone(),
|
||||
message3.clone(),
|
||||
message4.clone(),
|
||||
];
|
||||
|
||||
// Batch insert the messages
|
||||
repository
|
||||
.insert_messages(&conversation_id, original_messages.clone())
|
||||
.unwrap();
|
||||
|
||||
// Retrieve messages and verify
|
||||
let retrieved_messages = repository.get_messages_for_conversation(&conversation_id).unwrap();
|
||||
assert_eq!(retrieved_messages.len(), original_messages.len());
|
||||
|
||||
// Ensure ordering by date
|
||||
for i in 1..retrieved_messages.len() {
|
||||
assert!(retrieved_messages[i].date > retrieved_messages[i - 1].date);
|
||||
}
|
||||
|
||||
// Verify that all messages are present with correct content and sender
|
||||
for original in &original_messages {
|
||||
let retrieved = retrieved_messages
|
||||
.iter()
|
||||
.find(|m| m.id == original.id)
|
||||
.expect("Message not found");
|
||||
assert_eq!(retrieved.text, original.text);
|
||||
|
||||
match (&original.sender, &retrieved.sender) {
|
||||
(Participant::Me, Participant::Me) => {}
|
||||
(
|
||||
Participant::Remote { display_name: o_name, .. },
|
||||
Participant::Remote { display_name: r_name, .. },
|
||||
) => assert_eq!(o_name, r_name),
|
||||
_ => panic!(
|
||||
"Sender mismatch: original {:?}, retrieved {:?}",
|
||||
original.sender, retrieved.sender
|
||||
),
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_settings() {
|
||||
let mut db = Database::new_in_memory().unwrap();
|
||||
db.with_settings(|settings| {
|
||||
settings.put("test", &"test".to_string()).unwrap();
|
||||
@@ -244,5 +326,27 @@ fn test_settings() {
|
||||
|
||||
let keys = settings.list_keys().unwrap();
|
||||
assert_eq!(keys.len(), 0);
|
||||
});
|
||||
|
||||
// Try encoding a struct
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
|
||||
struct TestStruct {
|
||||
name: String,
|
||||
age: u32,
|
||||
}
|
||||
|
||||
let test_struct = TestStruct {
|
||||
name: "James".to_string(),
|
||||
age: 35,
|
||||
};
|
||||
|
||||
settings.put("test_struct", &test_struct).unwrap();
|
||||
assert_eq!(settings.get::<TestStruct>("test_struct").unwrap().unwrap(), test_struct);
|
||||
|
||||
// Test with an option<string>
|
||||
settings.put("test_struct_option", &Option::<String>::None).unwrap();
|
||||
assert!(settings.get::<Option<String>>("test_struct_option").unwrap().unwrap().is_none());
|
||||
|
||||
settings.put("test_struct_option", &Option::<String>::Some("test".to_string())).unwrap();
|
||||
assert_eq!(settings.get::<Option<String>>("test_struct_option").unwrap().unwrap(), Some("test".to_string()));
|
||||
}).await;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user