Files
frxd/tests/aggregates.rs
T

208 lines
5.5 KiB
Rust

mod common;
use std::fs;
use std::path::Path;
use common::{ask, client, collection, config_for, spawn_relay};
use frxd::config::{Member, save_members};
use frxd::index::LocalIndex;
use frxd::message::EXPOSURE_FULL;
use frxd::node::{self, Node, NodeHandle, current_period};
use serde_json::Value;
fn corpus_dir(root: &Path, name: &str, files: &[(&str, &str)]) -> std::path::PathBuf {
let dir = root.join(format!("{name}-docs"));
fs::create_dir_all(&dir).unwrap();
for (file, text) in files {
fs::write(dir.join(file), text).unwrap();
}
dir
}
async fn start_node(
root: &Path,
name: &str,
relay_url: &str,
exposure: &str,
files: &[(&str, &str)],
) -> NodeHandle {
let docs = corpus_dir(root, name, files);
let config = config_for(&root.join(name), name, relay_url);
{
let index = LocalIndex::open(&config.index_dir()).unwrap();
index
.add_collection(&collection("docs", &docs, true, exposure))
.unwrap();
}
Node::start(config).await.unwrap()
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn aggregates_count_sent_and_passed_per_member() {
let root = tempfile::tempdir().unwrap();
let relay_url = spawn_relay().await;
let _bob = start_node(
root.path(),
"bob",
&relay_url,
EXPOSURE_FULL,
&[("doc.txt", "aggregate rust document")],
)
.await;
let alice = start_node(
root.path(),
"alice",
&relay_url,
EXPOSURE_FULL,
&[("mine.txt", "alice rust note")],
)
.await;
let (_status, _raw, value) = ask(&client(), &alice.addr.to_string(), "rust", 5).await;
assert_eq!(
value
.get("responses")
.and_then(Value::as_array)
.unwrap()
.len(),
1
);
let period = current_period();
let aggregate = node::control_aggregate_request(
&format!("http://{}", _bob.addr),
&alice.pubkey,
&period,
Some(700),
)
.await
.unwrap();
assert_eq!(aggregate.get("sent").and_then(Value::as_u64), Some(1));
assert_eq!(aggregate.get("passed").and_then(Value::as_u64), Some(1));
assert!(aggregate.get("cited").is_none());
assert_eq!(
aggregate.get("period").and_then(Value::as_str),
Some(period.as_str())
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn aggregate_rollup_for_year_sums_months() {
let root = tempfile::tempdir().unwrap();
let relay_url = spawn_relay().await;
let bob = start_node(
root.path(),
"bob",
&relay_url,
EXPOSURE_FULL,
&[("doc.txt", "rollup rust document")],
)
.await;
let alice = start_node(
root.path(),
"alice",
&relay_url,
EXPOSURE_FULL,
&[("mine.txt", "alice rust note")],
)
.await;
let (_status, _raw, _value) = ask(&client(), &alice.addr.to_string(), "rust", 5).await;
let year = current_period()[0..4].to_string();
let aggregate = node::control_aggregate_request(
&format!("http://{}", bob.addr),
&alice.pubkey,
&year,
Some(700),
)
.await
.unwrap();
assert_eq!(aggregate.get("sent").and_then(Value::as_u64), Some(1));
assert_eq!(
aggregate.get("period").and_then(Value::as_str),
Some(year.as_str())
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn aggregate_floor_rejects_finer_than_month() {
let root = tempfile::tempdir().unwrap();
let alice = start_node(
root.path(),
"alice",
&spawn_relay().await,
EXPOSURE_FULL,
&[("mine.txt", "alice rust note")],
)
.await;
let http = client();
let response = http
.get(format!(
"http://{}/v1/local/aggregates?period=2026-09-15",
alice.addr
))
.send()
.await
.unwrap();
assert_eq!(response.status(), reqwest::StatusCode::BAD_REQUEST);
let local = http
.get(format!("http://{}/v1/local/aggregates", alice.addr))
.send()
.await
.unwrap();
assert!(local.status().is_success());
let config = alice.node.config.clone();
let direct = alice
.node
.request_aggregate(&config.node.relays[0], "2026-09-15", Some(50))
.await;
assert!(direct.is_err());
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn full_exposure_responses_carry_content() {
let root = tempfile::tempdir().unwrap();
let relay_url = spawn_relay().await;
let bob = start_node(
root.path(),
"bob",
&relay_url,
EXPOSURE_FULL,
&[("doc.txt", "source test rust content")],
)
.await;
let alice = start_node(
root.path(),
"alice",
&relay_url,
EXPOSURE_FULL,
&[("mine.txt", "alice local")],
)
.await;
save_members(
&alice.node.config.members_path(),
&[Member {
name: "bob".to_string(),
pubkey: bob.pubkey.clone(),
previous: Vec::new(),
}],
)
.unwrap();
let (_status, _raw, value) = ask(&client(), &alice.addr.to_string(), "rust", 5).await;
let responses = value.get("responses").and_then(Value::as_array).unwrap();
assert_eq!(responses.len(), 1);
assert!(
responses[0]
.get("results")
.and_then(Value::as_array)
.unwrap()[0]
.get("content")
.unwrap()
.is_string()
);
}