diff --git a/Cargo.lock b/Cargo.lock index ac68a26..d876697 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1789,6 +1789,17 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" +[[package]] +name = "insta" +version = "1.44.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5c943d4415edd8153251b6f197de5eb1640e56d84e8d9159bea190421c73698" +dependencies = [ + "console", + "once_cell", + "similar", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -3142,6 +3153,7 @@ dependencies = [ "futures-util", "http", "ical", + "insta", "percent-encoding", "quick-xml", "rustical_dav", diff --git a/Cargo.toml b/Cargo.toml index f2d8ca0..bd45446 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -149,6 +149,7 @@ ece = { version = "2.3", default-features = false, features = [ openssl = { version = "0.10", features = ["vendored"] } async-std = { version = "1.13", features = ["attributes"] } similar-asserts = "1.7" +insta = "1.44" [dependencies] rustical_store.workspace = true diff --git a/crates/carddav/Cargo.toml b/crates/carddav/Cargo.toml index ef26313..7fc92eb 100644 --- a/crates/carddav/Cargo.toml +++ b/crates/carddav/Cargo.toml @@ -35,3 +35,6 @@ percent-encoding.workspace = true ical.workspace = true strum.workspace = true strum_macros.workspace = true + +[dev-dependencies] +insta.workspace = true diff --git a/crates/carddav/src/principal/mod.rs b/crates/carddav/src/principal/mod.rs index bb43c10..e6f428a 100644 --- a/crates/carddav/src/principal/mod.rs +++ b/crates/carddav/src/principal/mod.rs @@ -11,11 +11,13 @@ mod service; pub use service::*; mod prop; pub use prop::*; +#[cfg(test)] +pub mod tests; #[derive(Debug, Clone)] pub struct PrincipalResource { - principal: Principal, - members: Vec, + pub principal: Principal, + pub members: Vec, } impl ResourceName for PrincipalResource { diff --git a/crates/carddav/src/principal/prop.rs b/crates/carddav/src/principal/prop.rs index 2d56992..ae5ecca 100644 --- a/crates/carddav/src/principal/prop.rs +++ b/crates/carddav/src/principal/prop.rs @@ -4,7 +4,7 @@ use rustical_dav::{ }; use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize}; -#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, EnumVariants, PropName)] +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, EnumVariants, PropName, Debug)] #[xml(unit_variants_ident = "PrincipalPropName")] pub enum PrincipalProp { // WebDAV Access Control (RFC 3744) @@ -27,10 +27,10 @@ pub enum PrincipalProp { PrincipalAddress(Option), } -#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone)] +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, Debug)] pub struct AddressbookHomeSet(#[xml(ty = "untagged", flatten)] pub Vec); -#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, EnumVariants, PropName)] +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, EnumVariants, PropName, Debug)] #[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)] pub enum PrincipalPropWrapper { Principal(PrincipalProp), diff --git a/crates/carddav/src/principal/snapshots/rustical_carddav__principal__tests__propfind-2.snap b/crates/carddav/src/principal/snapshots/rustical_carddav__principal__tests__propfind-2.snap new file mode 100644 index 0000000..7ce8593 --- /dev/null +++ b/crates/carddav/src/principal/snapshots/rustical_carddav__principal__tests__propfind-2.snap @@ -0,0 +1,125 @@ +--- +source: crates/carddav/src/principal/tests.rs +expression: response +--- +ResponseElement { + href: "/carddav/principal/user/", + status: None, + propstat: [ + Normal( + PropstatElement { + prop: PropTagWrapper( + [ + Principal( + PrincipalUrl( + HrefElement { + href: "/carddav/principal/user/", + }, + ), + ), + Principal( + GroupMembership( + GroupMembership( + [ + HrefElement { + href: "/carddav/principal/group/", + }, + ], + ), + ), + ), + Principal( + GroupMemberSet( + GroupMemberSet( + [], + ), + ), + ), + Principal( + AlternateUriSet, + ), + Principal( + PrincipalCollectionSet( + HrefElement { + href: "/carddav/principal/", + }, + ), + ), + Principal( + AddressbookHomeSet( + AddressbookHomeSet( + [ + HrefElement { + href: "/carddav/principal/group/", + }, + HrefElement { + href: "/carddav/principal/user/", + }, + ], + ), + ), + ), + Principal( + PrincipalAddress( + None, + ), + ), + Common( + Resourcetype( + Resourcetype( + [ + ResourcetypeInner( + Some( + Namespace("DAV:"), + ), + "collection", + ), + ResourcetypeInner( + Some( + Namespace("DAV:"), + ), + "principal", + ), + ], + ), + ), + ), + Common( + Displayname( + Some( + "user", + ), + ), + ), + Common( + CurrentUserPrincipal( + HrefElement { + href: "/carddav/principal/user/", + }, + ), + ), + Common( + CurrentUserPrivilegeSet( + UserPrivilegeSet { + privileges: { + All, + }, + }, + ), + ), + Common( + Owner( + Some( + HrefElement { + href: "/carddav/principal/user/", + }, + ), + ), + ), + ], + ), + status: 200, + }, + ), + ], +} diff --git a/crates/carddav/src/principal/snapshots/rustical_carddav__principal__tests__propfind-3.snap b/crates/carddav/src/principal/snapshots/rustical_carddav__principal__tests__propfind-3.snap new file mode 100644 index 0000000..cde483a --- /dev/null +++ b/crates/carddav/src/principal/snapshots/rustical_carddav__principal__tests__propfind-3.snap @@ -0,0 +1,45 @@ +--- +source: crates/carddav/src/principal/tests.rs +expression: response.serialize_to_string().unwrap() +--- + + + /carddav/principal/user/ + + + + /carddav/principal/user/ + + + /carddav/principal/group/ + + + + + + /carddav/principal/ + + + /carddav/principal/group/ + /carddav/principal/user/ + + + + + + user + + /carddav/principal/user/ + + + + + + + + /carddav/principal/user/ + + + HTTP/1.1 200 OK + + diff --git a/crates/carddav/src/principal/snapshots/rustical_carddav__principal__tests__propfind.snap b/crates/carddav/src/principal/snapshots/rustical_carddav__principal__tests__propfind.snap new file mode 100644 index 0000000..380891b --- /dev/null +++ b/crates/carddav/src/principal/snapshots/rustical_carddav__principal__tests__propfind.snap @@ -0,0 +1,8 @@ +--- +source: crates/carddav/src/principal/tests.rs +expression: propfind +--- +PropfindElement { + prop: Allprop, + include: None, +} diff --git a/crates/carddav/src/principal/tests.rs b/crates/carddav/src/principal/tests.rs new file mode 100644 index 0000000..14c9f2d --- /dev/null +++ b/crates/carddav/src/principal/tests.rs @@ -0,0 +1,41 @@ +use rustical_dav::resource::Resource; +use rustical_store::auth::Principal; +use rustical_xml::XmlSerializeRoot; + +use crate::{CardDavPrincipalUri, principal::PrincipalResource}; + +#[test] +fn test_propfind() { + let propfind = PrincipalResource::parse_propfind( + r#""#, + ) + .unwrap(); + + insta::assert_debug_snapshot!(propfind); + + let principal = Principal { + id: "user".to_string(), + displayname: None, + principal_type: rustical_store::auth::PrincipalType::Individual, + password: None, + memberships: vec!["group".to_string()], + }; + + let resource = PrincipalResource { + principal: principal.clone(), + members: vec![], + }; + + let response = resource + .propfind( + &format!("/carddav/principal/{}", principal.id), + &propfind.prop, + propfind.include.as_ref(), + &CardDavPrincipalUri("/carddav"), + &principal, + ) + .unwrap(); + + insta::assert_debug_snapshot!(response); + insta::assert_snapshot!(response.serialize_to_string().unwrap()); +} diff --git a/crates/dav/src/extensions/common.rs b/crates/dav/src/extensions/common.rs index 0592252..625972a 100644 --- a/crates/dav/src/extensions/common.rs +++ b/crates/dav/src/extensions/common.rs @@ -6,7 +6,7 @@ use crate::{ }; use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize}; -#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, PropName, EnumVariants)] +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Debug, Clone, PropName, EnumVariants)] #[xml(unit_variants_ident = "CommonPropertiesPropName")] pub enum CommonPropertiesProp { // WebDAV (RFC 2518) diff --git a/crates/dav/src/xml/group.rs b/crates/dav/src/xml/group.rs index fce2d28..6e9027a 100644 --- a/crates/dav/src/xml/group.rs +++ b/crates/dav/src/xml/group.rs @@ -1,8 +1,8 @@ use crate::xml::HrefElement; use rustical_xml::{XmlDeserialize, XmlSerialize}; -#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone)] +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, Debug)] pub struct GroupMembership(#[xml(ty = "untagged", flatten)] pub Vec); -#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone)] +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, Debug)] pub struct GroupMemberSet(#[xml(ty = "untagged", flatten)] pub Vec); diff --git a/crates/dav/src/xml/multistatus.rs b/crates/dav/src/xml/multistatus.rs index 0df60bb..67cd7c8 100644 --- a/crates/dav/src/xml/multistatus.rs +++ b/crates/dav/src/xml/multistatus.rs @@ -3,9 +3,9 @@ use headers::{CacheControl, ContentType, HeaderMapExt}; use http::StatusCode; use quick_xml::name::Namespace; use rustical_xml::{XmlRootTag, XmlSerialize, XmlSerializeRoot}; -use std::collections::HashMap; +use std::{collections::HashMap, fmt::Debug}; -#[derive(XmlSerialize)] +#[derive(XmlSerialize, Debug)] pub struct PropTagWrapper(#[xml(flatten, ty = "untagged")] pub Vec); // RFC 2518 @@ -30,7 +30,7 @@ fn xml_serialize_status( XmlSerialize::serialize(&format!("HTTP/1.1 {status}"), ns, tag, namespaces, writer) } -#[derive(XmlSerialize)] +#[derive(XmlSerialize, Debug)] #[xml(untagged)] pub enum PropstatWrapper { Normal(PropstatElement>), @@ -40,7 +40,7 @@ pub enum PropstatWrapper { // RFC 2518 // -#[derive(XmlSerialize, XmlRootTag)] +#[derive(XmlSerialize, XmlRootTag, Debug)] #[xml(ns = "crate::namespace::NS_DAV", root = "response")] #[xml(ns_prefix( crate::namespace::NS_DAV = "",