im-core 0.1.0

Rust IM SDK for Awiki clients built on Agent Network Protocol (ANP)
Documentation
use im_core::compat;
use im_core::prelude::*;
use serde_json::json;

#[test]
fn wire_content_type_for_message_kind_matches_p1_contract() {
    assert_eq!(
        compat::wire::content_type_for_message_kind(MessageKind::Text, None),
        "text/plain"
    );
    assert_eq!(
        compat::wire::content_type_for_message_kind(MessageKind::Markdown, None),
        "text/markdown"
    );
    assert_eq!(
        compat::wire::content_type_for_message_kind(MessageKind::Text, Some("event")),
        "application/json"
    );
    assert_eq!(
        compat::wire::content_type_for_message_kind(MessageKind::Text, Some("attachment_manifest")),
        "application/anp-attachment-manifest+json"
    );
}

#[test]
fn wire_operation_id_is_lower_hex_without_prefix() {
    let first = compat::wire::generate_operation_id();
    let second = compat::wire::generate_operation_id();

    assert_eq!(first.len(), 16);
    assert_eq!(second.len(), 16);
    assert_ne!(first, second);
    assert!(first.chars().all(|ch| ch.is_ascii_hexdigit()));
    assert!(first.chars().all(|ch| !ch.is_ascii_uppercase()));
}

#[test]
fn wire_now_rfc3339_uses_second_precision_utc_shape() {
    let value = compat::wire::now_rfc3339();
    let bytes = value.as_bytes();

    assert_eq!(bytes.len(), 20);
    assert_eq!(bytes[4], b'-');
    assert_eq!(bytes[7], b'-');
    assert_eq!(bytes[10], b'T');
    assert_eq!(bytes[13], b':');
    assert_eq!(bytes[16], b':');
    assert_eq!(bytes[19], b'Z');
    assert!(bytes
        .iter()
        .enumerate()
        .filter(|(index, _)| !matches!(index, 4 | 7 | 10 | 13 | 16 | 19))
        .all(|(_, byte)| byte.is_ascii_digit()));
}

#[test]
fn wire_direct_text_payload_matches_go_contract() {
    let payload = compat::wire::build_direct_text_payload(
        "did:wba:awiki.ai:user:alice:e1",
        "did:wba:awiki.ai:user:bob:e1",
        "hello",
        "text/plain",
    )
    .expect("direct payload");

    assert_eq!(payload.method, "direct.send");
    assert_eq!(payload.meta["profile"], "anp.direct.base.v1");
    assert_eq!(payload.meta["security_profile"], "transport-protected");
    assert_eq!(
        payload.meta["target"],
        json!({
            "kind": "agent",
            "did": "did:wba:awiki.ai:user:bob:e1",
        })
    );
    assert_eq!(payload.body, json!({ "text": "hello" }));
    assert_has_generated_meta(&payload.meta);
    assert!(payload.meta["message_id"]
        .as_str()
        .expect("message id")
        .starts_with("msg-"));
}

