synta 0.2.6

ASN.1 parser, decoder, and encoder library with DER/BER support and C FFI
Documentation
//! Tests for serde Serialize / Deserialize support.
//!
//! These tests require both the `serde` feature (for the impls) and `serde_json`
//! (for a concrete serialization format to test against).

#![cfg(feature = "serde")]

use synta::*;

// ---- helpers ----------------------------------------------------------------

fn roundtrip<T>(value: &T) -> T
where
    T: serde::Serialize + for<'de> serde::Deserialize<'de> + std::fmt::Debug + PartialEq,
{
    let json = serde_json::to_string(value).expect("serialize");
    let out: T = serde_json::from_str(&json).expect("deserialize");
    assert_eq!(value, &out, "roundtrip mismatch");
    out
}

// ---- Encoding ---------------------------------------------------------------

#[test]
fn encoding_serializes_as_str() {
    assert_eq!(serde_json::to_string(&Encoding::Der).unwrap(), "\"der\"");
    assert_eq!(serde_json::to_string(&Encoding::Ber).unwrap(), "\"ber\"");
    assert_eq!(serde_json::to_string(&Encoding::Cer).unwrap(), "\"cer\"");
}

#[test]
fn encoding_roundtrip() {
    roundtrip(&Encoding::Der);
    roundtrip(&Encoding::Ber);
    roundtrip(&Encoding::Cer);
}

// ---- TagClass / Tag ---------------------------------------------------------

#[test]
fn tagclass_roundtrip() {
    roundtrip(&TagClass::Universal);
    roundtrip(&TagClass::Application);
    roundtrip(&TagClass::ContextSpecific);
    roundtrip(&TagClass::Private);
}

#[test]
fn tag_roundtrip() {
    roundtrip(&Tag::universal(2));
    roundtrip(&Tag::context_specific(0));
    roundtrip(&Tag::universal_constructed(16));
}

// ---- Primitives -------------------------------------------------------------

#[test]
fn boolean_roundtrip() {
    roundtrip(&Boolean::new(true));
    roundtrip(&Boolean::new(false));
}

#[test]
fn boolean_json_value() {
    assert_eq!(serde_json::to_string(&Boolean::new(true)).unwrap(), "true");
    assert_eq!(
        serde_json::to_string(&Boolean::new(false)).unwrap(),
        "false"
    );
}

#[test]
fn integer_serializes_as_hex() {
    let i = Integer::from_i64(42);
    assert_eq!(serde_json::to_string(&i).unwrap(), "\"2a\"");

    let i = Integer::from_i64(0);
    assert_eq!(serde_json::to_string(&i).unwrap(), "\"00\"");

    let i = Integer::from_i64(-1);
    assert_eq!(serde_json::to_string(&i).unwrap(), "\"ff\"");
}

#[test]
fn integer_roundtrip() {
    roundtrip(&Integer::from_i64(0));
    roundtrip(&Integer::from_i64(42));
    roundtrip(&Integer::from_i64(-1));
    roundtrip(&Integer::from_i64(i64::MAX));
    roundtrip(&Integer::from_i64(i64::MIN));
    roundtrip(&Integer::from_u64(u64::MAX));
}

#[test]
fn null_roundtrip() {
    let json = serde_json::to_string(&Null).unwrap();
    assert_eq!(json, "null");
    let _: Null = serde_json::from_str(&json).unwrap();
}

#[test]
fn real_roundtrip() {
    roundtrip(&Real::new(0.0));
    roundtrip(&Real::new(2.5));
    roundtrip(&Real::new(-1.5));
}

// ---- ObjectIdentifier -------------------------------------------------------

#[test]
fn oid_serializes_as_dotted_decimal() {
    let oid = ObjectIdentifier::new(&[1, 2, 840, 113549]).unwrap();
    assert_eq!(serde_json::to_string(&oid).unwrap(), "\"1.2.840.113549\"");
}

#[test]
fn oid_roundtrip() {
    roundtrip(&ObjectIdentifier::new(&[1, 2, 840, 113549, 1, 1, 11]).unwrap());
    roundtrip(&ObjectIdentifier::new(&[2, 5, 4, 3]).unwrap());
}

// ---- Time types -------------------------------------------------------------

#[test]
fn utctime_serializes_as_string() {
    let t = UtcTime::new(2023, 12, 31, 23, 59, 59).unwrap();
    assert_eq!(serde_json::to_string(&t).unwrap(), "\"231231235959Z\"");
}

#[test]
fn utctime_roundtrip() {
    roundtrip(&UtcTime::new(2023, 6, 15, 12, 0, 0).unwrap());
    roundtrip(&UtcTime::new(1999, 1, 1, 0, 0, 0).unwrap());
}

