carddav: Try out some tests with insta

This commit is contained in:
Lennart
2025-12-10 12:26:31 +01:00
parent eecc03b7b7
commit a45e0b2efd
12 changed files with 249 additions and 12 deletions

12
Cargo.lock generated
View File

@@ -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",

View File

@@ -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

View File

@@ -35,3 +35,6 @@ percent-encoding.workspace = true
ical.workspace = true
strum.workspace = true
strum_macros.workspace = true
[dev-dependencies]
insta.workspace = true

View File

@@ -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<String>,
pub principal: Principal,
pub members: Vec<String>,
}
impl ResourceName for PrincipalResource {

View File

@@ -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<HrefElement>),
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone)]
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, Debug)]
pub struct AddressbookHomeSet(#[xml(ty = "untagged", flatten)] pub Vec<HrefElement>);
#[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),

View File

@@ -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,
},
),
],
}

View File

@@ -0,0 +1,45 @@
---
source: crates/carddav/src/principal/tests.rs
expression: response.serialize_to_string().unwrap()
---
<?xml version="1.0" encoding="utf-8"?>
<response xmlns="DAV:" xmlns:CAL="urn:ietf:params:xml:ns:caldav" xmlns:CARD="urn:ietf:params:xml:ns:carddav" xmlns:CS="http://calendarserver.org/ns/" xmlns:PUSH="https://bitfire.at/webdav-push">
<href>/carddav/principal/user/</href>
<propstat>
<prop>
<principal-URL>
<href>/carddav/principal/user/</href>
</principal-URL>
<group-membership>
<href>/carddav/principal/group/</href>
</group-membership>
<group-member-set>
</group-member-set>
<alternate-URI-set/>
<principal-collection-set>
<href>/carddav/principal/</href>
</principal-collection-set>
<CARD:addressbook-home-set>
<href>/carddav/principal/group/</href>
<href>/carddav/principal/user/</href>
</CARD:addressbook-home-set>
<resourcetype>
<collection/>
<principal/>
</resourcetype>
<displayname>user</displayname>
<current-user-principal>
<href>/carddav/principal/user/</href>
</current-user-principal>
<current-user-privilege-set>
<privilege>
<all/>
</privilege>
</current-user-privilege-set>
<owner>
<href>/carddav/principal/user/</href>
</owner>
</prop>
<status>HTTP/1.1 200 OK</status>
</propstat>
</response>

View File

@@ -0,0 +1,8 @@
---
source: crates/carddav/src/principal/tests.rs
expression: propfind
---
PropfindElement {
prop: Allprop,
include: None,
}

View File

@@ -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#"<?xml version="1.0" encoding="UTF-8"?><propfind xmlns="DAV:"><allprop/></propfind>"#,
)
.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());
}

View File

@@ -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)

View File

@@ -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<HrefElement>);
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone)]
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Eq, Clone, Debug)]
pub struct GroupMemberSet(#[xml(ty = "untagged", flatten)] pub Vec<HrefElement>);

View File

@@ -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<T: XmlSerialize>(#[xml(flatten, ty = "untagged")] pub Vec<T>);
// 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<T: XmlSerialize> {
Normal(PropstatElement<PropTagWrapper<T>>),
@@ -40,7 +40,7 @@ pub enum PropstatWrapper<T: XmlSerialize> {
// RFC 2518
// <!ELEMENT response (href, ((href*, status)|(propstat+)),
// responsedescription?) >
#[derive(XmlSerialize, XmlRootTag)]
#[derive(XmlSerialize, XmlRootTag, Debug)]
#[xml(ns = "crate::namespace::NS_DAV", root = "response")]
#[xml(ns_prefix(
crate::namespace::NS_DAV = "",