use jmap_types::{Id, JmapUri, JmapUriError};
#[test]
fn object_uri_draft_example_round_trips() {
let uri: JmapUri = "jmap:calendarevent:A13824:E6B2".parse().unwrap();
assert_eq!(uri.data_type(), "calendarevent");
assert_eq!(uri.account_id().as_ref(), "A13824");
assert_eq!(uri.object_id().as_ref(), "E6B2");
assert_eq!(uri.to_string(), "jmap:calendarevent:A13824:E6B2");
}
#[test]
fn id_chars_include_hyphen_and_underscore() {
let uri: JmapUri = "jmap:calendarevent:A1-_:ev-7_x".parse().unwrap();
assert_eq!(uri.account_id().as_ref(), "A1-_");
assert_eq!(uri.object_id().as_ref(), "ev-7_x");
}
#[test]
fn principal_form_parses() {
let uri: JmapUri = "jmap:principal:acct1:p9".parse().unwrap();
assert_eq!(uri.data_type(), "principal");
assert_eq!(uri.object_id().as_ref(), "p9");
}
#[test]
fn task_form_carries_one_task_id() {
let uri: JmapUri = "jmap:task:acct1:task3".parse().unwrap();
assert_eq!(uri.data_type(), "task");
assert_eq!(uri.account_id().as_ref(), "acct1");
assert_eq!(uri.object_id().as_ref(), "task3");
assert_eq!(uri.to_string(), "jmap:task:acct1:task3");
}
#[test]
fn segments_after_object_id_rejected() {
for input in ["jmap:task:acct1:list2:task3", "jmap:calendarevent:a1:e1:"] {
assert!(
matches!(
input.parse::<JmapUri>(),
Err(JmapUriError::InvalidObjectId(_))
),
"{input}"
);
}
}
#[test]
fn malformed_uris_rejected() {
let cases: &[(&str, JmapUriError)] = &[
("https://example.com/e1", JmapUriError::NotJmapScheme),
("webcal:calendarevent:a1:e1", JmapUriError::NotJmapScheme),
("calendarevent:a1:e1", JmapUriError::NotJmapScheme),
("jmap", JmapUriError::InvalidDataType),
("jmap::a1:e1", JmapUriError::InvalidDataType),
("jmap:CalendarEvent:a1:e1", JmapUriError::InvalidDataType),
("jmap:calendar-event:a1:e1", JmapUriError::InvalidDataType),
("jmap://host/x", JmapUriError::InvalidDataType),
("jmap:calendarevent", JmapUriError::InvalidAccountId(None)),
("jmap:calendarevent:a1", JmapUriError::MissingObjectId),
];
for (input, want) in cases {
assert_eq!(&input.parse::<JmapUri>().unwrap_err(), want, "{input}");
}
for input in [
"jmap:calendarevent::e1",
"jmap:calendarevent:a/1:e1",
"jmap:calendarevent:a1:",
"jmap:calendarevent:a1:e 1",
"jmap:task:a1::t1",
] {
let err = input.parse::<JmapUri>().unwrap_err();
assert!(
matches!(
err,
JmapUriError::InvalidAccountId(Some(_)) | JmapUriError::InvalidObjectId(_)
),
"{input}: {err:?}"
);
}
}
#[test]
fn scheme_is_case_insensitive_and_rendered_lowercase() {
let uri: JmapUri = "JMAP:calendarevent:a1:e1".parse().unwrap();
assert_eq!(uri.to_string(), "jmap:calendarevent:a1:e1");
let upper: JmapUri = "jmap:calendarevent:A1:E1".parse().unwrap();
assert_ne!(uri, upper);
}
#[test]
fn builder_validates_parts() {
let uri = JmapUri::new("calendarevent", Id::from("a1"), Id::from("e1")).unwrap();
assert_eq!(uri.to_string(), "jmap:calendarevent:a1:e1");
assert!(matches!(
JmapUri::new("task", Id::from("a:1"), Id::from("t1")),
Err(JmapUriError::InvalidAccountId(Some(_)))
));
assert!(matches!(
JmapUri::new("task", Id::from("a1"), Id::from("l1:t1")),
Err(JmapUriError::InvalidObjectId(_))
));
assert_eq!(
JmapUri::new("Task", Id::from("a1"), Id::from("t1")),
Err(JmapUriError::InvalidDataType)
);
}