#[test]
fn generalizedtime_serializes_as_string() {
    let t = GeneralizedTime::new(2023, 12, 31, 23, 59, 59, None).unwrap();
    assert_eq!(serde_json::to_string(&t).unwrap(), "\"20231231235959Z\"");

    let t = GeneralizedTime::new(2023, 12, 31, 23, 59, 59, Some(500)).unwrap();
    assert_eq!(
        serde_json::to_string(&t).unwrap(),
        "\"20231231235959.500Z\""
    );
}

#[test]
fn generalizedtime_roundtrip() {
    roundtrip(&GeneralizedTime::new(2023, 6, 15, 12, 0, 0, None).unwrap());
    roundtrip(&GeneralizedTime::new(2023, 6, 15, 12, 0, 0, Some(123)).unwrap());
}

// ---- String types -----------------------------------------------------------

#[test]
fn octetstring_serializes_as_hex() {
    let os = OctetString::new(vec![0xDE, 0xAD, 0xBE, 0xEF]);
    assert_eq!(serde_json::to_string(&os).unwrap(), "\"deadbeef\"");
}

#[test]
fn octetstring_roundtrip() {
    roundtrip(&OctetString::new(vec![]));
    roundtrip(&OctetString::new(vec![0x00, 0xFF, 0xAB]));
}

#[test]
fn bitstring_roundtrip() {
    let bs = BitString::new(vec![0xDE, 0xAD], 0).unwrap();
    let json = serde_json::to_string(&bs).unwrap();
    assert!(json.contains("\"bytes\""));
    assert!(json.contains("\"unused_bits\""));
    let bs2: BitString = serde_json::from_str(&json).unwrap();
    assert_eq!(bs.as_bytes(), bs2.as_bytes());
    assert_eq!(bs.unused_bits(), bs2.unused_bits());
}

#[test]
fn utf8string_roundtrip() {
    roundtrip(&Utf8String::new("hello world".to_string()));
    roundtrip(&Utf8String::new(String::new()));
}

#[test]
fn printablestring_roundtrip() {
    let ps = PrintableString::new("Hello World".to_string()).unwrap();
    roundtrip(&ps);
}

#[test]
fn ia5string_roundtrip() {
    let s = IA5String::new("hello@example.com".to_string()).unwrap();
    roundtrip(&s);
}

// ---- Constructed types ------------------------------------------------------

#[test]
fn sequenceof_roundtrip() {
    let mut seq: SequenceOf<Integer> = SequenceOf::new();
    seq.push(Integer::from_i64(1));
    seq.push(Integer::from_i64(2));
    seq.push(Integer::from_i64(3));
    roundtrip(&seq);
}

#[test]
fn setof_roundtrip() {
    let mut set: SetOf<Boolean> = SetOf::new();
    set.push(Boolean::new(true));
    set.push(Boolean::new(false));
    roundtrip(&set);
}

// ---- Element serialization --------------------------------------------------

#[test]
fn element_boolean_serializes() {
    let el = Element::Boolean(Boolean::new(true));
    let json = serde_json::to_string(&el).unwrap();
    assert_eq!(json, r#"{"type":"Boolean","value":true}"#);
}

#[test]
fn element_integer_serializes() {
    let el = Element::Integer(Integer::from_i64(42));
    let json = serde_json::to_string(&el).unwrap();
    assert_eq!(json, r#"{"type":"Integer","value":"2a"}"#);
}

#[test]
fn element_oid_serializes() {
    let oid = ObjectIdentifier::new(&[1, 2, 3]).unwrap();
    let el = Element::ObjectIdentifier(oid);
    let json = serde_json::to_string(&el).unwrap();
    assert_eq!(json, r#"{"type":"ObjectIdentifier","value":"1.2.3"}"#);
}

#[test]
fn element_null_serializes() {
    let el = Element::Null(Null);
    let json = serde_json::to_string(&el).unwrap();
    assert_eq!(json, r#"{"type":"Null","value":null}"#);
}

// ---- Tagged types -----------------------------------------------------------

#[test]
fn explicit_tag_roundtrip() {
    let tagged = ExplicitTag::context_specific(0, Integer::from_i64(42));
    let json = serde_json::to_string(&tagged).unwrap();
    let tagged2: ExplicitTag<Integer> = serde_json::from_str(&json).unwrap();
    assert_eq!(tagged.tag(), tagged2.tag());
    assert_eq!(tagged.inner().as_bytes(), tagged2.inner().as_bytes());
}

#[test]
fn implicit_tag_roundtrip() {
    let tagged = ImplicitTag::context_specific(1, Boolean::new(false));
    let json = serde_json::to_string(&tagged).unwrap();
    let tagged2: ImplicitTag<Boolean> = serde_json::from_str(&json).unwrap();
    assert_eq!(tagged.tag(), tagged2.tag());
    assert_eq!(tagged.inner(), tagged2.inner());
}