117 lines
4.0 KiB
Rust
117 lines
4.0 KiB
Rust
mod common;
|
|
|
|
use std::fs;
|
|
|
|
use common::{ask, client, collection, config_for, publish, spawn_relay};
|
|
use frxd::index::LocalIndex;
|
|
use frxd::message::{EXPOSURE_FULL, Envelope, TYPE_RESPONSE};
|
|
use frxd::node::Node;
|
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
|
async fn two_node_broadcast_query_flow() {
|
|
let root = tempfile::tempdir().unwrap();
|
|
let relay_url = spawn_relay().await;
|
|
|
|
let shared_dir = root.path().join("bob/shared");
|
|
let private_dir = root.path().join("bob/private");
|
|
fs::create_dir_all(&shared_dir).unwrap();
|
|
fs::create_dir_all(&private_dir).unwrap();
|
|
fs::write(
|
|
shared_dir.join("alpha.txt"),
|
|
"Tantivy provides BM25 relevance scoring for rust search",
|
|
)
|
|
.unwrap();
|
|
fs::write(
|
|
shared_dir.join("beta.txt"),
|
|
"Rust lifetimes and borrowing explained",
|
|
)
|
|
.unwrap();
|
|
fs::write(private_dir.join("gamma.txt"), "rust secret launch codes").unwrap();
|
|
|
|
let bob_config = config_for(&root.path().join("bob"), "bob", &relay_url);
|
|
{
|
|
let index = LocalIndex::open(&bob_config.index_dir()).unwrap();
|
|
index
|
|
.add_collection(&collection("shared", &shared_dir, true, EXPOSURE_FULL))
|
|
.unwrap();
|
|
index
|
|
.add_collection(&collection("private", &private_dir, false, "metadata"))
|
|
.unwrap();
|
|
}
|
|
let _bob = Node::start(bob_config).await.unwrap();
|
|
|
|
let alice_dir = root.path().join("alice");
|
|
let alice_local = root.path().join("alice_mine");
|
|
fs::create_dir_all(&alice_local).unwrap();
|
|
fs::write(alice_local.join("notes.txt"), "my rust notes").unwrap();
|
|
let alice_config = config_for(&alice_dir, "alice", &relay_url);
|
|
{
|
|
let index = LocalIndex::open(&alice_config.index_dir()).unwrap();
|
|
index
|
|
.add_collection(&collection("mine", &alice_local, false, "metadata"))
|
|
.unwrap();
|
|
}
|
|
let alice = Node::start(alice_config).await.unwrap();
|
|
|
|
let http = client();
|
|
let addr = alice.addr.to_string();
|
|
|
|
let (status, raw, value) = ask(&http, &addr, "rust", 1).await;
|
|
assert!(status.is_success());
|
|
assert_eq!(
|
|
value.pointer("/local/total").and_then(|v| v.as_u64()),
|
|
Some(1)
|
|
);
|
|
let responses = value.get("responses").and_then(|v| v.as_array()).unwrap();
|
|
assert_eq!(responses.len(), 1, "expected one responder: {value}");
|
|
let response = &responses[0];
|
|
let results = response.get("results").and_then(|v| v.as_array()).unwrap();
|
|
assert_eq!(results.len(), 1);
|
|
assert_eq!(
|
|
response.get("truncated").and_then(|v| v.as_bool()),
|
|
Some(true)
|
|
);
|
|
assert_eq!(
|
|
response.get("more_available").and_then(|v| v.as_u64()),
|
|
Some(1)
|
|
);
|
|
assert!(!raw.contains("gamma"), "unshared collection leaked: {raw}");
|
|
assert!(!raw.contains("\"score\""), "response carries scores: {raw}");
|
|
|
|
let (_status, _raw, value) = ask(&http, &addr, "rust", 5).await;
|
|
let responses = value.get("responses").and_then(|v| v.as_array()).unwrap();
|
|
assert_eq!(responses.len(), 1);
|
|
assert_eq!(
|
|
responses[0]
|
|
.get("results")
|
|
.and_then(|v| v.as_array())
|
|
.unwrap()
|
|
.len(),
|
|
2
|
|
);
|
|
assert_eq!(
|
|
responses[0].get("truncated").and_then(|v| v.as_bool()),
|
|
Some(false)
|
|
);
|
|
|
|
let (_status, _raw, value) = ask(&http, &addr, "secret launch codes", 5).await;
|
|
let responses = value.get("responses").and_then(|v| v.as_array()).unwrap();
|
|
assert!(
|
|
responses.is_empty(),
|
|
"private collection was served: {value}"
|
|
);
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
|
async fn relay_rejects_non_query_broadcasts() {
|
|
let relay_url = spawn_relay().await;
|
|
let key = frxd::crypto::Keypair::generate();
|
|
let envelope = Envelope::new(
|
|
&key,
|
|
TYPE_RESPONSE,
|
|
serde_json::json!({"qid": "x", "results": []}),
|
|
);
|
|
let response = publish(&client(), &relay_url, &envelope).await;
|
|
assert_eq!(response.status(), reqwest::StatusCode::BAD_REQUEST);
|
|
}
|