20bdf56668
mDNS-style LAN discovery (iroh MdnsDiscovery, discovery flag, default on) feeds a presence tracker; trusted peers that appear trigger fetches of every incomplete pin, and Pin itself now fetches from present trusted peers. Serving is our own ProtocolHandler: trusted NodeIds get the full store, everyone else a filtered view limited to open_lan pins and ticket-exported hashes (plus hashseq children) that answers "not found" for the rest. Ticket export records standing serve-consent for that hash; trust changes take effect on new connections. ShapedStore implements the full iroh-blobs Store trait to charge provider reads to an upload token bucket and downloader writes to a download bucket; upload cap 0 closes incoming connections at accept. Subscribe now streams transfer_progress both ways, peer_joined, and pin_complete. Tests: forged-ticket trust gating (denied untrusted, served after trust), 256 KiB/s upload cap enforced by wall clock, event stream during a transfer, and real-mdns auto-sync between two daemons (skips where multicast is unavailable). Dependencies: n0-future, async-channel, futures-lite, bytes — all already in the tree via iroh; needed directly to name types in iroh-blobs trait signatures and channels. iroh feature discovery-local-network for MdnsDiscovery. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
157 lines
4.7 KiB
Rust
157 lines
4.7 KiB
Rust
//! Shared helpers for integration tests: spawn the real daemon binary on
|
|
//! ephemeral paths and speak the JSON-lines protocol to it.
|
|
|
|
// Each test binary compiles its own copy of this module and none of them
|
|
// uses every helper.
|
|
#![allow(dead_code)]
|
|
|
|
use std::io::{BufRead, BufReader, Write};
|
|
use std::os::unix::net::UnixStream;
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::{Child, Command};
|
|
use std::time::{Duration, Instant};
|
|
|
|
use varde_proto::{Envelope, Request};
|
|
|
|
/// A running daemon process, killed on drop.
|
|
pub struct DaemonProc {
|
|
pub child: Child,
|
|
pub socket: PathBuf,
|
|
pub store: PathBuf,
|
|
// Kept alive so the store/socket dirs survive as long as the daemon.
|
|
_dir: Option<tempfile::TempDir>,
|
|
}
|
|
|
|
impl DaemonProc {
|
|
/// SIGKILL the daemon — no cleanup handlers run, simulating a crash.
|
|
pub fn kill_dash_nine(&mut self) {
|
|
let _ = self.child.kill();
|
|
let _ = self.child.wait();
|
|
}
|
|
}
|
|
|
|
impl Drop for DaemonProc {
|
|
fn drop(&mut self) {
|
|
let _ = self.child.kill();
|
|
let _ = self.child.wait();
|
|
}
|
|
}
|
|
|
|
/// Spawn the compiled varde-daemon on a fresh tempdir.
|
|
pub fn spawn_daemon() -> DaemonProc {
|
|
let dir = tempfile::tempdir().expect("tempdir");
|
|
let mut proc = spawn_daemon_at(dir.path());
|
|
proc._dir = Some(dir);
|
|
proc
|
|
}
|
|
|
|
/// Spawn the daemon against an existing directory (for restart tests).
|
|
pub fn spawn_daemon_at(dir: &Path) -> DaemonProc {
|
|
spawn_daemon_with_env(dir, &[])
|
|
}
|
|
|
|
/// Spawn the daemon with extra environment variables (config overrides).
|
|
pub fn spawn_daemon_with_env(dir: &Path, envs: &[(&str, &str)]) -> DaemonProc {
|
|
let socket = dir.join("varde.sock");
|
|
let store = dir.join("store");
|
|
let mut command = Command::new(env!("CARGO_BIN_EXE_varde-daemon"));
|
|
command
|
|
.arg("--user")
|
|
.arg("--socket")
|
|
.arg(&socket)
|
|
.arg("--store")
|
|
.arg(&store)
|
|
// Isolate from any config file on the developer's machine.
|
|
.env("VARDE_CONFIG", dir.join("no-config.toml"))
|
|
.env("VARDE_DISCOVERY", "false");
|
|
for (key, value) in envs {
|
|
command.env(key, value);
|
|
}
|
|
let child = command.spawn().expect("spawning varde-daemon");
|
|
// An explicitly named config must exist for the daemon to start.
|
|
std::fs::write(dir.join("no-config.toml"), "").expect("writing empty config");
|
|
|
|
let proc = DaemonProc {
|
|
child,
|
|
socket,
|
|
store,
|
|
_dir: None,
|
|
};
|
|
wait_for_socket(&proc.socket);
|
|
proc
|
|
}
|
|
|
|
fn wait_for_socket(path: &Path) {
|
|
let deadline = Instant::now() + Duration::from_secs(10);
|
|
while Instant::now() < deadline {
|
|
if UnixStream::connect(path).is_ok() {
|
|
return;
|
|
}
|
|
std::thread::sleep(Duration::from_millis(20));
|
|
}
|
|
panic!("daemon socket {} never appeared", path.display());
|
|
}
|
|
|
|
/// One connected API client.
|
|
pub struct Client {
|
|
reader: BufReader<UnixStream>,
|
|
writer: UnixStream,
|
|
}
|
|
|
|
impl Client {
|
|
pub fn connect(socket: &Path) -> Client {
|
|
let stream = UnixStream::connect(socket).expect("connecting to daemon");
|
|
let reader = BufReader::new(stream.try_clone().expect("cloning stream"));
|
|
Client {
|
|
reader,
|
|
writer: stream,
|
|
}
|
|
}
|
|
|
|
/// Send a request and read the response envelope.
|
|
pub fn request(&mut self, req: &Request) -> Envelope {
|
|
let mut line = serde_json::to_string(req).expect("serializing request");
|
|
line.push('\n');
|
|
self.writer
|
|
.write_all(line.as_bytes())
|
|
.expect("writing request");
|
|
self.read_envelope()
|
|
}
|
|
|
|
/// Send a raw line (for malformed-input tests).
|
|
pub fn send_raw(&mut self, raw: &str) -> Envelope {
|
|
self.writer
|
|
.write_all(raw.as_bytes())
|
|
.expect("writing raw line");
|
|
self.writer.write_all(b"\n").expect("writing newline");
|
|
self.read_envelope()
|
|
}
|
|
|
|
fn read_envelope(&mut self) -> Envelope {
|
|
let mut reply = String::new();
|
|
self.reader.read_line(&mut reply).expect("reading reply");
|
|
assert!(
|
|
!reply.is_empty(),
|
|
"daemon closed connection without replying"
|
|
);
|
|
serde_json::from_str(&reply).expect("parsing reply envelope")
|
|
}
|
|
|
|
/// Bound how long reads may block (applies to the shared socket).
|
|
pub fn set_read_timeout(&mut self, timeout: Duration) {
|
|
self.writer
|
|
.set_read_timeout(Some(timeout))
|
|
.expect("setting read timeout");
|
|
}
|
|
|
|
/// Read one line of the event stream (after Subscribe).
|
|
pub fn read_event_line(&mut self) -> Option<String> {
|
|
let mut line = String::new();
|
|
match self.reader.read_line(&mut line) {
|
|
Ok(0) => None,
|
|
Ok(_) => Some(line),
|
|
Err(e) => panic!("reading event line: {e}"),
|
|
}
|
|
}
|
|
}
|