use super::{SearchCell, Searcher};
use crate::registry::presence::Presence;
use crate::search::{Found, Hit};
use crate::test_support::wire::{EPHEMERAL, material, mint};
use crate::wire::material::Role;
use crate::wire::server::{Answerer, Listener};
use serde_json::{Value, json};
use std::sync::Arc;
use tempfile::TempDir;
struct Says(Vec<Value>);
impl Answerer for Says {
fn answer(
&self,
_client: &crate::registry::Client,
_request: Value,
) -> Box<dyn Iterator<Item = Value>> {
Box::new(self.0.clone().into_iter())
}
}
fn wired(tmp: &TempDir, says: Vec<Value>) -> (Listener, Searcher, SearchCell) {
mint(tmp.path());
let listener = Listener::bind(
&material(tmp.path(), Role::Server, EPHEMERAL),
Arc::new(Says(says)),
Presence::default(),
)
.expect("bind");
let seat = crate::wire::client::Seat::open(&material(
tmp.path(),
Role::Window,
&crate::wire::loopback(&listener.address()),
))
.expect("seat");
let asks = SearchCell::default();
(listener, Searcher::new(seat, asks.clone()), asks)
}
fn answers(needle: &str) -> Vec<Value> {
vec![json!({
"ok": true, "kind": "search", "needle": needle,
"rows": [{"at": "ball", "project": "/dev/yog", "id": "bl-1",
"field": "name", "offset": 0, "excerpt": needle}],
"unreadable": [],
})]
}
#[test]
fn the_searcher_asks_the_engine_and_publishes_what_came_back() {
let tmp = TempDir::new().expect("tmp");
let (_listener, searcher, asks) = wired(&tmp, answers("kraken"));
assert!(!searcher.pass(), "nothing asked, nothing answered");
asks.ask("kraken");
assert!(searcher.pass(), "the pass answered the ask");
let found = asks.found();
assert_eq!(found.needle, "kraken");
assert!(
matches!(found.hits.as_slice(), [Hit { .. }]),
"the engine's own row reached the cell: {found:?}"
);
assert!(!asks.searching());
}
#[test]
fn a_refusal_lands_as_the_reason_and_never_as_an_empty_answer() {
let tmp = TempDir::new().expect("tmp");
let (_listener, searcher, asks) = wired(
&tmp,
vec![json!({"ok": false, "error": "no such workspace: gone"})],
);
asks.ask("kraken");
assert!(searcher.pass());
let found = asks.found();
assert_eq!(
found.needle, "kraken",
"the answer still knows its question"
);
assert!(found.hits.is_empty());
assert_eq!(found.unreadable, vec!["no such workspace: gone".to_owned()]);
assert!(!found.is_empty(), "a refusal is content, not an empty pane");
}
#[test]
fn a_reply_of_another_kind_invents_nothing() {
let tmp = TempDir::new().expect("tmp");
let (_listener, searcher, asks) =
wired(&tmp, vec![json!({"ok": true, "kind": "balls", "rows": []})]);
asks.ask("kraken");
assert!(searcher.pass());
assert_eq!(asks.found(), Found::default());
}
#[test]
fn the_searcher_thread_answers_an_ask_and_stops_on_drop() {
let tmp = TempDir::new().expect("tmp");
let (_listener, searcher, asks) = wired(&tmp, answers("bl-thread"));
let thread = searcher.start();
asks.ask("bl-thread");
for _ in 0..200 {
if !asks.searching() {
break;
}
std::thread::sleep(std::time::Duration::from_millis(10));
}
assert_eq!(asks.found().hits.len(), 1, "the thread answered");
drop(thread);
}