Milestone 1: workspace skeleton, wire protocol, socket round-trip
Three-crate workspace per SPECS.md. varde-proto defines the full JSON Lines protocol (requests, envelopes, structured errors, events) with string-typed hashes so the crate carries no iroh dependency. The daemon binds its unix socket, loads config with flags > env > file > defaults precedence, and answers status/list; everything else returns a structured "unimplemented" error. varde-ctl maps subcommands 1:1 onto requests and round-trips status against a real daemon in the tests. Dependencies: serde/serde_json (wire format), tokio (async runtime and unix sockets), tracing/tracing-subscriber (structured logging), toml (config file), anyhow (binary-edge errors), thiserror (reserved for library errors), clap (ctl flag parsing, per spec), tempfile (dev-only, ephemeral test dirs). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
//! Milestone 1: the daemon binds its socket and answers the protocol.
|
||||
|
||||
mod support;
|
||||
|
||||
use varde_proto::{ErrorCode, Request, ResponseData, PROTOCOL_VERSION};
|
||||
|
||||
#[test]
|
||||
fn status_round_trips_on_empty_daemon() {
|
||||
let daemon = support::spawn_daemon();
|
||||
let mut client = support::Client::connect(&daemon.socket);
|
||||
|
||||
let reply = client.request(&Request::Status { hash: None });
|
||||
assert!(reply.ok, "status failed: {:?}", reply.error);
|
||||
match reply.data {
|
||||
Some(ResponseData::Status(s)) => {
|
||||
assert_eq!(s.protocol, PROTOCOL_VERSION);
|
||||
assert_eq!(s.pins, 0);
|
||||
assert_eq!(s.store_path, daemon.store.display().to_string());
|
||||
}
|
||||
other => panic!("unexpected status payload: {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_is_empty_on_fresh_daemon() {
|
||||
let daemon = support::spawn_daemon();
|
||||
let mut client = support::Client::connect(&daemon.socket);
|
||||
|
||||
let reply = client.request(&Request::List {});
|
||||
assert!(reply.ok);
|
||||
match reply.data {
|
||||
Some(ResponseData::Pins { pins }) => assert!(pins.is_empty()),
|
||||
other => panic!("unexpected list payload: {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn malformed_json_gets_structured_error_and_connection_survives() {
|
||||
let daemon = support::spawn_daemon();
|
||||
let mut client = support::Client::connect(&daemon.socket);
|
||||
|
||||
let reply = client.send_raw("this is not json");
|
||||
assert!(!reply.ok);
|
||||
assert_eq!(reply.error.expect("error body").code, ErrorCode::BadRequest);
|
||||
|
||||
// The same connection keeps working after a bad request.
|
||||
let reply = client.request(&Request::Status { hash: None });
|
||||
assert!(reply.ok);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_concurrent_clients() {
|
||||
let daemon = support::spawn_daemon();
|
||||
let mut a = support::Client::connect(&daemon.socket);
|
||||
let mut b = support::Client::connect(&daemon.socket);
|
||||
|
||||
assert!(a.request(&Request::Status { hash: None }).ok);
|
||||
assert!(b.request(&Request::List {}).ok);
|
||||
assert!(a.request(&Request::List {}).ok);
|
||||
}
|
||||
Reference in New Issue
Block a user