agecli-skill-protocol 0.1.0

Wire protocol contract for agecli skill ↔ host UDS communication (binary framing + payload types)
Documentation
use super::*;

#[test]
fn message_type_roundtrip() {
    assert_eq!(
        SkillMessageType::from_u32(1),
        Some(SkillMessageType::Execute)
    );
    assert_eq!(SkillMessageType::from_u32(128), Some(SkillMessageType::Ack));
    assert_eq!(SkillMessageType::from_u32(999), None);
}

#[test]
fn execute_payload_serde() {
    let payload = ExecutePayload {
        execution_id: "exec-1".into(),
        command_name: "test:exec".into(),
        command: Some("echo hello".into()),
        args: None,
        working_directory: None,
        environment: None,
        timeout_ms: 30000,
    };
    let json = serde_json::to_string(&payload).unwrap();
    let parsed: ExecutePayload = serde_json::from_str(&json).unwrap();
    assert_eq!(parsed.execution_id, "exec-1");
    assert_eq!(parsed.command_name, "test:exec");
    assert_eq!(parsed.command.as_deref(), Some("echo hello"));
}

#[test]
fn completed_payload_serde() {
    let payload = CompletedPayload {
        id: "exec-1".into(),
        exit_code: 0,
        status: "success".into(),
        error: None,
        finished_at_unix: 1700000000,
    };
    let json = serde_json::to_string(&payload).unwrap();
    let parsed: CompletedPayload = serde_json::from_str(&json).unwrap();
    assert_eq!(parsed.exit_code, 0);
    assert_eq!(parsed.status, "success");
}

#[test]
fn error_payload_serde() {
    let payload = ErrorPayload {
        id: Some("exec-err".into()),
        code: "SKILL_EXEC_FAILED".into(),
        message: "process killed".into(),
    };
    let json = serde_json::to_string(&payload).unwrap();
    let parsed: ErrorPayload = serde_json::from_str(&json).unwrap();
    assert_eq!(parsed.code, "SKILL_EXEC_FAILED");
    assert_eq!(parsed.id.as_deref(), Some("exec-err"));
}

#[test]
fn session_started_payload_serde() {
    let payload = SessionStartedPayload {
        id: "sess-1".into(),
        status: "active".into(),
    };
    let json = serde_json::to_string(&payload).unwrap();
    let parsed: SessionStartedPayload = serde_json::from_str(&json).unwrap();
    assert_eq!(parsed.id, "sess-1");
    assert_eq!(parsed.status, "active");
}

#[test]
fn stdout_payload_serde_with_bytes() {
    let payload = DataChunkPayload {
        id: "exec-1".into(),
        seq: 1,
        data: b"hello world".to_vec(),
    };
    let json = serde_json::to_string(&payload).unwrap();
    let parsed: DataChunkPayload = serde_json::from_str(&json).unwrap();
    assert_eq!(parsed.data, b"hello world");
    assert_eq!(parsed.seq, 1);
}

#[test]
fn all_message_types_have_values() {
    // Server → Skill
    assert_eq!(
        SkillMessageType::from_u32(1),
        Some(SkillMessageType::Execute)
    );
    assert_eq!(
        SkillMessageType::from_u32(2),
        Some(SkillMessageType::Cancel)
    );
    assert_eq!(
        SkillMessageType::from_u32(3),
        Some(SkillMessageType::StdinData)
    );
    assert_eq!(
        SkillMessageType::from_u32(4),
        Some(SkillMessageType::Resize)
    );
    assert_eq!(
        SkillMessageType::from_u32(5),
        Some(SkillMessageType::Signal)
    );
    assert_eq!(
        SkillMessageType::from_u32(6),
        Some(SkillMessageType::StartSession)
    );
    assert_eq!(
        SkillMessageType::from_u32(7),
        Some(SkillMessageType::Shutdown)
    );
    // Skill → Server
    assert_eq!(SkillMessageType::from_u32(128), Some(SkillMessageType::Ack));
    assert_eq!(
        SkillMessageType::from_u32(129),
        Some(SkillMessageType::StdoutChunk)
    );
    assert_eq!(
        SkillMessageType::from_u32(130),
        Some(SkillMessageType::StderrChunk)
    );
    assert_eq!(
        SkillMessageType::from_u32(131),
        Some(SkillMessageType::Progress)
    );
    assert_eq!(
        SkillMessageType::from_u32(132),
        Some(SkillMessageType::Completed)
    );
    assert_eq!(
        SkillMessageType::from_u32(133),
        Some(SkillMessageType::Error)
    );
    assert_eq!(
        SkillMessageType::from_u32(134),
        Some(SkillMessageType::SessionStarted)
    );
    // Unknown
    assert_eq!(SkillMessageType::from_u32(999), None);
}