actix-xml 0.2.0

XML extractor for actix-web
Documentation
use actix_web::http::header;
use actix_web::test::TestRequest;
use actix_web::web::Bytes;
use actix_web::{web, FromRequest};
use serde::Deserialize;

use crate::error::XMLPayloadError;
use crate::{Xml, XmlBody, XmlConfig};

#[derive(Deserialize, Eq, PartialEq, Debug)]
struct MyObject {
    name: String,
}

fn xml_eq(err: XMLPayloadError, other: XMLPayloadError) -> bool {
    match err {
        XMLPayloadError::Overflow => matches!(other, XMLPayloadError::Overflow),
        XMLPayloadError::ContentType => {
            matches!(other, XMLPayloadError::ContentType)
        }
        _ => false,
    }
}

#[actix_rt::test]
async fn test_extract() {
    let (req, mut pl) = TestRequest::default()
        .insert_header((
            header::CONTENT_TYPE,
            header::HeaderValue::from_static("application/xml"),
        ))
        .insert_header((
            header::CONTENT_LENGTH,
            header::HeaderValue::from_static("25"),
        ))
        .set_payload(Bytes::from_static(b"<MyObject name=\"test\" />"))
        .to_http_parts();

    let s = Xml::<MyObject>::from_request(&req, &mut pl).await.unwrap();
    assert_eq!(s.name, "test");
    assert_eq!(
        s.into_inner(),
        MyObject {
            name: "test".to_string()
        }
    );

    let (req, mut pl) = TestRequest::default()
        .insert_header((
            header::CONTENT_TYPE,
            header::HeaderValue::from_static("application/xml"),
        ))
        .insert_header((
            header::CONTENT_LENGTH,
            header::HeaderValue::from_static("25"),
        ))
        .set_payload(Bytes::from_static(b"<MyObject name=\"test\" />"))
        .app_data(XmlConfig::default().limit(10))
        .to_http_parts();

    let s = Xml::<MyObject>::from_request(&req, &mut pl).await;
    assert!(format!("{}", s.err().unwrap()).contains("Xml payload size is bigger than allowed"));
}

#[actix_rt::test]
async fn test_xml_body() {
    let (req, mut pl) = TestRequest::default()
        .insert_header((
            header::CONTENT_TYPE,
            header::HeaderValue::from_static("application/xml"),
        ))
        .insert_header((
            header::CONTENT_LENGTH,
            header::HeaderValue::from_static("10000"),
        ))
        .to_http_parts();

    let xml = XmlBody::<MyObject>::new(&req, &mut pl).limit(100).await;
    assert!(xml_eq(xml.err().unwrap(), XMLPayloadError::Overflow));

    let (req, mut pl) = TestRequest::default()
        .insert_header((
            header::CONTENT_TYPE,
            header::HeaderValue::from_static("application/xml"),
        ))
        .insert_header((
            header::CONTENT_LENGTH,
            header::HeaderValue::from_static("25"),
        ))
        .set_payload(Bytes::from_static(b"<MyObject name=\"test\" />"))
        .to_http_parts();

    let xml = XmlBody::<MyObject>::new(&req, &mut pl).await;
    assert_eq!(
        xml.ok().unwrap(),
        MyObject {
            name: "test".to_owned()
        }
    );
}

#[actix_rt::test]
async fn test_with_xml_and_bad_content_type() {
    let (req, mut pl) = TestRequest::default()
        .insert_header((
            header::CONTENT_TYPE,
            header::HeaderValue::from_static("text/plain"),
        ))
        .insert_header((
            header::CONTENT_LENGTH,
            header::HeaderValue::from_static("25"),
        ))
        .set_payload(Bytes::from_static(b"<MyObject name=\"test\" />"))
        .app_data(XmlConfig::default().limit(4096))
        .to_http_parts();

    let s = Xml::<MyObject>::from_request(&req, &mut pl).await;
    assert!(s.is_err())
}

#[actix_rt::test]
async fn test_with_xml_and_good_custom_content_type() {
    let (req, mut pl) = TestRequest::default()
        .insert_header((
            header::CONTENT_TYPE,
            header::HeaderValue::from_static("text/plain"),
        ))
        .insert_header((
            header::CONTENT_LENGTH,
            header::HeaderValue::from_static("25"),
        ))
        .set_payload(Bytes::from_static(b"<MyObject name=\"test\" />"))
        .app_data(XmlConfig::default().content_type(|mime: mime::Mime| {
            mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN
        }))
        .to_http_parts();

    let s = Xml::<MyObject>::from_request(&req, &mut pl).await;
    assert!(s.is_ok())
}

#[actix_rt::test]
async fn test_with_xml_and_bad_custom_content_type() {
    let (req, mut pl) = TestRequest::default()
        .insert_header((
            header::CONTENT_TYPE,
            header::HeaderValue::from_static("text/html"),
        ))
        .insert_header((
            header::CONTENT_LENGTH,
            header::HeaderValue::from_static("25"),
        ))
        .set_payload(Bytes::from_static(b"<MyObject name=\"test\" />"))
        .app_data(XmlConfig::default().content_type(|mime: mime::Mime| {
            mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN
        }))
        .to_http_parts();

    let s = Xml::<MyObject>::from_request(&req, &mut pl).await;
    assert!(s.is_err())
}

#[actix_rt::test]
async fn test_with_config_in_data_wrapper() {
    let (req, mut pl) = TestRequest::default()
        .insert_header((
            header::CONTENT_TYPE,
            header::HeaderValue::from_static("application/xml"),
        ))
        .insert_header((
            header::CONTENT_LENGTH,
            header::HeaderValue::from_static("25"),
        ))
        .set_payload(Bytes::from_static(b"<MyObject name=\"test\" />"))
        .app_data(web::Data::new(XmlConfig::default().limit(10)))
        .to_http_parts();

    let s = Xml::<MyObject>::from_request(&req, &mut pl).await;
    assert!(s.is_err());

    let err_str = s.err().unwrap().to_string();
    assert!(err_str.contains("Xml payload size is bigger than allowed"));
}