Skip to content

Commit 8636a7f

Browse files
add integration tests for retries, fix bugs that surfaced (#18)
1 parent a2ebd0d commit 8636a7f

File tree

3 files changed

+91
-26
lines changed

3 files changed

+91
-26
lines changed

crates/djls-ipc/src/client.rs

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
use anyhow::{Context, Result};
22
use async_trait::async_trait;
33
use serde::{Deserialize, Serialize};
4-
use std::{path::Path, time::Duration};
4+
use std::{
5+
path::{Path, PathBuf},
6+
time::Duration,
7+
};
58
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
69

7-
#[derive(Clone, Debug)]
10+
#[derive(Clone, Copy, Debug)]
811
pub(crate) struct ConnectionConfig {
912
max_retries: u32,
1013
initial_delay_ms: u64,
@@ -103,6 +106,7 @@ pub struct Message<T> {
103106
pub struct Client {
104107
connection: Box<dyn ConnectionTrait>,
105108
message_id: u64,
109+
socket_path: PathBuf,
106110
}
107111

108112
impl Client {
@@ -111,6 +115,7 @@ impl Client {
111115
Ok(Self {
112116
connection,
113117
message_id: 0,
118+
socket_path: path.to_owned(),
114119
})
115120
}
116121

@@ -378,16 +383,21 @@ mod client_tests {
378383
value: String,
379384
}
380385

386+
fn create_test_client(mock_conn: MockConnection) -> Client {
387+
Client {
388+
connection: Box::new(mock_conn),
389+
message_id: 0,
390+
socket_path: PathBuf::from("/test/socket"),
391+
}
392+
}
393+
381394
#[tokio::test]
382395
async fn test_successful_message_exchange() -> Result<()> {
383396
let mock_conn = MockConnection::new(vec![Ok(
384397
r#"{"id":1,"content":{"value":"response"}}"#.to_string()
385398
)]);
386399

387-
let mut client = Client {
388-
connection: Box::new(mock_conn),
389-
message_id: 0,
390-
};
400+
let mut client = create_test_client(mock_conn);
391401

392402
let request = TestMessage {
393403
value: "test".to_string(),
@@ -403,10 +413,7 @@ mod client_tests {
403413
async fn test_connection_error() {
404414
let mock_conn = MockConnection::new(vec![Err(anyhow::anyhow!("Connection error"))]);
405415

406-
let mut client = Client {
407-
connection: Box::new(mock_conn),
408-
message_id: 0,
409-
};
416+
let mut client = create_test_client(mock_conn);
410417

411418
let request = TestMessage {
412419
value: "test".to_string(),
@@ -422,10 +429,7 @@ mod client_tests {
422429
r#"{"id":2,"content":{"value":"response"}}"#.to_string()
423430
)]);
424431

425-
let mut client = Client {
426-
connection: Box::new(mock_conn),
427-
message_id: 0,
428-
};
432+
let mut client = create_test_client(mock_conn);
429433

430434
let request = TestMessage {
431435
value: "test".to_string(),
@@ -443,10 +447,7 @@ mod client_tests {
443447
async fn test_invalid_json_response() {
444448
let mock_conn = MockConnection::new(vec![Ok("invalid json".to_string())]);
445449

446-
let mut client = Client {
447-
connection: Box::new(mock_conn),
448-
message_id: 0,
449-
};
450+
let mut client = create_test_client(mock_conn);
450451

451452
let request = TestMessage {
452453
value: "test".to_string(),
@@ -462,10 +463,7 @@ mod client_tests {
462463
Ok(r#"{"id":2,"content":{"value":"response2"}}"#.to_string()),
463464
]);
464465

465-
let mut client = Client {
466-
connection: Box::new(mock_conn),
467-
message_id: 0,
468-
};
466+
let mut client = create_test_client(mock_conn);
469467

470468
let request1 = TestMessage {
471469
value: "test1".to_string(),

crates/djls-ipc/src/server.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ impl Server {
2424
fn start_with_options(python_path: &str, args: &[&str], use_module: bool) -> Result<Self> {
2525
let temp_dir = tempdir()?;
2626

27-
let path = {
28-
let socket_path = temp_dir.path().join("ipc.sock");
29-
socket_path
30-
};
27+
let path = args
28+
.windows(2)
29+
.find(|w| w[0] == "--ipc-path")
30+
.map(|w| PathBuf::from(w[1]))
31+
.unwrap_or_else(|| temp_dir.path().join("ipc.sock"));
3132

3233
let mut command = Command::new("python");
3334
if use_module {

crates/djls-ipc/tests/integration.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
use std::{
2+
path::Path,
3+
time::{Duration, Instant},
4+
};
5+
16
use anyhow::Result;
27
use djls_ipc::{Client, Server};
38
use serde::{Deserialize, Serialize};
@@ -139,3 +144,64 @@ async fn test_rapid_messages() -> Result<()> {
139144

140145
Ok(())
141146
}
147+
148+
#[tokio::test]
149+
async fn test_connect_with_delayed_server() -> Result<()> {
150+
let path = format!("{}/echo_server.py", FIXTURES_PATH);
151+
let server_path = Path::new(&path).to_owned();
152+
153+
// Start server with a shorter delay
154+
let server_handle = tokio::spawn(async move {
155+
tokio::time::sleep(Duration::from_millis(50)).await;
156+
Server::start_script(server_path.to_str().unwrap(), &[])
157+
});
158+
159+
// Wait for server to start
160+
let server = server_handle.await??;
161+
let mut client = Client::connect(server.get_path()).await?;
162+
163+
// Test the connection works
164+
let msg = "test".to_string();
165+
let response: String = client.send(msg.clone()).await?;
166+
assert_eq!(response, msg);
167+
168+
Ok(())
169+
}
170+
171+
#[tokio::test]
172+
async fn test_connect_with_server_restart() -> Result<()> {
173+
let temp_dir = tempfile::tempdir()?;
174+
let socket_path = temp_dir.path().join("ipc.sock");
175+
176+
// Start first server
177+
let path = format!("{}/echo_server.py", FIXTURES_PATH);
178+
let server = Server::start_script(&path, &["--ipc-path", socket_path.to_str().unwrap()])?;
179+
180+
let mut client = Client::connect(&socket_path).await?;
181+
182+
let msg = "test".to_string();
183+
let response: String = client.send(msg.clone()).await?;
184+
assert_eq!(response, msg);
185+
186+
// Drop old server and client
187+
drop(server);
188+
drop(client);
189+
tokio::time::sleep(Duration::from_millis(50)).await;
190+
191+
// Start new server
192+
let new_server = Server::start_script(&path, &["--ipc-path", socket_path.to_str().unwrap()])?;
193+
println!(
194+
"Second server started, socket path: {:?}",
195+
new_server.get_path()
196+
);
197+
198+
// Create new client
199+
let mut new_client = Client::connect(&socket_path).await?;
200+
201+
// Try to send a message
202+
let msg = "test after restart".to_string();
203+
let response: String = new_client.send(msg.clone()).await?;
204+
assert_eq!(response, msg);
205+
206+
Ok(())
207+
}

0 commit comments

Comments
 (0)