Files
owlry/crates/owlry-core/tests/server_test.rs

240 lines
6.6 KiB
Rust

use std::io::{BufRead, BufReader, Write};
use std::os::unix::net::UnixStream;
use std::thread;
use owlry_core::ipc::{Request, Response};
use owlry_core::server::Server;
/// Helper: send a JSON request line and read the JSON response line.
fn roundtrip(stream: &mut UnixStream, request: &Request) -> Response {
let mut line = serde_json::to_string(request).unwrap();
line.push('\n');
stream.write_all(line.as_bytes()).unwrap();
stream.flush().unwrap();
let mut reader = BufReader::new(stream.try_clone().unwrap());
let mut buf = String::new();
reader.read_line(&mut buf).unwrap();
serde_json::from_str(buf.trim()).unwrap()
}
#[test]
fn test_server_responds_to_providers_request() {
let dir = tempfile::tempdir().unwrap();
let sock = dir.path().join("owlry-test.sock");
let server = Server::bind(&sock).unwrap();
// Spawn the server to handle exactly one connection
let handle = thread::spawn(move || {
server.handle_one_for_testing().unwrap();
});
// Connect as a client
let mut stream = UnixStream::connect(&sock).unwrap();
let resp = roundtrip(&mut stream, &Request::Providers);
match resp {
Response::Providers { list } => {
// The default ProviderManager always has at least Application and Command
assert!(
list.len() >= 2,
"expected at least 2 providers, got {}",
list.len()
);
let ids: Vec<&str> = list.iter().map(|p| p.id.as_str()).collect();
assert!(ids.contains(&"app"), "missing 'app' provider");
assert!(ids.contains(&"cmd"), "missing 'cmd' provider");
}
other => panic!("expected Providers response, got: {:?}", other),
}
drop(stream);
handle.join().unwrap();
}
#[test]
fn test_server_handles_launch_request() {
let dir = tempfile::tempdir().unwrap();
let sock = dir.path().join("owlry-test.sock");
let server = Server::bind(&sock).unwrap();
let handle = thread::spawn(move || {
server.handle_one_for_testing().unwrap();
});
let mut stream = UnixStream::connect(&sock).unwrap();
let req = Request::Launch {
item_id: "firefox.desktop".into(),
provider: "app".into(),
};
let resp = roundtrip(&mut stream, &req);
assert_eq!(resp, Response::Ack);
drop(stream);
handle.join().unwrap();
}
#[test]
fn test_server_handles_query_request() {
let dir = tempfile::tempdir().unwrap();
let sock = dir.path().join("owlry-test.sock");
let server = Server::bind(&sock).unwrap();
let handle = thread::spawn(move || {
server.handle_one_for_testing().unwrap();
});
let mut stream = UnixStream::connect(&sock).unwrap();
let req = Request::Query {
text: "nonexistent_query_xyz".into(),
modes: None,
};
let resp = roundtrip(&mut stream, &req);
match resp {
Response::Results { items } => {
// A nonsense query should return empty or very few results
// (no items will fuzzy-match "nonexistent_query_xyz")
assert!(
items.len() <= 5,
"expected few/no results for gibberish query"
);
}
other => panic!("expected Results response, got: {:?}", other),
}
drop(stream);
handle.join().unwrap();
}
#[test]
fn test_server_handles_toggle_request() {
let dir = tempfile::tempdir().unwrap();
let sock = dir.path().join("owlry-test.sock");
let server = Server::bind(&sock).unwrap();
let handle = thread::spawn(move || {
server.handle_one_for_testing().unwrap();
});
let mut stream = UnixStream::connect(&sock).unwrap();
let resp = roundtrip(&mut stream, &Request::Toggle);
assert_eq!(resp, Response::Ack);
drop(stream);
handle.join().unwrap();
}
#[test]
fn test_server_handles_refresh_request() {
let dir = tempfile::tempdir().unwrap();
let sock = dir.path().join("owlry-test.sock");
let server = Server::bind(&sock).unwrap();
let handle = thread::spawn(move || {
server.handle_one_for_testing().unwrap();
});
let mut stream = UnixStream::connect(&sock).unwrap();
let req = Request::Refresh {
provider: "app".into(),
};
let resp = roundtrip(&mut stream, &req);
assert_eq!(resp, Response::Ack);
drop(stream);
handle.join().unwrap();
}
#[test]
fn test_server_handles_submenu_for_unknown_plugin() {
let dir = tempfile::tempdir().unwrap();
let sock = dir.path().join("owlry-test.sock");
let server = Server::bind(&sock).unwrap();
let handle = thread::spawn(move || {
server.handle_one_for_testing().unwrap();
});
let mut stream = UnixStream::connect(&sock).unwrap();
let req = Request::Submenu {
plugin_id: "nonexistent_plugin".into(),
data: "some_data".into(),
};
let resp = roundtrip(&mut stream, &req);
match resp {
Response::Error { message } => {
assert!(
message.contains("nonexistent_plugin"),
"error should mention the plugin id"
);
}
other => panic!(
"expected Error response for unknown plugin, got: {:?}",
other
),
}
drop(stream);
handle.join().unwrap();
}
#[test]
fn test_server_handles_multiple_requests_per_connection() {
let dir = tempfile::tempdir().unwrap();
let sock = dir.path().join("owlry-test.sock");
let server = Server::bind(&sock).unwrap();
let handle = thread::spawn(move || {
server.handle_one_for_testing().unwrap();
});
let mut stream = UnixStream::connect(&sock).unwrap();
// Send Providers request
let resp1 = roundtrip(&mut stream, &Request::Providers);
assert!(matches!(resp1, Response::Providers { .. }));
// Send Toggle request on same connection
let resp2 = roundtrip(&mut stream, &Request::Toggle);
assert_eq!(resp2, Response::Ack);
drop(stream);
handle.join().unwrap();
}
#[test]
fn test_server_cleans_up_stale_socket() {
let dir = tempfile::tempdir().unwrap();
let sock = dir.path().join("owlry-test.sock");
// Create a stale socket file
std::os::unix::net::UnixListener::bind(&sock).unwrap();
assert!(sock.exists());
// Server::bind should succeed by removing the stale socket
let server = Server::bind(&sock).unwrap();
let handle = thread::spawn(move || {
server.handle_one_for_testing().unwrap();
});
let mut stream = UnixStream::connect(&sock).unwrap();
let resp = roundtrip(&mut stream, &Request::Toggle);
assert_eq!(resp, Response::Ack);
drop(stream);
handle.join().unwrap();
}