buntrail-protocol 0.2.0

Versioned framed transport contracts and validation.
Documentation
use buntrail_core::contracts as c;
use buntrail_protocol::services::*;
#[test]
fn paths_and_evidence_round_trip_without_unicode_or_target_identity_loss() {
    let t = c::TargetIdentity {
        security_binding: b"verified".to_vec(),
        effective_user: b"user".to_vec(),
        filesystem_namespace: b"remote".to_vec(),
        container: None,
    };
    let p = c::TargetPath::posix(b"/repo/a-\xff\n".to_vec()).unwrap();
    assert_eq!(target_path(&path(&p).unwrap()).unwrap(), p);
    let file = c::FileRef {
        target: t.clone(),
        path: p,
        resolved_identity: Some(vec![0, 255]),
    };
    let dto = File::from_core(&file).unwrap();
    let bytes = serde_json::to_vec(&dto).unwrap();
    assert!(!bytes.contains(&255));
    assert_eq!(
        serde_json::from_slice::<File>(&bytes)
            .unwrap()
            .core(&t)
            .unwrap(),
        file
    );
    let operation = c::OperationReceipt {
        id: c::OperationID([8; 16]),
        state: c::OperationState::Unknown(c::ReconciliationEvidence {
            target: t.clone(),
            observed: c::ObservedState::File {
                file,
                version: None,
                digest: Some([9; 32]),
            },
            explanation: "inspect original".into(),
        }),
    };
    assert_eq!(
        Receipt::from_core(&operation).unwrap().core(&t).unwrap(),
        operation
    );
    assert!(digest(&"λ".repeat(32)).is_err());
}
#[test]
fn nullable_fields_are_required_and_nested_unknown_fields_rejected() {
    assert!(serde_json::from_str::<File>(r#"{"path_base64":"L3RtcA=="}"#).is_err());
    assert!(
        serde_json::from_str::<File>(
            r#"{"path_base64":"L3RtcA==","resolved_identity":null,"display_path":"/spoofed"}"#
        )
        .is_err()
    );
    assert!(serde_json::from_str::<Call>(r#"{"method":"git_mutate","worktree":{"path_base64":"L3RtcA==","common_directory_base64":""},"expected":{"head":{"kind":"unborn","name_base64":"bWFpbg=="},"index_digest":"00","worktree_digest":"00"},"command":{"kind":"checkout","reference":"bWFpbg=="},"coordinator_approved":true}"#).is_err());
}
#[test]
fn terminal_receipt_preserves_runtime_and_output_bytes_round_trip() {
    let target = c::TargetIdentity {
        security_binding: vec![1],
        effective_user: vec![2],
        filesystem_namespace: vec![3],
        container: None,
    };
    let receipt = c::OperationReceipt {
        id: c::OperationID([4; 16]),
        state: c::OperationState::Confirmed(c::MutationResult::TerminalOpened {
            session: c::TerminalSessionID([5; 16]),
            runtime: c::RuntimeGeneration {
                supervisor_boot: [6; 16],
                container_start: Some(vec![0, 255]),
            },
        }),
    };
    assert_eq!(
        Receipt::from_core(&receipt).unwrap().core(&target).unwrap(),
        receipt
    );
    let output = c::TerminalEvent::Output {
        sequence: 9,
        bytes: vec![0, 255, 27],
    };
    assert_eq!(
        buntrail_protocol::terminal::Event::from_core(output.clone())
            .core()
            .unwrap(),
        output
    );
    assert!(buntrail_protocol::terminal::size(0, 24).is_err());
}

#[test]
fn terminal_exit_code_nullable_field_is_required() {
    assert!(
        serde_json::from_str::<buntrail_protocol::terminal::Event>(r#"{"kind":"process_exit"}"#)
            .is_err()
    );
}

#[test]
fn owner_observation_golden_payloads_require_exact_typed_states() {
    let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("protocol/v1/golden");
    for state in ["active", "detached", "absent", "runtime_lost"] {
        let bytes = std::fs::read(root.join(format!("valid-lease-observed-{state}.bin"))).unwrap();
        let header = u32::from_be_bytes(bytes[..4].try_into().unwrap()) as usize;
        let reply: Reply = serde_json::from_slice(&bytes[8 + header..]).unwrap();
        assert!(matches!(reply, Reply::LeaseObserved { .. }));
    }
    let invalid: Vec<serde_json::Value> = serde_json::from_slice(
        &std::fs::read(root.join("lease-observation-invalid-payloads.json")).unwrap(),
    )
    .unwrap();
    for value in invalid {
        assert!(serde_json::from_value::<Reply>(value).is_err());
    }
}

#[test]
fn git_lookup_golden_is_read_only_and_requires_original_operation_and_nullable_receipt() {
    let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("protocol/v1/golden");
    let bytes = std::fs::read(root.join("valid-git-existing.bin")).unwrap();
    let length = u32::from_be_bytes(bytes[..4].try_into().unwrap()) as usize;
    let header: buntrail_protocol::types::Header =
        serde_json::from_slice(&bytes[8..8 + length]).unwrap();
    let buntrail_protocol::types::Message::ServiceCall { call } = &header.message else {
        panic!("lookup call");
    };
    assert!(matches!(call, Call::GitExisting { .. }));
    assert!(!call.mutating());
    assert!(!call.effectful());
    let mut missing = header.clone();
    missing.operation_id = None;
    assert!(missing.validate(0, header.context.as_ref()).is_err());
    let mut bypass = serde_json::to_value(call).unwrap();
    bypass["execution_expected"] = serde_json::json!({});
    assert!(serde_json::from_value::<Call>(bypass).is_err());
    assert!(serde_json::from_str::<Reply>(r#"{"kind":"git_existing"}"#).is_err());
    assert!(matches!(
        serde_json::from_str::<Reply>(r#"{"kind":"git_existing","receipt":null}"#).unwrap(),
        Reply::GitExisting { receipt: None }
    ));
}

#[test]
fn fixed_git_inspection_queries_cannot_encode_mutating_flags() {
    for query in [
        c::GitRead::StatusShort,
        c::GitRead::Refs,
        c::GitRead::Graph,
        c::GitRead::Stashes,
        c::GitRead::Upstream,
    ] {
        let json = serde_json::to_value(query).unwrap();
        assert_eq!(serde_json::from_value::<c::GitRead>(json).unwrap(), query);
    }
    assert!(
        serde_json::from_value::<c::GitRead>(serde_json::json!({"args":["clean","-fd"]})).is_err()
    );
    assert!(c::GitRead::from_args(&["checkout", "main"]).is_none());
    let schema: serde_json::Value =
        serde_json::from_slice(include_bytes!("../protocol/v1/services.schema.json")).unwrap();
    assert!(
        schema["$defs"]["Call"]["oneOf"]
            .as_array()
            .unwrap()
            .iter()
            .any(|v| v["properties"]["method"]["const"] == "git_read")
    );
}

#[test]
fn repository_commands_preserve_byte_names() {
    let raw = serde_json::json!({"kind":"repository","action":{"CreateBranch":{"name":[255,120],"source_ref":[114,101,102,115,47,104,101,97,100,115,47,120],"start_oid":"a".repeat(40)}}});
    assert!(
        serde_json::from_value::<GitCommand>(raw).is_ok(),
        "Repository command must round-trip byte identities"
    );
}

#[test]
fn repository_outcomes_preserve_legacy_receipts() {
    let target = c::TargetIdentity {
        security_binding: vec![1],
        effective_user: vec![2],
        filesystem_namespace: vec![3],
        container: None,
    };
    let worktree = c::WorktreeIdentity {
        target: target.clone(),
        canonical_path: c::TargetPath::PosixBytes(b"/repo".to_vec()),
        common_git_directory_identity: vec![4],
    };
    let state = c::GitExpectedState {
        head: c::GitHead::Unborn(b"main".to_vec()),
        index_fingerprint: [0; 32],
        worktree_fingerprint: [0; 32],
    };
    for outcome in [
        None,
        Some(Box::new(c::RepositoryOutcome::RefsUpdated)),
        Some(Box::new(c::RepositoryOutcome::MergePending {
            merge: c::GitMergeState {
                heads: vec!["a".repeat(40)],
                session: None,
                message: "retained message".into(),
                error: Some("hook failed".into()),
                conflicts: vec![],
            },
        })),
    ] {
        let legacy = outcome.is_none();
        let receipt = c::OperationReceipt {
            id: c::OperationID([5; 16]),
            state: c::OperationState::Confirmed(c::MutationResult::GitChanged {
                worktree: worktree.clone(),
                state: state.clone(),
                outcome,
            }),
        };
        let wire = Receipt::from_core(&receipt).unwrap();
        let json = serde_json::to_value(&wire).unwrap();
        assert_eq!(json.to_string().contains("\"outcome\""), !legacy);
        assert_eq!(
            serde_json::from_value::<Receipt>(json)
                .unwrap()
                .core(&target)
                .unwrap(),
            receipt
        );
        let json = serde_json::to_value(&receipt).unwrap();
        assert_eq!(json.to_string().contains("\"outcome\""), !legacy);
        assert_eq!(
            serde_json::from_value::<c::OperationReceipt>(json).unwrap(),
            receipt
        );
    }
}