From 3aef9abe486240556d0d76f125260ea4c234444e Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Wed, 31 Dec 2025 16:50:55 +0100 Subject: [PATCH] carddav: Add more integration tests --- src/integration_tests/carddav/addressbook.rs | 253 ++++++++++++++++++ ...__carddav__addressbook__multiget_body.snap | 35 +++ 2 files changed, 288 insertions(+) create mode 100644 src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__addressbook__multiget_body.snap diff --git a/src/integration_tests/carddav/addressbook.rs b/src/integration_tests/carddav/addressbook.rs index 0f1aae7..76d165e 100644 --- a/src/integration_tests/carddav/addressbook.rs +++ b/src/integration_tests/carddav/addressbook.rs @@ -192,3 +192,256 @@ async fn test_carddav_addressbook( Err(rustical_store::Error::NotFound) )); } + +#[rstest] +#[tokio::test] +async fn test_mkcol_rfc6352_6_3_1_1( + #[from(test_store_context)] + #[future] + context: TestStoreContext, +) { + let context = context.await; + let app = get_app(context.clone()); + let addr_store = context.addr_store; + + let (displayname, description) = ( + "Lisa's Contacts".to_owned(), + "My primary address book.".to_owned(), + ); + let (principal, addr_id) = ("user", "contacts"); + let url = format!("/carddav/principal/{principal}/{addr_id}"); + + let mut request = Request::builder() + .method("MKCOL") + .uri(&url) + .body(Body::from(format!( + r#" + + + + + + + + {displayname} + {description} + + + "# + ))) + .unwrap(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "pass")); + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::CREATED); + let body = response.extract_string().await; + insta::assert_snapshot!("mkcol_body", body); + let saved_addressbook = addr_store + .get_addressbook(principal, addr_id, false) + .await + .unwrap(); + assert_eq!( + ( + saved_addressbook.displayname.unwrap(), + saved_addressbook.description.unwrap() + ), + (displayname, description) + ); + + let vcard = r"BEGIN:VCARD +VERSION:3.0 +FN:Cyrus Daboo +N:Daboo;Cyrus +ADR;TYPE=POSTAL:;2822 Email HQ;Suite 2821;RFCVille;PA;15213;USA +EMAIL;TYPE=INTERNET,PREF:cyrus@example.com +NICKNAME:me +NOTE:Example VCard. +ORG:Self Employed +TEL;TYPE=WORK,VOICE:412 605 0499 +TEL;TYPE=FAX:412 605 0705 +URL:http://www.example.com +UID:1234-5678-9000-1 +END:VCARD + "; + + let mut request = Request::builder() + .method("PUT") + .uri(format!("{url}/newcard.vcf")) + .header("If-None-Match", "*") + .header("Content-Type", "text/vcard") + .body(Body::from(vcard)) + .unwrap(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "pass")); + + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::CREATED); + let etag = response.headers().get("ETag").unwrap(); + + // This should overwrite + let mut request = Request::builder() + .method("PUT") + .uri(format!("{url}/newcard.vcf")) + .header("If-None-Match", "\"somearbitraryetag\"") + .header("Content-Type", "text/vcard") + .body(Body::from(vcard)) + .unwrap(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "pass")); + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::CREATED); + + let mut request = Request::builder() + .method("PUT") + .uri(format!("{url}/newcard.vcf")) + .header("If-None-Match", etag) + .header("Content-Type", "text/vcard") + .body(Body::from(vcard)) + .unwrap(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "pass")); + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::CONFLICT); + + let mut request = Request::builder() + .method("PUT") + .uri(format!("{url}/newcard.vcf")) + .header("If-None-Match", "*") + .header("Content-Type", "text/vcard") + .body(Body::from(vcard)) + .unwrap(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "pass")); + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::CONFLICT); +} + +#[rstest] +#[tokio::test] +async fn test_rfc6352_8_7_1( + #[from(test_store_context)] + #[future] + context: TestStoreContext, +) { + let context = context.await; + let app = get_app(context.clone()); + let addr_store = context.addr_store; + + let (displayname, description) = ( + "Lisa's Contacts".to_owned(), + "My primary address book.".to_owned(), + ); + let (principal, addr_id) = ("user", "contacts"); + let url = format!("/carddav/principal/{principal}/{addr_id}"); + + let mut request = Request::builder() + .method("MKCOL") + .uri(&url) + .body(Body::from(format!( + r#" + + + + + + + + {displayname} + {description} + + + "# + ))) + .unwrap(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "pass")); + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::CREATED); + let body = response.extract_string().await; + insta::assert_snapshot!("mkcol_body", body); + let saved_addressbook = addr_store + .get_addressbook(principal, addr_id, false) + .await + .unwrap(); + assert_eq!( + ( + saved_addressbook.displayname.unwrap(), + saved_addressbook.description.unwrap() + ), + (displayname, description) + ); + + let vcard = r"BEGIN:VCARD +VERSION:3.0 +FN:Cyrus Daboo +N:Daboo;Cyrus +ADR;TYPE=POSTAL:;2822 Email HQ;Suite 2821;RFCVille;PA;15213;USA +EMAIL;TYPE=INTERNET,PREF:cyrus@example.com +NICKNAME:me +NOTE:Example VCard. +ORG:Self Employed +TEL;TYPE=WORK,VOICE:412 605 0499 +TEL;TYPE=FAX:412 605 0705 +URL:http://www.example.com +UID:1234-5678-9000-1 +END:VCARD + "; + + let mut request = Request::builder() + .method("PUT") + .uri(format!("{url}/newcard.vcf")) + .header("If-None-Match", "*") + .header("Content-Type", "text/vcard") + .body(Body::from(vcard)) + .unwrap(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "pass")); + + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::CREATED); + + let mut request = Request::builder() + .method("REPORT") + .uri(&url) + .header("Depth", "infinity") + .header("Content-Type", "text/xml; charset=\"utf-8\"") + .body(Body::from(format!( + r#" + + + + + + + + + + + + + {url}/newcard.vcf + /home/bernard/addressbook/vcf1.vcf + + "# + ))) + .unwrap(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "pass")); + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::MULTI_STATUS); + let body = response.extract_string().await; + insta::assert_snapshot!("multiget_body", body); +} diff --git a/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__addressbook__multiget_body.snap b/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__addressbook__multiget_body.snap new file mode 100644 index 0000000..147559f --- /dev/null +++ b/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__addressbook__multiget_body.snap @@ -0,0 +1,35 @@ +--- +source: src/integration_tests/carddav/addressbook.rs +expression: body +--- + + + + /carddav/principal/user/contacts/newcard.vcf + + + "24835b6c11816c864f9edadd4c7c296234c643892afcbbc5fbf5c9b7ac935cf8" + BEGIN:VCARD +VERSION:3.0 +FN:Cyrus Daboo +N:Daboo;Cyrus +ADR;TYPE=POSTAL:;2822 Email HQ;Suite 2821;RFCVille;PA;15213;USA +EMAIL;TYPE=INTERNET,PREF:cyrus@example.com +NICKNAME:me +NOTE:Example VCard. +ORG:Self Employed +TEL;TYPE=WORK,VOICE:412 605 0499 +TEL;TYPE=FAX:412 605 0705 +URL:http://www.example.com +UID:1234-5678-9000-1 +END:VCARD + + + HTTP/1.1 200 OK + + + + /home/bernard/addressbook/vcf1.vcf + HTTP/1.1 404 Not Found + +