Store vanished IMAP UIDs and WebDAV paths
This commit is contained in:
@@ -166,7 +166,7 @@ pub async fn test(imap: &mut ImapConnection, imap_check: &mut ImapConnection) {
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_count("VANISHED", 1)
|
||||
.assert_contains("VANISHED (EARLIER) 1:2") // .assert_contains("VANISHED (EARLIER) 2")
|
||||
.assert_contains("VANISHED (EARLIER) 2")
|
||||
.assert_count("FETCH (", 3);
|
||||
|
||||
// Fetch changes since SEQ 4
|
||||
@@ -178,7 +178,7 @@ pub async fn test(imap: &mut ImapConnection, imap_check: &mut ImapConnection) {
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_count("VANISHED", 1)
|
||||
.assert_contains("VANISHED (EARLIER) 1:2") // .assert_contains("VANISHED (EARLIER) 2")
|
||||
.assert_contains("VANISHED (EARLIER) 2")
|
||||
.assert_count("FETCH (", 2);
|
||||
|
||||
// Fetch changes since SEQ 6
|
||||
@@ -190,7 +190,7 @@ pub async fn test(imap: &mut ImapConnection, imap_check: &mut ImapConnection) {
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_count("VANISHED", 1)
|
||||
.assert_contains("VANISHED (EARLIER) 1:2") // .assert_contains("VANISHED (EARLIER) 2")
|
||||
.assert_contains("VANISHED (EARLIER) 2")
|
||||
.assert_count("FETCH (", 1);
|
||||
|
||||
// Fetch changes since SEQ 7
|
||||
@@ -202,7 +202,7 @@ pub async fn test(imap: &mut ImapConnection, imap_check: &mut ImapConnection) {
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_count("VANISHED", 1)
|
||||
.assert_contains("VANISHED (EARLIER) 1:2") // .assert_contains("VANISHED (EARLIER) 2")
|
||||
.assert_contains("VANISHED (EARLIER) 2")
|
||||
.assert_count("FETCH (", 0);
|
||||
|
||||
// Fetch changes since SEQ 8
|
||||
@@ -276,5 +276,5 @@ pub async fn test(imap: &mut ImapConnection, imap_check: &mut ImapConnection) {
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_count("FETCH (", 3)
|
||||
.assert_contains("VANISHED (EARLIER) 1:2"); // .assert_contains("VANISHED (EARLIER) 2");
|
||||
.assert_contains("VANISHED (EARLIER) 2");
|
||||
}
|
||||
|
||||
@@ -131,6 +131,24 @@ pub async fn test(test: &WebDavTest) {
|
||||
replace_prefix(&mut hierarchy, &hierarchy_root, &new_hierarchy_root);
|
||||
assert_result(&response, &hierarchy);
|
||||
client.validate_values(&hierarchy).await;
|
||||
// Validate changes
|
||||
let changes = client
|
||||
.sync_collection(
|
||||
&user_base_path,
|
||||
sync_token,
|
||||
Depth::Infinity,
|
||||
None,
|
||||
["D:getetag"],
|
||||
)
|
||||
.await
|
||||
.with_href_count(2)
|
||||
.into_propfind_response(None);
|
||||
changes
|
||||
.properties(&hierarchy_root)
|
||||
.with_status(StatusCode::NOT_FOUND);
|
||||
changes
|
||||
.properties(&new_hierarchy_root)
|
||||
.with_status(StatusCode::OK);
|
||||
let hierarchy_root = new_hierarchy_root;
|
||||
|
||||
// Test 2: Copy container
|
||||
@@ -167,6 +185,17 @@ pub async fn test(test: &WebDavTest) {
|
||||
// Test 4: Create a shallow container and overwrite the previous one using MOVE
|
||||
let (new_hierarchy_root, mut hierarchy) =
|
||||
client.create_hierarchy(&user_base_path, 0, 0, 3).await;
|
||||
let sync_token = client
|
||||
.sync_collection(
|
||||
&user_base_path,
|
||||
sync_token,
|
||||
Depth::Infinity,
|
||||
None,
|
||||
["D:getetag"],
|
||||
)
|
||||
.await
|
||||
.sync_token()
|
||||
.to_string();
|
||||
client
|
||||
.request_with_headers(
|
||||
"MOVE",
|
||||
@@ -182,6 +211,23 @@ pub async fn test(test: &WebDavTest) {
|
||||
replace_prefix(&mut hierarchy, &new_hierarchy_root, &hierarchy_root);
|
||||
assert_result(&response, &hierarchy);
|
||||
client.validate_values(&hierarchy).await;
|
||||
// Validate changes
|
||||
let changes = client
|
||||
.sync_collection(
|
||||
&user_base_path,
|
||||
&sync_token,
|
||||
Depth::Infinity,
|
||||
None,
|
||||
["D:getetag"],
|
||||
)
|
||||
.await
|
||||
.into_propfind_response(None);
|
||||
changes
|
||||
.properties(&new_hierarchy_root)
|
||||
.with_status(StatusCode::NOT_FOUND);
|
||||
changes
|
||||
.properties(&hierarchy_root)
|
||||
.with_status(StatusCode::OK);
|
||||
|
||||
// Test 5: Create a deep container and overwrite the previous one using COPY
|
||||
let (new_hierarchy_root, new_hierarchy) = client
|
||||
|
||||
@@ -710,7 +710,11 @@ pub struct DavItem {
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
pub struct DavProperties(Vec<DavItem>);
|
||||
pub struct DavProperties {
|
||||
#[serde(skip)]
|
||||
status: StatusCode,
|
||||
props: Vec<DavItem>,
|
||||
}
|
||||
|
||||
impl DavMultiStatus {
|
||||
pub fn properties(&self, href: &str) -> DavPropertyResult<'_> {
|
||||
@@ -752,7 +756,7 @@ impl DavPropertyResult<'_> {
|
||||
pub fn get(&self, name: impl AsRef<str>) -> DavQueryResult<'_> {
|
||||
let name = name.as_ref();
|
||||
self.properties
|
||||
.0
|
||||
.props
|
||||
.iter()
|
||||
.find_map(|prop| {
|
||||
prop.values.get(name).map(|values| DavQueryResult {
|
||||
@@ -765,15 +769,26 @@ impl DavPropertyResult<'_> {
|
||||
self.response.dump_response();
|
||||
panic!(
|
||||
"No property found for name: {name} in {}",
|
||||
serde_json::to_string_pretty(&self.properties.0).unwrap()
|
||||
serde_json::to_string_pretty(&self.properties.props).unwrap()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_status(&self, status: StatusCode) -> &Self {
|
||||
if self.properties.status != status {
|
||||
self.response.dump_response();
|
||||
panic!(
|
||||
"Expected status {status}, but got {}",
|
||||
self.properties.status
|
||||
);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_defined(&self, name: impl AsRef<str>) -> &Self {
|
||||
if self
|
||||
.properties
|
||||
.0
|
||||
.props
|
||||
.iter()
|
||||
.any(|prop| prop.values.contains_key(name.as_ref()))
|
||||
{
|
||||
@@ -787,7 +802,7 @@ impl DavPropertyResult<'_> {
|
||||
pub fn is_undefined(&self, name: impl AsRef<str>) -> &Self {
|
||||
if self
|
||||
.properties
|
||||
.0
|
||||
.props
|
||||
.iter()
|
||||
.any(|prop| prop.values.contains_key(name.as_ref()))
|
||||
{
|
||||
@@ -931,6 +946,7 @@ impl DavResponse {
|
||||
hrefs: AHashMap::new(),
|
||||
};
|
||||
let mut href = None;
|
||||
let mut href_status = StatusCode::OK;
|
||||
let mut props = Vec::new();
|
||||
let mut prop = DavItem::default();
|
||||
|
||||
@@ -941,12 +957,25 @@ impl DavResponse {
|
||||
if !prop.is_empty() {
|
||||
props.push(std::mem::take(&mut prop));
|
||||
}
|
||||
result
|
||||
.hrefs
|
||||
.insert(href, DavProperties(std::mem::take(&mut props)));
|
||||
result.hrefs.insert(
|
||||
href,
|
||||
DavProperties {
|
||||
status: href_status,
|
||||
props: std::mem::take(&mut props),
|
||||
},
|
||||
);
|
||||
href_status = StatusCode::OK;
|
||||
}
|
||||
href = Some(value.to_string());
|
||||
}
|
||||
"D:multistatus.D:response.D:status" => {
|
||||
href_status = value
|
||||
.split_ascii_whitespace()
|
||||
.nth(1)
|
||||
.unwrap_or_default()
|
||||
.parse()
|
||||
.unwrap();
|
||||
}
|
||||
"D:multistatus.D:response.D:propstat.D:status" => {
|
||||
prop.status = value
|
||||
.split_ascii_whitespace()
|
||||
@@ -958,7 +987,6 @@ impl DavResponse {
|
||||
"D:multistatus.D:response.D:propstat.D:responsedescription" => {
|
||||
prop.description = Some(value.to_string());
|
||||
}
|
||||
|
||||
_ => {
|
||||
if let Some(prop_name) =
|
||||
key.strip_prefix("D:multistatus.D:response.D:propstat.D:prop.")
|
||||
@@ -990,7 +1018,13 @@ impl DavResponse {
|
||||
if !prop.is_empty() {
|
||||
props.push(prop);
|
||||
}
|
||||
result.hrefs.insert(href, DavProperties(props));
|
||||
result.hrefs.insert(
|
||||
href,
|
||||
DavProperties {
|
||||
status: href_status,
|
||||
props,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
result
|
||||
|
||||
@@ -223,15 +223,68 @@ pub async fn test(test: &WebDavTest) {
|
||||
}
|
||||
assert!(expected_changes.is_empty(), "{:?}", expected_changes);
|
||||
|
||||
// Test 10: Expect changes after deletion
|
||||
client
|
||||
.request("DELETE", &folder_name, "")
|
||||
.request("DELETE", &new_file, "")
|
||||
.await
|
||||
.with_status(StatusCode::NO_CONTENT);
|
||||
|
||||
let response = client
|
||||
.sync_collection(
|
||||
&user_base_path,
|
||||
&sync_token,
|
||||
Depth::Infinity,
|
||||
None,
|
||||
["D:getetag"],
|
||||
)
|
||||
.await;
|
||||
sync_token = response.sync_token().to_string();
|
||||
response
|
||||
.with_href_count(1)
|
||||
.with_value("D:multistatus.D:response.D:href", &new_file)
|
||||
.with_value(
|
||||
"D:multistatus.D:response.D:status",
|
||||
"HTTP/1.1 404 Not Found",
|
||||
);
|
||||
client
|
||||
.request("DELETE", &new_collection, "")
|
||||
.await
|
||||
.with_status(StatusCode::NO_CONTENT);
|
||||
let response = client
|
||||
.sync_collection(
|
||||
&user_base_path,
|
||||
&sync_token,
|
||||
Depth::Infinity,
|
||||
None,
|
||||
["D:getetag"],
|
||||
)
|
||||
.await;
|
||||
sync_token = response.sync_token().to_string();
|
||||
response
|
||||
.with_href_count(1)
|
||||
.with_value("D:multistatus.D:response.D:href", &new_collection)
|
||||
.with_value(
|
||||
"D:multistatus.D:response.D:status",
|
||||
"HTTP/1.1 404 Not Found",
|
||||
);
|
||||
client
|
||||
.request("DELETE", &folder_name, "")
|
||||
.await
|
||||
.with_status(StatusCode::NO_CONTENT);
|
||||
client
|
||||
.sync_collection(
|
||||
&user_base_path,
|
||||
&sync_token,
|
||||
Depth::Infinity,
|
||||
None,
|
||||
["D:getetag"],
|
||||
)
|
||||
.await
|
||||
.with_href_count(1)
|
||||
.with_value("D:multistatus.D:response.D:href", &folder_name)
|
||||
.with_value(
|
||||
"D:multistatus.D:response.D:status",
|
||||
"HTTP/1.1 404 Not Found",
|
||||
);
|
||||
}
|
||||
|
||||
client.delete_default_containers().await;
|
||||
|
||||
Reference in New Issue
Block a user