use cose2::{iana, tag, CoseMap, Error, Key, KeySet, Label, Value};
#[test]
fn label_constructors_and_accessors() {
assert_eq!(Label::from(1i64), Label::Int(1));
assert_eq!(Label::from(2i32), Label::Int(2));
assert_eq!(Label::from("alg"), Label::Text("alg".into()));
assert_eq!(Label::from(String::from("kid")), Label::Text("kid".into()));
assert_eq!(Label::Int(5).as_int(), Some(5));
assert_eq!(Label::Int(5).as_text(), None);
assert_eq!(Label::Text("x".into()).as_text(), Some("x"));
assert_eq!(Label::Text("x".into()).as_int(), None);
assert_eq!(Label::Int(-7).to_string(), "-7");
assert_eq!(Label::Text("hi".into()).to_string(), "hi");
assert!(Label::Int(1) < Label::Text("a".into()));
}
#[test]
fn label_cbor_round_trip_int_and_text() {
for label in [Label::Int(1), Label::Int(-7), Label::Text("alg".into())] {
let bytes = cbor2::to_vec(&label).unwrap();
let back: Label = cbor2::from_slice(&bytes).unwrap();
assert_eq!(label, back);
}
}
#[test]
fn label_deserializes_all_integer_widths() {
let l: Label = cbor2::from_slice(&cbor2::to_vec(&10u64).unwrap()).unwrap();
assert_eq!(l, Label::Int(10));
let l: Label = cbor2::from_slice(&cbor2::to_vec(&-10i64).unwrap()).unwrap();
assert_eq!(l, Label::Int(-10));
let err = cbor2::from_slice::<Label>(&cbor2::to_vec(&u64::MAX).unwrap());
assert!(err.is_err());
let big_neg = cbor2::to_vec(&(i128::from(i64::MIN) - 1)).unwrap();
assert!(cbor2::from_slice::<Label>(&big_neg).is_err());
let big_pos = cbor2::to_vec(&(u128::from(u64::MAX) + 1)).unwrap();
assert!(cbor2::from_slice::<Label>(&big_pos).is_err());
}
#[test]
fn label_rejects_non_label_types() {
assert!(cbor2::from_slice::<Label>(&cbor2::to_vec(&true).unwrap()).is_err());
}
#[test]
fn cosemap_basic_operations() {
let mut m = CoseMap::new();
assert!(m.is_empty());
assert_eq!(m.len(), 0);
assert_eq!(m.insert(1, 10i64), None);
assert_eq!(m.insert("name", "v"), None);
assert_eq!(m.insert(1, 11i64), Some(Value::from(10i64)));
assert!(m.contains_key(1));
assert_eq!(m.len(), 2);
assert_eq!(m.get(1), Some(&Value::from(11i64)));
assert_eq!(m.remove(1), Some(Value::from(11i64)));
assert_eq!(m.remove(1), None);
assert!(!m.contains_key(1));
let mut m = CoseMap::new();
m.insert(1, 1i64);
m.insert(2, 2i64);
assert_eq!(m.iter().count(), 2);
assert_eq!((&m).into_iter().count(), 2);
assert_eq!(m.clone().into_iter().count(), 2);
let collected: CoseMap = m.clone().into_iter().collect();
assert_eq!(collected, m);
}
#[test]
fn cosemap_typed_getters() {
let mut m = CoseMap::new();
m.insert(1, 42i64);
m.insert(2, vec![1u8, 2, 3]);
m.insert(3, "text");
m.insert(4, true);
m.insert(5, vec![Value::from(1i64), Value::from(2i64)]);
assert_eq!(m.get_i64(1).unwrap(), Some(42));
assert_eq!(m.get_bytes(2).unwrap(), Some(&[1u8, 2, 3][..]));
assert_eq!(m.get_text(3).unwrap(), Some("text"));
assert_eq!(m.get_bool(4).unwrap(), Some(true));
assert_eq!(m.get_array(5).unwrap().unwrap().len(), 2);
assert_eq!(m.get_i64(99).unwrap(), None);
assert_eq!(m.get_bytes(99).unwrap(), None);
assert_eq!(m.get_text(99).unwrap(), None);
assert_eq!(m.get_bool(99).unwrap(), None);
assert_eq!(m.get_array(99).unwrap(), None);
}
#[test]
fn cosemap_type_mismatches_error() {
let mut m = CoseMap::new();
m.insert(1, "not an int");
m.insert(2, 1i64);
assert!(matches!(m.get_i64(1), Err(Error::UnexpectedType(_))));
assert!(matches!(m.get_bytes(2), Err(Error::UnexpectedType(_))));
assert!(matches!(m.get_text(2), Err(Error::UnexpectedType(_))));
assert!(matches!(m.get_bool(2), Err(Error::UnexpectedType(_))));
assert!(matches!(m.get_array(2), Err(Error::UnexpectedType(_))));
let mut big = CoseMap::new();
big.insert(1, u64::MAX);
assert!(matches!(big.get_i64(1), Err(Error::UnexpectedType(_))));
}
#[test]
fn cosemap_cbor_round_trip_and_canonical_order() {
let mut m = CoseMap::new();
m.insert(2, "b");
m.insert(1, "a");
m.insert(-1, "neg");
let bytes = m.to_vec().unwrap();
let back = CoseMap::from_slice(&bytes).unwrap();
assert_eq!(back, m);
assert_eq!(bytes[0], 0xa3);
}
#[test]
fn cosemap_rejects_duplicate_keys() {
let dup = hex::decode("a2010102").unwrap_or_default();
let dup = if dup.is_empty() {
vec![0xa2, 0x01, 0x01, 0x01, 0x02]
} else {
dup
};
assert!(CoseMap::from_slice(&dup).is_err());
}
#[test]
fn cosemap_default_impl() {
let m: CoseMap = Default::default();
assert!(m.is_empty());
}
#[test]
fn key_accessors_round_trip() {
let mut key = Key::new();
key.set_kty(iana::KeyTypeEC2)
.set_kid(b"my-kid".to_vec())
.set_alg(iana::AlgorithmES256)
.set_ops(&[iana::KeyOperationSign, iana::KeyOperationVerify]);
key.insert(iana::KeyParameterBaseIV, vec![9u8, 8, 7]);
assert_eq!(key.kty().unwrap(), Some(iana::KeyTypeEC2));
assert_eq!(key.kid().unwrap(), Some(&b"my-kid"[..]));
assert_eq!(key.alg().unwrap(), Some(iana::AlgorithmES256));
assert_eq!(key.ops().unwrap(), Some(vec![1, 2]));
assert_eq!(key.base_iv().unwrap(), Some(&[9u8, 8, 7][..]));
assert!(key.contains_key(iana::KeyParameterKty));
let bytes = key.to_vec().unwrap();
let back = Key::from_slice(&bytes).unwrap();
assert_eq!(back, key);
}
#[test]
fn key_ops_errors_on_non_integer_array() {
let mut key = Key::new();
key.insert(iana::KeyParameterKeyOps, vec![Value::from("sign")]);
assert!(key.ops().is_err());
let empty = Key::new();
assert_eq!(empty.ops().unwrap(), None);
assert_eq!(empty.kty().unwrap(), None);
}
#[test]
fn key_default_and_deref_mut() {
let mut key = Key::default();
key.insert(1, 2i64); assert_eq!(key.get_i64(1).unwrap(), Some(2));
}
#[test]
fn keyset_lookup_and_round_trip() {
let mut k1 = Key::new();
k1.set_kty(iana::KeyTypeOKP).set_kid(b"one".to_vec());
let mut k2 = Key::new();
k2.set_kty(iana::KeyTypeOKP).set_kid(b"two".to_vec());
let mut set = KeySet::new();
set.push(k1.clone()); set.0.push(k2.clone());
assert_eq!(set.len(), 2);
assert_eq!(set.lookup(b"two"), Some(&k2));
assert!(set.lookup(b"missing").is_none());
let bytes = set.to_vec().unwrap();
let back = KeySet::from_slice(&bytes).unwrap();
assert_eq!(back, set);
let empty = KeySet::default();
assert!(empty.is_empty());
let mut s2 = KeySet::new();
s2.push(Key::new());
assert!(s2.lookup(b"x").is_none());
}
#[test]
fn error_display_and_constructors() {
assert_eq!(
format!("{}", Error::Cbor("x".into())),
"cose: cbor error: x"
);
assert_eq!(
format!("{}", Error::UnexpectedType("y".into())),
"cose: unexpected type: y"
);
assert_eq!(
format!("{}", Error::Verify("z".into())),
"cose: verification failed: z"
);
assert_eq!(format!("{}", Error::Custom("w".into())), "cose: w");
assert_eq!(Error::custom("a"), Error::Custom("a".into()));
assert_eq!(Error::verify("b"), Error::Verify("b".into()));
let e: &dyn std::error::Error = &Error::Custom("e".into());
assert!(e.to_string().contains("e"));
}
#[test]
fn error_from_cbor_errors() {
use serde::{de::Error as _, ser::Error as _};
let de: Error = cbor2::de::Error::custom("de-boom").into();
assert!(matches!(de, Error::Cbor(_)));
let ser: Error = cbor2::ser::Error::custom("ser-boom").into();
assert!(matches!(ser, Error::Cbor(_)));
assert!(CoseMap::from_slice(&[0xff, 0xff]).is_err());
}
#[test]
fn tag_with_and_skip() {
let data = [0x84u8, 1, 2, 3];
let tagged = tag::with_tag(tag::SIGN1_PREFIX, &data);
assert_eq!(tagged[0], 0xd2);
assert_eq!(tag::skip_tag(tag::SIGN1_PREFIX, &tagged), &data);
assert_eq!(tag::skip_tag(tag::SIGN1_PREFIX, &data), &data);
}
#[test]
fn tag_remove_cbor_tag_variants() {
let body = [0x80u8];
for prefix in [tag::SIGN1_PREFIX, tag::MAC0_PREFIX, tag::ENCRYPT0_PREFIX] {
let tagged = tag::with_tag(prefix, &body);
assert_eq!(tag::remove_cbor_tag(&tagged), &body);
}
for prefix in [tag::SIGN_PREFIX, tag::MAC_PREFIX, tag::ENCRYPT_PREFIX] {
let tagged = tag::with_tag(prefix, &body);
assert_eq!(tag::remove_cbor_tag(&tagged), &body);
}
let cwt = tag::with_tag(tag::CWT_PREFIX, &tag::with_tag(tag::SIGN1_PREFIX, &body));
assert_eq!(tag::remove_cbor_tag(&cwt), &body);
let self_tagged = tag::with_tag(
tag::CBOR_SELF_PREFIX,
&tag::with_tag(tag::SIGN1_PREFIX, &body),
);
assert_eq!(tag::remove_cbor_tag(&self_tagged), &body);
assert_eq!(tag::remove_cbor_tag(&body), &body);
}