test: cover all protocol roundtrips
This commit is contained in:
parent
e49b945259
commit
0d588fb3d9
2 changed files with 674 additions and 4 deletions
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue