Files
frxd/tests/phase1.rs
T

117 lines
4.0 KiB
Rust

mod common;
use std::fs;
use common::{ask, client, collection, config_for, publish, spawn_relay, test_envelope};
use frxd::index::LocalIndex;
use frxd::message::{EXPOSURE_FULL, 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 = test_envelope(
&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);
}