From 5a8bf5a902c9b4ab2f0ec1f40d0d95b9bbacbdc4 Mon Sep 17 00:00:00 2001 From: Hocuri Date: Mon, 10 Mar 2025 18:10:39 +0100 Subject: [PATCH 1/2] fix: Don't panic if the server returns 0 as the quota limit --- src/types/quota.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/types/quota.rs b/src/types/quota.rs index 3df3328..3526596 100644 --- a/src/types/quota.rs +++ b/src/types/quota.rs @@ -48,7 +48,11 @@ impl From> for QuotaResource { impl QuotaResource { /// Returns the usage percentage of a QuotaResource. pub fn get_usage_percentage(&self) -> u64 { - self.usage.saturating_mul(100) / self.limit + self.usage + .saturating_mul(100) + .checked_div(self.limit) + // Assume that if `limit` is 0, this means that storage is unlimited: + .unwrap_or(0) } } From 6de32949ae826744176abcc43148ee30d47cf924 Mon Sep 17 00:00:00 2001 From: Hocuri Date: Mon, 10 Mar 2025 22:47:01 +0100 Subject: [PATCH 2/2] Add test --- src/client.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/client.rs b/src/client.rs index b4fa1be..c0f5d65 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2537,6 +2537,78 @@ mod tests { } } + #[cfg_attr(feature = "runtime-tokio", tokio::test)] + #[cfg_attr(feature = "runtime-async-std", async_std::test)] + async fn test_get_quota_root() { + { + let response = b"* QUOTAROOT Sent Userquota\r\n\ + * QUOTA Userquota (STORAGE 4855 48576)\r\n\ + A0001 OK Getquotaroot completed (0.004 + 0.000 + 0.004 secs).\r\n" + .to_vec(); + + let mock_stream = MockStream::new(response); + let mut session = mock_session!(mock_stream); + let (quotaroots, quota) = dbg!(session.get_quota_root("Sent").await.unwrap()); + assert_eq!( + str::from_utf8(&session.stream.inner.written_buf).unwrap(), + "A0001 GETQUOTAROOT \"Sent\"\r\n" + ); + assert_eq!( + quotaroots, + vec![QuotaRoot { + mailbox_name: "Sent".to_string(), + quota_root_names: vec!["Userquota".to_string(),], + },], + ); + assert_eq!( + quota, + vec![Quota { + root_name: "Userquota".to_string(), + resources: vec![QuotaResource { + name: QuotaResourceName::Storage, + usage: 4855, + limit: 48576, + }], + }] + ); + assert_eq!(quota[0].resources[0].get_usage_percentage(), 9); + } + + { + let response = b"* QUOTAROOT \"INBOX\" \"#19\"\r\n\ + * QUOTA \"#19\" (STORAGE 0 0)\r\n\ + A0001 OK GETQUOTAROOT successful.\r\n" + .to_vec(); + + let mock_stream = MockStream::new(response); + let mut session = mock_session!(mock_stream); + let (quotaroots, quota) = session.get_quota_root("INBOX").await.unwrap(); + assert_eq!( + str::from_utf8(&session.stream.inner.written_buf).unwrap(), + "A0001 GETQUOTAROOT \"INBOX\"\r\n" + ); + assert_eq!( + quotaroots, + vec![QuotaRoot { + mailbox_name: "INBOX".to_string(), + quota_root_names: vec!["#19".to_string(),], + },], + ); + assert_eq!( + quota, + vec![Quota { + root_name: "#19".to_string(), + resources: vec![QuotaResource { + name: QuotaResourceName::Storage, + usage: 0, + limit: 0, + }], + }] + ); + assert_eq!(quota[0].resources[0].get_usage_percentage(), 0); + } + } + #[cfg_attr(feature = "runtime-tokio", tokio::test)] #[cfg_attr(feature = "runtime-async-std", async_std::test)] async fn test_parsing_error() {