test: cover all protocol roundtrips

This commit is contained in:
Eric Wendland 2026-07-05 22:30:15 +02:00
commit 0d588fb3d9
2 changed files with 674 additions and 4 deletions

View file

@ -1809,6 +1809,676 @@ pub fn decode_overlay_wire_response(line: &str) -> Result<OverlayWireResponse, C
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use serde_json::{Map, Value, json};
const CONTROL_REQUEST_VARIANTS: usize = 122;
const CONTROL_RESPONSE_VARIANTS: usize = 114;
const PEER_CONTROL_REQUEST_VARIANTS: usize = 19;
const PEER_CONTROL_RESPONSE_VARIANTS: usize = 20;
const PIPE_WIRE_REQUEST_VARIANTS: usize = 3;
const PIPE_WIRE_RESPONSE_VARIANTS: usize = 3;
const OVERLAY_WIRE_REQUEST_VARIANTS: usize = 1;
const OVERLAY_WIRE_RESPONSE_VARIANTS: usize = 2;
#[test]
fn all_protocol_variants_have_roundtrip_samples() {
roundtrip_declared_variants::<ControlRequest>(
"ControlRequest",
CONTROL_REQUEST_VARIANTS,
decode_request,
encode_request,
);
roundtrip_declared_variants::<ControlResponse>(
"ControlResponse",
CONTROL_RESPONSE_VARIANTS,
decode_response,
encode_response,
);
roundtrip_declared_variants::<PeerControlRequest>(
"PeerControlRequest",
PEER_CONTROL_REQUEST_VARIANTS,
decode_peer_request,
encode_peer_request,
);
roundtrip_declared_variants::<PeerControlResponse>(
"PeerControlResponse",
PEER_CONTROL_RESPONSE_VARIANTS,
decode_peer_response,
encode_peer_response,
);
roundtrip_declared_variants::<PipeWireRequest>(
"PipeWireRequest",
PIPE_WIRE_REQUEST_VARIANTS,
decode_pipe_wire_request,
encode_pipe_wire_request,
);
roundtrip_declared_variants::<PipeWireResponse>(
"PipeWireResponse",
PIPE_WIRE_RESPONSE_VARIANTS,
decode_pipe_wire_response,
encode_pipe_wire_response,
);
roundtrip_declared_variants::<OverlayWireRequest>(
"OverlayWireRequest",
OVERLAY_WIRE_REQUEST_VARIANTS,
decode_overlay_wire_request,
encode_overlay_wire_request,
);
roundtrip_declared_variants::<OverlayWireResponse>(
"OverlayWireResponse",
OVERLAY_WIRE_RESPONSE_VARIANTS,
decode_overlay_wire_response,
encode_overlay_wire_response,
);
}
fn roundtrip_declared_variants<T>(
enum_name: &str,
expected_count: usize,
decode: fn(&str) -> Result<T, ControlError>,
encode: fn(&T) -> Result<String, ControlError>,
) where
T: std::fmt::Debug + PartialEq,
{
let variants = declared_enum_variants(enum_name);
assert_eq!(variants.len(), expected_count, "{enum_name} variant count");
for variant in variants {
let sample = protocol_sample(&variant);
let encoded_sample = serde_json::to_string(&sample).expect("sample json");
let decoded = decode(&encoded_sample).unwrap_or_else(|err| {
panic!("{enum_name}::{} sample decodes: {err}", variant.name)
});
let encoded = encode(&decoded).unwrap_or_else(|err| {
panic!("{enum_name}::{} sample encodes: {err}", variant.name)
});
let decoded_again = decode(&encoded).unwrap_or_else(|err| {
panic!(
"{enum_name}::{} encoded sample decodes: {err}",
variant.name
)
});
assert_eq!(
decoded_again, decoded,
"{enum_name}::{} roundtrips through line codec",
variant.name
);
}
}
#[derive(Debug)]
struct EnumVariant {
name: String,
body: VariantBody,
}
#[derive(Debug)]
enum VariantBody {
Unit,
Tuple(String),
Struct(Vec<(String, String)>),
}
fn declared_enum_variants(enum_name: &str) -> Vec<EnumVariant> {
let source = include_str!("lib.rs");
let needle = format!("pub enum {enum_name}");
let start = source.find(&needle).expect("enum exists");
let after_name = &source[start + needle.len()..];
let open = after_name.find('{').expect("enum opens");
let block = enum_block(&after_name[open..]);
split_top_level(block)
.into_iter()
.filter_map(|decl| parse_variant_decl(decl.trim()))
.collect()
}
fn enum_block(input: &str) -> &str {
let mut depth = 0_i32;
let mut start = None;
for (idx, ch) in input.char_indices() {
match ch {
'{' => {
depth += 1;
if start.is_none() {
start = Some(idx + 1);
}
}
'}' => {
depth -= 1;
if depth == 0 {
return &input[start.expect("block start")..idx];
}
}
_ => {}
}
}
panic!("unterminated enum block");
}
fn split_top_level(input: &str) -> Vec<&str> {
let mut parts = Vec::new();
let mut start = 0;
let mut brace_depth = 0_i32;
let mut paren_depth = 0_i32;
let mut angle_depth = 0_i32;
for (idx, ch) in input.char_indices() {
match ch {
'{' => brace_depth += 1,
'}' => brace_depth -= 1,
'(' => paren_depth += 1,
')' => paren_depth -= 1,
'<' => angle_depth += 1,
'>' => angle_depth -= 1,
',' if brace_depth == 0 && paren_depth == 0 && angle_depth == 0 => {
parts.push(&input[start..idx]);
start = idx + 1;
}
_ => {}
}
}
if start < input.len() {
parts.push(&input[start..]);
}
parts
}
fn parse_variant_decl(decl: &str) -> Option<EnumVariant> {
let decl = decl.trim();
if decl.is_empty() || decl.starts_with("#[") {
return None;
}
let name_len = decl
.find(|ch: char| !(ch.is_ascii_alphanumeric() || ch == '_'))
.unwrap_or(decl.len());
let name = decl[..name_len].to_owned();
let rest = decl[name_len..].trim();
let body = if rest.starts_with('{') {
let fields = split_top_level(enum_block(rest))
.into_iter()
.filter_map(|field| {
let (name, ty) = field.trim().split_once(':')?;
Some((name.trim().to_owned(), ty.trim().to_owned()))
})
.collect();
VariantBody::Struct(fields)
} else if let Some(tuple) = rest
.strip_prefix('(')
.and_then(|rest| rest.split(')').next())
{
VariantBody::Tuple(tuple.trim().to_owned())
} else {
VariantBody::Unit
};
Some(EnumVariant { name, body })
}
fn protocol_sample(variant: &EnumVariant) -> Value {
let mut object = Map::new();
object.insert("type".to_owned(), Value::String(kebab_case(&variant.name)));
match &variant.body {
VariantBody::Unit => {}
VariantBody::Tuple(ty) => {
let Value::Object(fields) = sample_for_type(ty) else {
panic!("tuple variant {} must use object sample", variant.name);
};
object.extend(fields);
}
VariantBody::Struct(fields) => {
for (name, ty) in fields {
object.insert(name.clone(), sample_for_type(ty));
}
}
}
Value::Object(object)
}
fn kebab_case(name: &str) -> String {
let mut out = String::new();
for (idx, ch) in name.chars().enumerate() {
if ch.is_ascii_uppercase() {
if idx > 0 {
out.push('-');
}
out.push(ch.to_ascii_lowercase());
} else {
out.push(ch);
}
}
out
}
fn sample_for_type(ty: &str) -> Value {
let ty = compact_type(ty);
if let Some(inner) = ty
.strip_prefix("Option<")
.and_then(|ty| ty.strip_suffix('>'))
{
let _ = inner;
return Value::Null;
}
if ty.starts_with("Vec<") {
return json!([]);
}
if let Some(inner) = ty.strip_prefix("Box<").and_then(|ty| ty.strip_suffix('>')) {
return sample_for_type(inner);
}
match ty.as_str() {
"String" | "PathBuf" => json!("sample"),
"bool" => json!(true),
"usize" | "u64" | "u32" | "u16" | "i64" => json!(1),
"BlobHash" => json!("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"),
"StatusResponse" => json!({
"home": "/tmp/geth-home",
"socket": "/tmp/geth.sock",
"agent_id": "agent:local",
"node_id": "node:local",
"daemon_started_at_ms": 1,
"daemon_uptime_seconds": 1,
"store_schema_version": 1,
"store_current_schema_version": 1,
"store_journal_mode": "wal",
"store_synchronous": "normal",
"store_status": "ok",
"store_note": "sample",
"iroh_enabled": true,
"endpoint_id": "endpoint:local",
"iroh_relay_mode": "default",
"iroh_local_discovery": true,
"iroh": "enabled",
"native_backends": []
}),
"NodeIdResponse" => json!({
"agent_id": "agent:local",
"node_id": "node:local",
"endpoint_id": "endpoint:local"
}),
"KeychainStatusResponse" => json!({
"initialized": true,
"admin_keys": 1,
"signatures": 1,
"verified_signatures": 1,
"failed_signatures": 0,
"users": 1,
"devices": 1,
"nodes": 1
}),
"KeychainFetchImportReport" => json!({
"ops_imported": 1,
"signatures_imported": 1,
"invalid_ops_rejected": 0
}),
"ResourceDescriptor" => json!({
"id": "resource:sample",
"kind": "kv",
"name": "sample",
"authority": {"kind": "local"},
"local_role": "owner",
"replication": "local-only",
"retention": "keep",
"status": "active"
}),
"PeerCard" => peer_card_json(),
"DiscoveredPeer" => json!({
"card": peer_card_json(),
"discovered_at": 1,
"source": "manual",
"trust_state": "candidate-only"
}),
"OverlayPlan" => json!({
"name": "home",
"resource": "resource:overlay:home",
"cidr": "172.22.0.0/24",
"alpn": "/geth/overlay/1",
"capabilities": [],
"discovery": "manual",
"runtime": "planned",
"security": [],
"implementation_notes": []
}),
"OverlayJoinPlan" => json!({
"plan": sample_for_type("OverlayPlan"),
"network": sample_for_type("OverlayNetworkStatus"),
"enabled": true,
"note": "sample"
}),
"OverlayNetworkStatus" => json!({
"name": "home",
"resource": "resource:overlay:home",
"cidr": "172.22.0.0/24",
"state": "joined",
"virtual_ip": "172.22.0.2",
"peers": [],
"note": "sample"
}),
"OverlayInterfacePlan" => json!({
"name": "home",
"platform": "linux",
"interface_name": "geth-home",
"cidr": "172.22.0.0/24",
"virtual_ip": "172.22.0.2",
"requires_privileges": true,
"commands": [],
"notes": []
}),
"OverlayRuntimeStatus" => json!({
"name": "home",
"interface_name": "geth-home",
"virtual_ip": "172.22.0.2",
"cidr": "172.22.0.0/24",
"mtu": 1280,
"started_at_ms": 1,
"packets_from_tun": 0,
"packets_to_tun": 0,
"packets_to_peers": 0,
"last_error": null,
"note": "sample"
}),
"OverlayPacket" => json!({
"id": "overlay-packet:sample",
"network": "home",
"source_node": "node:local",
"destination_node": "node:peer",
"packet_base64": "AA==",
"size_bytes": 1,
"received_at_ms": 1,
"note": "sample"
}),
"FileRoot" => json!({
"id": "file-root:sample",
"resource": "resource:cas-root:sample",
"name": "sample",
"path": "/tmp/sample",
"latest_tree": null,
"updated_at_ms": 1
}),
"FileRootScan" => json!({
"root": sample_for_type("FileRoot"),
"tree": {
"hash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"size_bytes": 1
},
"changes": [],
"note": "sample"
}),
"FileConflict" => json!({
"id": "file-conflict:sample",
"root": "sample",
"resource": "resource:cas-root:sample",
"path": "file.txt",
"kind": "concurrent-edit",
"status": "open",
"base_tree": null,
"local_tree": null,
"remote_tree": null,
"detail": "sample",
"resolution": null,
"resolution_note": null,
"created_at_ms": 1,
"resolved_at_ms": null
}),
"KeychainOp" => json!({
"id": "auth-op:sample",
"created_at": 1,
"kind": {"kind": "keychain-init"}
}),
"KeychainOpSignature" => json!({
"op_id": "auth-op:sample",
"signer": "key:sample",
"signer_public_key": "public",
"namespace": "geth.keychain.v1@geth.local",
"signature": [1],
"created_at": 1
}),
"KeychainAllowedSigner" => json!({
"key": "key:sample",
"principal": "admin",
"public_key": "public",
"valid_after_ms": null,
"valid_before_ms": null
}),
"KeychainSigchainReport" => json!({
"ops": 1,
"signatures": 1,
"accepted_ops": 1,
"rejected_ops": 0,
"active_admin_keys": 1,
"accepted_head": null,
"note": "sample"
}),
"KeychainSigchainEntry" => json!({
"op": sample_for_type("KeychainOp"),
"signatures": []
}),
"KeychainCheckpoint" => json!({
"version": 1,
"profile": {
"keychain_signature_namespace": "geth.keychain.v1@geth.local",
"node_enrollment_request_namespace": "geth.node-enrollment-request.v1@geth.local",
"default_admin_principal": "geth-admin"
},
"base_url": "https://example.invalid",
"head": null,
"ops": 1,
"signatures": 1,
"sigchain_bytes": 1,
"sigchain_hash": "hash",
"allowed_signers_hash": "hash",
"reduced_view_hash": "hash",
"generated_at": 1
}),
"AuthExplanation" => json!({
"subject": "node:peer",
"resource": "resource:sample",
"capability": "sample.read",
"allowed": true,
"reason": "sample",
"evaluated_ops": 1,
"diagnostics": []
}),
"AuthOp" => json!({
"id": "auth-op:sample",
"resource": "resource:sample",
"created_at": 1,
"kind": {"kind": "resource-create"}
}),
"AuthOpSignature" => json!({
"op_id": "auth-op:sample",
"signer": "key:sample",
"signer_public_key": "public",
"namespace": "geth.auth.v1@geth.local",
"signature": [1],
"created_at": 1
}),
"NodeRecord" => json!({
"id": "node:peer",
"device": "device:peer",
"name": "peer",
"endpoints": []
}),
"NodeEnrollmentRequest" => json!({
"id": "auth-op:enroll",
"requester_node": "node:peer",
"requester_agent": "agent:peer",
"requester_agent_public_key": "public",
"requested_node_name": "peer",
"requested_capabilities": [],
"endpoint_id": null,
"reason": null,
"status": "pending",
"created_at": 1,
"provenance": null
}),
"ResourceMasterSecret" => json!({
"id": "secret:sample",
"resource": "resource:sample",
"epoch": 1,
"created_at": 1
}),
"BearerAccess" => json!({
"secret": "bearer:sample",
"token": null,
"token_hash": "hash",
"resource": "resource:sample",
"capabilities": ["sample.read"],
"expires_at": null,
"may_delegate": false
}),
"BearerChallenge" => json!({
"resource": "resource:sample",
"capabilities": ["sample.read"],
"nonce": "nonce",
"issued_at": 1
}),
"BearerProof" => json!({
"secret": "bearer:sample",
"resource": "resource:sample",
"capabilities": ["sample.read"],
"nonce": "nonce",
"response": "response"
}),
"SshCertRequest" => json!({
"id": "ssh-cert-request:sample",
"requester_node": "node:peer",
"public_key": "ssh-ed25519 AAAA sample",
"public_key_fingerprint": "SHA256:sample",
"cert_kind": "user",
"principals": [],
"requested_validity": null,
"renewal_of": null,
"reason": null,
"status": "pending",
"created_at": 1,
"provenance": null
}),
"SshCertApproval" => json!({
"request_id": "ssh-cert-request:sample",
"approved_by_node": "node:local",
"ca_key_path": "/tmp/ca",
"key_id": "geth",
"valid_for": "+1h",
"serial": null,
"output_path": null,
"signing_command": [],
"signed": false,
"certificate_id": null,
"note": "sample"
}),
"SshCertificateRecord" => json!({
"id": "ssh-cert:sample",
"request_id": "ssh-cert-request:sample",
"certificate": "cert",
"certificate_fingerprint": "SHA256:sample",
"imported_at": 1,
"provenance": null
}),
"SshRevocationEntry" => json!({
"id": "ssh-revocation:sample",
"kind": "public-key",
"target": "ssh-ed25519 AAAA sample",
"reason": null,
"created_at": 1,
"published": false,
"provenance": null
}),
"DbResource" => json!({
"id": "db:sample",
"resource": "resource:db:sample",
"name": "sample",
"path": "/tmp/sample.sqlite",
"path_exists": true,
"size_bytes": 1,
"schema_metadata": "schema",
"crsqlite_changes": {
"available": false,
"change_count": null,
"max_db_version": null,
"columns": [],
"error": null
},
"sync_status": "local"
}),
"CrSqliteChangeBatch" => json!({
"schema_metadata": "schema",
"max_db_version": null,
"changes": []
}),
"KvResource" => json!({
"id": "kv:sample",
"resource": "resource:kv:sample",
"name": "sample",
"sync_status": "local"
}),
"KvEntry" => json!({
"store": "kv:sample",
"key": "key",
"value": "value"
}),
"DocumentResource" => json!({
"id": "document:sample",
"resource": "resource:document:sample",
"name": "sample",
"sync_status": "local",
"state_bytes": 2
}),
"DocumentState" => json!({
"document": sample_for_type("DocumentResource"),
"state_json": "{}",
"updated_at": 1
}),
"PubsubMessage" => json!({
"topic": "topic:sample",
"message": "sample",
"published_at": 1
}),
"PipeListener" => json!({
"id": "pipe:sample",
"name": "sample",
"listened_at": 1,
"note": "sample"
}),
"PipeConnection" => json!({
"target": "sample",
"connected_at": 1,
"local_listener_found": true,
"note": "sample"
}),
"PipeMessage" => json!({
"pipe": "sample",
"data_base64": "AA==",
"received_at": 1,
"source_node": null,
"note": "sample"
}),
"SshProxyConnection" => json!({
"target_node": "node:peer",
"connected_at": 1,
"local_sshd_target": "127.0.0.1:22",
"admin_shell_available": false,
"note": "sample"
}),
other => panic!("missing protocol sample for type {other}"),
}
}
fn compact_type(ty: &str) -> String {
ty.chars().filter(|ch| !ch.is_whitespace()).collect()
}
fn peer_card_json() -> Value {
json!({
"node_id": "node:peer",
"agent_id": "agent:peer",
"endpoints": [],
"issued_at": 1,
"signature": {
"namespace": "geth.peer-card.v1@geth.local",
"signer": "agent:peer",
"public_key": "public",
"signature": "signature"
}
})
}
#[test] #[test]
fn control_request_response_serialization_roundtrip() { fn control_request_response_serialization_roundtrip() {

View file

@ -101,10 +101,10 @@ and downstream projects.
failures. failures.
- `[x]` Fixture updates require intentional review. - `[x]` Fixture updates require intentional review.
- `[~]` Expand protocol roundtrip tests. - `[x]` Expand protocol roundtrip tests.
Acceptance criteria: Acceptance criteria:
- `[ ]` Every `ControlRequest` and `ControlResponse` variant roundtrips. - `[x]` Every `ControlRequest` and `ControlResponse` variant roundtrips.
- `[ ]` Every `PeerControlRequest` and `PeerControlResponse` variant - `[x]` Every `PeerControlRequest` and `PeerControlResponse` variant
roundtrips. roundtrips.
- `[x]` Pipe and overlay wire protocol variants roundtrip. - `[x]` Pipe and overlay wire protocol variants roundtrip.
- `[x]` Unknown or malformed protocol inputs fail safely. - `[x]` Unknown or malformed protocol inputs fail safely.
@ -338,7 +338,7 @@ Goal: prove the system works as an actual base layer before broader use.
1. `[x]` Finish Phase 0. 1. `[x]` Finish Phase 0.
2. `[~]` Refactor `geth-node` into daemon subsystems. 2. `[~]` Refactor `geth-node` into daemon subsystems.
3. `[ ]` Add stable contract and golden JSON tests. 3. `[x]` Add stable contract and golden JSON tests.
4. `[ ]` Harden store migrations and backup. 4. `[ ]` Harden store migrations and backup.
5. `[ ]` Complete security-boundary test coverage. 5. `[ ]` Complete security-boundary test coverage.
6. `[ ]` Replace prototype private CAS cryptography. 6. `[ ]` Replace prototype private CAS cryptography.