aboutsummaryrefslogtreecommitdiff
path: root/aerogramme
diff options
context:
space:
mode:
authorQuentin Dufour <quentin@deuxfleurs.fr>2024-05-22 19:36:27 +0200
committerQuentin Dufour <quentin@deuxfleurs.fr>2024-05-22 19:36:27 +0200
commit649a7b8b1be97a5d43f48ceff0d3f396fadabbbc (patch)
tree0b7e6761d2ef5ac7162e4e3666f66731ecde6e99 /aerogramme
parent742beeeafbe46c7677d4af36006f0febd0edb6cd (diff)
downloadaerogramme-649a7b8b1be97a5d43f48ceff0d3f396fadabbbc.tar.gz
aerogramme-649a7b8b1be97a5d43f48ceff0d3f396fadabbbc.zip
webdav propfind integration tests
Diffstat (limited to 'aerogramme')
-rw-r--r--aerogramme/Cargo.toml5
-rw-r--r--aerogramme/tests/behavior.rs125
-rw-r--r--aerogramme/tests/common/mod.rs26
3 files changed, 138 insertions, 18 deletions
diff --git a/aerogramme/Cargo.toml b/aerogramme/Cargo.toml
index ab62e44..77f3584 100644
--- a/aerogramme/Cargo.toml
+++ b/aerogramme/Cargo.toml
@@ -21,6 +21,11 @@ tracing.workspace = true
tracing-subscriber.workspace = true
rpassword.workspace = true
+[dev-dependencies]
+reqwest.workspace = true
+aero-dav.workspace = true
+quick-xml.workspace = true
+
[[test]]
name = "behavior"
path = "tests/behavior.rs"
diff --git a/aerogramme/tests/behavior.rs b/aerogramme/tests/behavior.rs
index 13baf0e..1786500 100644
--- a/aerogramme/tests/behavior.rs
+++ b/aerogramme/tests/behavior.rs
@@ -5,21 +5,25 @@ use crate::common::constants::*;
use crate::common::fragments::*;
fn main() {
- rfc3501_imap4rev1_base();
+ // IMAP
+ /*rfc3501_imap4rev1_base();
rfc6851_imapext_move();
rfc4551_imapext_condstore();
rfc2177_imapext_idle();
- rfc5161_imapext_enable(); // 1
- rfc3691_imapext_unselect(); // 2
- rfc7888_imapext_literal(); // 3
- rfc4315_imapext_uidplus(); // 4
- rfc5819_imapext_liststatus(); // 5
+ rfc5161_imapext_enable();
+ rfc3691_imapext_unselect();
+ rfc7888_imapext_literal();
+ rfc4315_imapext_uidplus();
+ rfc5819_imapext_liststatus();*/
+
+ // WebDAV
+ rfc4918_webdav_core();
println!("✅ SUCCESS 🌟🚀🥳🙏🥹");
}
fn rfc3501_imap4rev1_base() {
println!("🧪 rfc3501_imap4rev1_base");
- common::aerogramme_provider_daemon_dev(|imap_socket, lmtp_socket| {
+ common::aerogramme_provider_daemon_dev(|imap_socket, lmtp_socket, _dav_socket| {
connect(imap_socket).context("server says hello")?;
capability(imap_socket, Extension::None).context("check server capabilities")?;
login(imap_socket, Account::Alice).context("login test")?;
@@ -69,7 +73,7 @@ fn rfc3501_imap4rev1_base() {
fn rfc3691_imapext_unselect() {
println!("🧪 rfc3691_imapext_unselect");
- common::aerogramme_provider_daemon_dev(|imap_socket, lmtp_socket| {
+ common::aerogramme_provider_daemon_dev(|imap_socket, lmtp_socket, _dav_socket| {
connect(imap_socket).context("server says hello")?;
lmtp_handshake(lmtp_socket).context("handshake lmtp done")?;
@@ -118,7 +122,7 @@ fn rfc3691_imapext_unselect() {
fn rfc5161_imapext_enable() {
println!("🧪 rfc5161_imapext_enable");
- common::aerogramme_provider_daemon_dev(|imap_socket, _lmtp_socket| {
+ common::aerogramme_provider_daemon_dev(|imap_socket, _lmtp_socket, _dav_socket| {
connect(imap_socket).context("server says hello")?;
login(imap_socket, Account::Alice).context("login test")?;
enable(imap_socket, Enable::Utf8Accept, Some(Enable::Utf8Accept))?;
@@ -132,7 +136,7 @@ fn rfc5161_imapext_enable() {
fn rfc6851_imapext_move() {
println!("🧪 rfc6851_imapext_move");
- common::aerogramme_provider_daemon_dev(|imap_socket, lmtp_socket| {
+ common::aerogramme_provider_daemon_dev(|imap_socket, lmtp_socket, _dav_socket| {
connect(imap_socket).context("server says hello")?;
capability(imap_socket, Extension::Move).context("check server capabilities")?;
@@ -174,7 +178,7 @@ fn rfc6851_imapext_move() {
fn rfc7888_imapext_literal() {
println!("🧪 rfc7888_imapext_literal");
- common::aerogramme_provider_daemon_dev(|imap_socket, _lmtp_socket| {
+ common::aerogramme_provider_daemon_dev(|imap_socket, _lmtp_socket, _dav_socket| {
connect(imap_socket).context("server says hello")?;
capability(imap_socket, Extension::LiteralPlus).context("check server capabilities")?;
@@ -187,7 +191,7 @@ fn rfc7888_imapext_literal() {
fn rfc4551_imapext_condstore() {
println!("🧪 rfc4551_imapext_condstore");
- common::aerogramme_provider_daemon_dev(|imap_socket, lmtp_socket| {
+ common::aerogramme_provider_daemon_dev(|imap_socket, lmtp_socket, _dav_socket| {
// Setup the test
connect(imap_socket).context("server says hello")?;
@@ -245,7 +249,7 @@ fn rfc4551_imapext_condstore() {
fn rfc2177_imapext_idle() {
println!("🧪 rfc2177_imapext_idle");
- common::aerogramme_provider_daemon_dev(|imap_socket, lmtp_socket| {
+ common::aerogramme_provider_daemon_dev(|imap_socket, lmtp_socket, _dav_socket| {
// Test setup, check capability
connect(imap_socket).context("server says hello")?;
capability(imap_socket, Extension::Idle).context("check server capabilities")?;
@@ -266,7 +270,7 @@ fn rfc2177_imapext_idle() {
fn rfc4315_imapext_uidplus() {
println!("🧪 rfc4315_imapext_uidplus");
- common::aerogramme_provider_daemon_dev(|imap_socket, lmtp_socket| {
+ common::aerogramme_provider_daemon_dev(|imap_socket, lmtp_socket, _dav_socket| {
// Test setup, check capability, insert 2 emails
connect(imap_socket).context("server says hello")?;
capability(imap_socket, Extension::UidPlus).context("check server capabilities")?;
@@ -320,7 +324,7 @@ fn rfc4315_imapext_uidplus() {
/// ```
fn rfc5819_imapext_liststatus() {
println!("🧪 rfc5819_imapext_liststatus");
- common::aerogramme_provider_daemon_dev(|imap_socket, lmtp_socket| {
+ common::aerogramme_provider_daemon_dev(|imap_socket, lmtp_socket, _dav_socket| {
// Test setup, check capability, add 2 emails, read 1
connect(imap_socket).context("server says hello")?;
capability(imap_socket, Extension::ListStatus).context("check server capabilities")?;
@@ -355,3 +359,94 @@ fn rfc5819_imapext_liststatus() {
})
.expect("test fully run");
}
+
+use aero_dav::caltypes as cal;
+use aero_dav::realization::All;
+use aero_dav::types as dav;
+
+use crate::common::dav_deserialize;
+
+fn rfc4918_webdav_core() {
+ println!("🧪 rfc4918_webdav_core");
+ common::aerogramme_provider_daemon_dev(|_imap, _lmtp, http| {
+ // --- PROPFIND ---
+ // empty request body (assume "allprop")
+ let body = http.request(reqwest::Method::from_bytes(b"PROPFIND")?, "http://localhost:8087").send()?.text()?;
+ let multistatus = dav_deserialize::<dav::Multistatus<All>>(&body);
+ let root_propstats = multistatus.responses.iter()
+ .find_map(|v| match &v.status_or_propstat {
+ dav::StatusOrPropstat::PropStat(dav::Href(p), x) if p.as_str() == "/" => Some(x),
+ _ => None,
+ })
+ .expect("propstats for root must exist");
+
+ let root_success = root_propstats.iter().find(|p| p.status.0.as_u16() == 200).expect("some propstats for root must be 200");
+ let display_name = root_success.prop.0.iter()
+ .find_map(|v| match v { dav::AnyProperty::Value(dav::Property::DisplayName(x)) => Some(x), _ => None } )
+ .expect("root has a display name");
+ let content_type = root_success.prop.0.iter()
+ .find_map(|v| match v { dav::AnyProperty::Value(dav::Property::GetContentType(x)) => Some(x), _ => None } )
+ .expect("root has a content type");
+ let resource_type = root_success.prop.0.iter()
+ .find_map(|v| match v { dav::AnyProperty::Value(dav::Property::ResourceType(x)) => Some(x), _ => None } )
+ .expect("root has a resource type");
+
+ assert_eq!(display_name, "DAV Root");
+ assert_eq!(content_type, "httpd/unix-directory");
+ assert_eq!(resource_type, &[ dav::ResourceType::Collection ]);
+
+ // propname
+ let propfind_req = r#"<?xml version="1.0" encoding="utf-8" ?><propfind xmlns="DAV:"><propname/></propfind>"#;
+ let body = http.request(reqwest::Method::from_bytes(b"PROPFIND")?, "http://localhost:8087").body(propfind_req).send()?.text()?;
+ let multistatus = dav_deserialize::<dav::Multistatus<All>>(&body);
+ let root_propstats = multistatus.responses.iter()
+ .find_map(|v| match &v.status_or_propstat {
+ dav::StatusOrPropstat::PropStat(dav::Href(p), x) if p.as_str() == "/" => Some(x),
+ _ => None,
+ })
+ .expect("propstats for root must exist");
+ let root_success = root_propstats.iter().find(|p| p.status.0.as_u16() == 200).expect("some propstats for root must be 200");
+ assert!(root_success.prop.0.iter().find(|p| matches!(p, dav::AnyProperty::Request(dav::PropertyRequest::DisplayName))).is_some());
+ assert!(root_success.prop.0.iter().find(|p| matches!(p, dav::AnyProperty::Request(dav::PropertyRequest::ResourceType))).is_some());
+ assert!(root_success.prop.0.iter().find(|p| matches!(p, dav::AnyProperty::Request(dav::PropertyRequest::GetContentType))).is_some());
+
+ // list of properties
+ let propfind_req = r#"<?xml version="1.0" encoding="utf-8" ?><propfind xmlns="DAV:"><prop><displayname/><getcontentlength/></prop></propfind>"#;
+ let body = http.request(reqwest::Method::from_bytes(b"PROPFIND")?, "http://localhost:8087").body(propfind_req).send()?.text()?;
+ let multistatus = dav_deserialize::<dav::Multistatus<All>>(&body);
+ let root_propstats = multistatus.responses.iter()
+ .find_map(|v| match &v.status_or_propstat {
+ dav::StatusOrPropstat::PropStat(dav::Href(p), x) if p.as_str() == "/" => Some(x),
+ _ => None,
+ })
+ .expect("propstats for root must exist");
+
+ let root_success = root_propstats.iter().find(|p| p.status.0.as_u16() == 200).expect("some propstats for root must be 200");
+ let root_not_found = root_propstats.iter().find(|p| p.status.0.as_u16() == 404).expect("some propstats for root must be not found");
+
+ assert!(root_success.prop.0.iter().find(|p| matches!(p, dav::AnyProperty::Value(dav::Property::DisplayName(x)) if x == "DAV Root")).is_some());
+ assert!(root_success.prop.0.iter().find(|p| matches!(p, dav::AnyProperty::Value(dav::Property::ResourceType(_)))).is_none());
+ assert!(root_success.prop.0.iter().find(|p| matches!(p, dav::AnyProperty::Value(dav::Property::GetContentType(_)))).is_none());
+ assert!(root_not_found.prop.0.iter().find(|p| matches!(p, dav::AnyProperty::Request(dav::PropertyRequest::GetContentLength))).is_some());
+
+ // depth 1
+
+ // check tree (calendar, Personal)
+
+ // --- PUT ---
+
+ // --- GET ---
+
+ // --- DELETE ---
+
+
+ Ok(())
+ })
+ .expect("test fully run");
+}
+
+// @TODO ACL
+
+// @TODO CALDAV
+
+// @TODO SYNC
diff --git a/aerogramme/tests/common/mod.rs b/aerogramme/tests/common/mod.rs
index cbe0271..12f2764 100644
--- a/aerogramme/tests/common/mod.rs
+++ b/aerogramme/tests/common/mod.rs
@@ -8,10 +8,13 @@ use std::net::{Shutdown, TcpStream};
use std::process::Command;
use std::thread;
+use reqwest::blocking::Client;
+use reqwest::header;
+
use constants::SMALL_DELAY;
pub fn aerogramme_provider_daemon_dev(
- mut fx: impl FnMut(&mut TcpStream, &mut TcpStream) -> Result<()>,
+ mut fx: impl FnMut(&mut TcpStream, &mut TcpStream, &mut Client) -> Result<()>,
) -> Result<()> {
// Check port is not used (= free) before starting the test
let mut max_retry = 20;
@@ -53,8 +56,15 @@ pub fn aerogramme_provider_daemon_dev(
let mut lmtp_socket =
TcpStream::connect("[::1]:1025").context("lmtp socket must be connected")?;
- println!("-- ready to test imap features --");
- let result = fx(&mut imap_socket, &mut lmtp_socket);
+ let mut headers = header::HeaderMap::new();
+ headers.insert(
+ header::AUTHORIZATION,
+ header::HeaderValue::from_static("Basic YWxpY2U6aHVudGVyMg=="),
+ );
+ let mut http_client = Client::builder().default_headers(headers).build()?;
+
+ println!("-- ready to test features --");
+ let result = fx(&mut imap_socket, &mut lmtp_socket, &mut http_client);
println!("-- test teardown --");
imap_socket
@@ -97,3 +107,13 @@ pub fn read_first_u32(inp: &str) -> Result<u32> {
.collect::<String>()
.parse::<u32>()?)
}
+
+use aero_dav::xml::{Node, Reader};
+pub fn dav_deserialize<T: Node<T>>(src: &str) -> T {
+ futures::executor::block_on(async {
+ let mut rdr = Reader::new(quick_xml::NsReader::from_reader(src.as_bytes()))
+ .await
+ .expect("build reader");
+ rdr.find().await.expect("parse XML")
+ })
+}