#[test]
fn wire_inbox_history_and_mark_read_params_match_go_contracts() {
    let identity = compat::wire::WireIdentity {
        did: "did:wba:awiki.ai:user:alice:e1_alice".to_string(),
    };

    let inbox = compat::wire::build_inbox_rpc_params(
        &identity,
        compat::wire::InboxWireRequest {
            limit: 0,
            auth: None,
        },
    );
    assert_eq!(inbox["meta"]["profile"], "anp.inbox.local.v1");
    assert_eq!(inbox["meta"]["security_profile"], "transport-protected");
    assert_eq!(inbox["meta"]["sender_did"], identity.did);
    assert_has_generated_meta(&inbox["meta"]);
    assert_eq!(inbox["body"]["user_did"], identity.did);
    assert_eq!(inbox["body"]["limit"], 20);

    let history = compat::wire::build_history_rpc_params(
        &identity,
        compat::wire::HistoryWireRequest {
            peer_did: "did:wba:awiki.ai:user:bob:e1_bob".to_string(),
            limit: 0,
            cursor: Some("42".to_string()),
            skip: 3,
            auth: None,
        },
    )
    .expect("history params");
    assert_eq!(history["meta"]["profile"], "anp.direct.local.v1");
    assert_eq!(
        history["body"]["peer_did"],
        "did:wba:awiki.ai:user:bob:e1_bob"
    );
    assert_eq!(history["body"]["limit"], 50);
    assert_eq!(history["body"]["since_seq"], "42");
    assert_eq!(history["body"]["skip"], 3);

    assert!(compat::wire::build_mark_read_rpc_params(
        &identity,
        compat::wire::MarkReadWireRequest {
            message_ids: Vec::new(),
        }
    )
    .is_err());
    let mark_read = compat::wire::build_mark_read_rpc_params(
        &identity,
        compat::wire::MarkReadWireRequest {
            message_ids: vec!["msg-1".to_string(), "msg-2".to_string()],
        },
    )
    .expect("mark-read params");
    assert_eq!(mark_read["meta"]["profile"], "anp.inbox.local.v1");
    assert_eq!(mark_read["body"]["message_ids"], json!(["msg-1", "msg-2"]));
}

#[test]
fn wire_delegated_inbox_history_params_include_inbox_auth_fields() {
    let identity = compat::wire::WireIdentity {
        did: "did:wba:awiki.ai:agent:daemon:e1_daemon".to_string(),
    };
    let inbox_owner_did = "did:wba:awiki.ai:user:alice:e1_alice".to_string();
    let inbox_auth_verification_method =
        "did:wba:awiki.ai:user:alice:e1_alice#daemon-key-1".to_string();

    let inbox = compat::wire::build_inbox_rpc_params(
        &identity,
        compat::wire::InboxWireRequest {
            limit: 7,
            auth: Some(compat::wire::InboxWireAuth {
                inbox_owner_did: inbox_owner_did.clone(),
                inbox_auth_verification_method: inbox_auth_verification_method.clone(),
            }),
        },
    );
    assert_eq!(inbox["meta"]["sender_did"], inbox_owner_did);
    assert_eq!(
        inbox["meta"]["target"],
        json!({"kind": "service", "did": "did:awiki:message-service"})
    );
    assert_eq!(inbox["body"]["user_did"], inbox_owner_did);
    assert_eq!(inbox["body"]["inbox_owner_did"], inbox_owner_did);
    assert_eq!(
        inbox["body"]["inbox_auth_verification_method"],
        inbox_auth_verification_method
    );

    let history = compat::wire::build_history_rpc_params(
        &identity,
        compat::wire::HistoryWireRequest {
            peer_did: "did:wba:awiki.ai:user:bob:e1_bob".to_string(),
            limit: 3,
            cursor: None,
            skip: 0,
            auth: Some(compat::wire::HistoryWireAuth {
                inbox_owner_did: inbox_owner_did.clone(),
                inbox_auth_verification_method: inbox_auth_verification_method.clone(),
            }),
        },
    )
    .expect("delegated history params");
    assert_eq!(history["meta"]["sender_did"], inbox_owner_did);
    assert_eq!(
        history["meta"]["target"],
        json!({"kind": "service", "did": "did:awiki:message-service"})
    );
    assert_eq!(history["body"]["user_did"], inbox_owner_did);
    assert_eq!(history["body"]["inbox_owner_did"], inbox_owner_did);
    assert_eq!(
        history["body"]["inbox_auth_verification_method"],
        inbox_auth_verification_method
    );
}

fn assert_has_generated_meta(meta: &serde_json::Value) {
    assert_eq!(meta["anp_version"], "1.0");
    assert!(meta["operation_id"]
        .as_str()
        .expect("operation id")
        .starts_with("op-"));
    assert_eq!(meta["created_at"].as_str().expect("created at").len(), 20);
}