initial commit
Signed-off-by: Sienna Meridian Satterwhite <sienna@r3t.io>
This commit is contained in:
98
crates/lib/tests/our_messages_test.rs
Normal file
98
crates/lib/tests/our_messages_test.rs
Normal file
@@ -0,0 +1,98 @@
|
||||
use lib::{ChatDb, Result};
|
||||
use chrono::Datelike;
|
||||
|
||||
/// Test that we can get messages from the Dutch phone number conversation
|
||||
#[test]
|
||||
fn test_get_our_messages_default_range() -> Result<()> {
|
||||
let db = ChatDb::open("chat.db")?;
|
||||
|
||||
// Get messages from January 2024 to now (default)
|
||||
let messages = db.get_our_messages(None, None)?;
|
||||
|
||||
println!("Found {} messages from January 2024 to now", messages.len());
|
||||
|
||||
// Verify we got some messages
|
||||
assert!(messages.len() > 0, "Should find messages in the conversation");
|
||||
|
||||
// Verify messages are in chronological order (ASC)
|
||||
for i in 1..messages.len().min(10) {
|
||||
if let (Some(prev_date), Some(curr_date)) = (messages[i-1].date, messages[i].date) {
|
||||
assert!(
|
||||
prev_date <= curr_date,
|
||||
"Messages should be in ascending date order"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify all messages are from 2024 or later
|
||||
for msg in messages.iter().take(10) {
|
||||
if let Some(date) = msg.date {
|
||||
assert!(date.year() >= 2024, "Messages should be from 2024 or later");
|
||||
println!("Message date: {}, from_me: {}, text: {:?}",
|
||||
date, msg.is_from_me, msg.text.as_ref().map(|s| &s[..s.len().min(50)]));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test that we can get messages with a custom date range
|
||||
#[test]
|
||||
fn test_get_our_messages_custom_range() -> Result<()> {
|
||||
use chrono::{TimeZone, Utc};
|
||||
|
||||
let db = ChatDb::open("chat.db")?;
|
||||
|
||||
// Get messages from March 2024 to June 2024
|
||||
let start = Utc.with_ymd_and_hms(2024, 3, 1, 0, 0, 0).unwrap();
|
||||
let end = Utc.with_ymd_and_hms(2024, 6, 1, 0, 0, 0).unwrap();
|
||||
|
||||
let messages = db.get_our_messages(Some(start), Some(end))?;
|
||||
|
||||
println!("Found {} messages from March to June 2024", messages.len());
|
||||
|
||||
// Verify all messages are within the date range
|
||||
for msg in &messages {
|
||||
if let Some(date) = msg.date {
|
||||
assert!(
|
||||
date >= start && date <= end,
|
||||
"Message date {} should be between {} and {}",
|
||||
date, start, end
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test displaying a summary of the conversation
|
||||
#[test]
|
||||
fn test_conversation_summary() -> Result<()> {
|
||||
let db = ChatDb::open("chat.db")?;
|
||||
|
||||
let messages = db.get_our_messages(None, None)?;
|
||||
|
||||
println!("\n=== Conversation Summary ===");
|
||||
println!("Total messages: {}", messages.len());
|
||||
|
||||
let from_me = messages.iter().filter(|m| m.is_from_me).count();
|
||||
let from_them = messages.len() - from_me;
|
||||
|
||||
println!("From me: {}", from_me);
|
||||
println!("From them: {}", from_them);
|
||||
|
||||
// Show first few messages
|
||||
println!("\nFirst 5 messages:");
|
||||
for (i, msg) in messages.iter().take(5).enumerate() {
|
||||
if let Some(date) = msg.date {
|
||||
let sender = if msg.is_from_me { "Me" } else { "Them" };
|
||||
let text = msg.text.as_ref()
|
||||
.map(|t| if t.len() > 60 { format!("{}...", &t[..60]) } else { t.clone() })
|
||||
.unwrap_or_else(|| "[No text]".to_string());
|
||||
|
||||
println!("{}. {} ({}): {}", i + 1, date.format("%Y-%m-%d %H:%M"), sender, text);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
157
crates/lib/tests/sync_integration.rs
Normal file
157
crates/lib/tests/sync_integration.rs
Normal file
@@ -0,0 +1,157 @@
|
||||
use lib::sync::{synced, SyncMessage, Syncable};
|
||||
use iroh::{Endpoint, protocol::{Router, ProtocolHandler, AcceptError}};
|
||||
use anyhow::Result;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
/// Test configuration that can be synced
|
||||
#[synced]
|
||||
struct TestConfig {
|
||||
value: i32,
|
||||
name: String,
|
||||
|
||||
#[sync(skip)]
|
||||
node_id: String,
|
||||
}
|
||||
|
||||
/// ALPN identifier for our sync protocol
|
||||
const SYNC_ALPN: &[u8] = b"/lonni/sync/1";
|
||||
|
||||
/// Protocol handler for receiving sync messages
|
||||
#[derive(Debug, Clone)]
|
||||
struct SyncProtocol {
|
||||
config: Arc<Mutex<TestConfig>>,
|
||||
}
|
||||
|
||||
impl ProtocolHandler for SyncProtocol {
|
||||
async fn accept(&self, connection: iroh::endpoint::Connection) -> Result<(), AcceptError> {
|
||||
println!("Accepting connection from: {}", connection.remote_id());
|
||||
|
||||
// Accept the bidirectional stream
|
||||
let (mut send, mut recv) = connection.accept_bi().await
|
||||
.map_err(AcceptError::from_err)?;
|
||||
|
||||
println!("Stream accepted, reading message...");
|
||||
|
||||
// Read the sync message
|
||||
let bytes = recv.read_to_end(1024 * 1024).await
|
||||
.map_err(AcceptError::from_err)?;
|
||||
|
||||
println!("Received {} bytes", bytes.len());
|
||||
|
||||
// Deserialize and apply
|
||||
let msg = SyncMessage::<TestConfigOp>::from_bytes(&bytes)
|
||||
.map_err(|e| AcceptError::from_err(std::io::Error::new(std::io::ErrorKind::InvalidData, e)))?;
|
||||
|
||||
println!("Applying operation from node: {}", msg.node_id);
|
||||
|
||||
let mut config = self.config.lock().await;
|
||||
config.apply_op(&msg.operation);
|
||||
|
||||
println!("Operation applied successfully");
|
||||
|
||||
// Close the stream
|
||||
send.finish()
|
||||
.map_err(AcceptError::from_err)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_sync_between_two_nodes() -> Result<()> {
|
||||
println!("\n=== Testing Sync Between Two Nodes ===\n");
|
||||
|
||||
// Create two endpoints
|
||||
let node1 = Endpoint::builder().bind().await?;
|
||||
let node2 = Endpoint::builder().bind().await?;
|
||||
|
||||
let node1_addr = node1.addr();
|
||||
let node2_addr = node2.addr();
|
||||
|
||||
let node1_id = node1_addr.id.to_string();
|
||||
let node2_id = node2_addr.id.to_string();
|
||||
|
||||
println!("Node 1: {}", node1_id);
|
||||
println!("Node 2: {}", node2_id);
|
||||
|
||||
// Create synced configs on both nodes
|
||||
let mut config1 = TestConfig::new(
|
||||
42,
|
||||
"initial".to_string(),
|
||||
node1_id.clone(),
|
||||
);
|
||||
|
||||
let config2 = TestConfig::new(
|
||||
42,
|
||||
"initial".to_string(),
|
||||
node2_id.clone(),
|
||||
);
|
||||
let config2_shared = Arc::new(Mutex::new(config2));
|
||||
|
||||
println!("\nInitial state:");
|
||||
println!(" Node 1: value={}, name={}", config1.value(), config1.name());
|
||||
{
|
||||
let config2 = config2_shared.lock().await;
|
||||
println!(" Node 2: value={}, name={}", config2.value(), config2.name());
|
||||
}
|
||||
|
||||
// Set up router on node2 to accept incoming connections
|
||||
println!("\nSetting up node2 router...");
|
||||
let protocol = SyncProtocol {
|
||||
config: config2_shared.clone(),
|
||||
};
|
||||
let router = Router::builder(node2)
|
||||
.accept(SYNC_ALPN, protocol)
|
||||
.spawn();
|
||||
|
||||
router.endpoint().online().await;
|
||||
println!("✓ Node2 router ready");
|
||||
|
||||
// Node 1 changes the value
|
||||
println!("\nNode 1 changing value to 100...");
|
||||
let op = config1.set_value(100);
|
||||
|
||||
// Serialize the operation
|
||||
let sync_msg = SyncMessage::new(node1_id.clone(), op);
|
||||
let bytes = sync_msg.to_bytes()?;
|
||||
println!("Serialized to {} bytes", bytes.len());
|
||||
|
||||
// Establish QUIC connection from node1 to node2
|
||||
println!("\nEstablishing QUIC connection...");
|
||||
let conn = node1.connect(node2_addr.clone(), SYNC_ALPN).await?;
|
||||
println!("✓ Connection established");
|
||||
|
||||
// Open a bidirectional stream
|
||||
let (mut send, _recv) = conn.open_bi().await?;
|
||||
|
||||
// Send the sync message
|
||||
println!("Sending sync message...");
|
||||
send.write_all(&bytes).await?;
|
||||
send.finish()?;
|
||||
println!("✓ Message sent");
|
||||
|
||||
// Wait a bit for the message to be processed
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
||||
|
||||
// Verify both configs have the same value
|
||||
println!("\nFinal state:");
|
||||
println!(" Node 1: value={}, name={}", config1.value(), config1.name());
|
||||
{
|
||||
let config2 = config2_shared.lock().await;
|
||||
println!(" Node 2: value={}, name={}", config2.value(), config2.name());
|
||||
|
||||
assert_eq!(*config1.value(), 100);
|
||||
assert_eq!(*config2.value(), 100);
|
||||
assert_eq!(config1.name(), "initial");
|
||||
assert_eq!(config2.name(), "initial");
|
||||
}
|
||||
|
||||
println!("\n✓ Sync successful!");
|
||||
|
||||
// Cleanup
|
||||
router.shutdown().await?;
|
||||
node1.close().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user