v0.4.2
This commit is contained in:
509
tests/src/jmap/blob.rs
Normal file
509
tests/src/jmap/blob.rs
Normal file
@@ -0,0 +1,509 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Stalwart Labs Ltd.
|
||||
*
|
||||
* This file is part of Stalwart Mail Server.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
* in the LICENSE file at the top-level directory of this distribution.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* You can be released from the requirements of the AGPLv3 license by
|
||||
* purchasing a commercial license. Please contact licensing@stalw.art
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use jmap::{mailbox::INBOX_ID, JMAP};
|
||||
use jmap_client::client::Client;
|
||||
use jmap_proto::types::id::Id;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{
|
||||
directory::sql::create_test_user_with_email,
|
||||
jmap::{jmap_json_request, mailbox::destroy_all_mailboxes},
|
||||
};
|
||||
|
||||
pub async fn test(server: Arc<JMAP>, admin_client: &mut Client) {
|
||||
println!("Running blob tests...");
|
||||
let directory = server.directory.as_ref();
|
||||
create_test_user_with_email(directory, "jdoe@example.com", "12345", "John Doe").await;
|
||||
let account_id = Id::from(server.get_account_id("jdoe@example.com").await.unwrap());
|
||||
|
||||
server
|
||||
.store
|
||||
.delete_account_blobs(account_id.document_id())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Blob/set simple test
|
||||
let response = jmap_json_request(
|
||||
r#"[[
|
||||
"Blob/upload",
|
||||
{
|
||||
"accountId": "$$",
|
||||
"create": {
|
||||
"abc": {
|
||||
"data" : [
|
||||
{
|
||||
"data:asBase64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEX/AAAZ4gk3AAAAAXRSTlN/gFy0ywAAAApJREFUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII="
|
||||
}
|
||||
],
|
||||
"type": "image/png"
|
||||
}
|
||||
}
|
||||
},
|
||||
"R1"
|
||||
]]"#
|
||||
.replace("$$", &account_id.to_string()),
|
||||
"jdoe@example.com",
|
||||
"12345",
|
||||
)
|
||||
.await;
|
||||
assert_eq!(
|
||||
response
|
||||
.pointer("/methodResponses/0/1/created/abc/type")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or_default(),
|
||||
"image/png",
|
||||
"Response: {:?}",
|
||||
response
|
||||
);
|
||||
assert_eq!(
|
||||
response
|
||||
.pointer("/methodResponses/0/1/created/abc/size")
|
||||
.and_then(|v| v.as_i64())
|
||||
.unwrap_or_default(),
|
||||
95,
|
||||
"Response: {:?}",
|
||||
response
|
||||
);
|
||||
|
||||
// Blob/get simple test
|
||||
let blob_id = jmap_json_request(
|
||||
r#"[[
|
||||
"Blob/upload",
|
||||
{
|
||||
"accountId": "$$",
|
||||
"create": {
|
||||
"abc": {
|
||||
"data" : [
|
||||
{
|
||||
"data:asText": "The quick brown fox jumped over the lazy dog."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"R1"
|
||||
]]"#
|
||||
.replace("$$", &account_id.to_string()),
|
||||
"jdoe@example.com",
|
||||
"12345",
|
||||
)
|
||||
.await
|
||||
.pointer("/methodResponses/0/1/created/abc/id")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
let response = jmap_json_request(
|
||||
r#"[
|
||||
[
|
||||
"Blob/get",
|
||||
{
|
||||
"accountId" : "$$",
|
||||
"ids" : [
|
||||
"%%"
|
||||
],
|
||||
"properties" : [
|
||||
"data:asText",
|
||||
"digest:sha",
|
||||
"size"
|
||||
]
|
||||
},
|
||||
"R1"
|
||||
],
|
||||
[
|
||||
"Blob/get",
|
||||
{
|
||||
"accountId" : "$$",
|
||||
"ids" : [
|
||||
"%%"
|
||||
],
|
||||
"properties" : [
|
||||
"data:asText",
|
||||
"digest:sha",
|
||||
"digest:sha-256",
|
||||
"size"
|
||||
],
|
||||
"offset" : 4,
|
||||
"length" : 9
|
||||
},
|
||||
"R2"
|
||||
]
|
||||
]"#
|
||||
.replace("$$", &account_id.to_string())
|
||||
.replace("%%", &blob_id),
|
||||
"jdoe@example.com",
|
||||
"12345",
|
||||
)
|
||||
.await;
|
||||
|
||||
for (pointer, expected) in [
|
||||
(
|
||||
"/methodResponses/0/1/list/0/data:asText",
|
||||
"The quick brown fox jumped over the lazy dog.",
|
||||
),
|
||||
(
|
||||
"/methodResponses/0/1/list/0/digest:sha",
|
||||
"wIVPufsDxBzOOALLDSIFKebu+U4=",
|
||||
),
|
||||
("/methodResponses/0/1/list/0/size", "45"),
|
||||
("/methodResponses/1/1/list/0/data:asText", "quick bro"),
|
||||
(
|
||||
"/methodResponses/1/1/list/0/digest:sha",
|
||||
"QiRAPtfyX8K6tm1iOAtZ87Xj3Ww=",
|
||||
),
|
||||
(
|
||||
"/methodResponses/1/1/list/0/digest:sha-256",
|
||||
"gdg9INW7lwHK6OQ9u0dwDz2ZY/gubi0En0xlFpKt0OA=",
|
||||
),
|
||||
] {
|
||||
assert_eq!(
|
||||
response
|
||||
.pointer(pointer)
|
||||
.and_then(|v| match v {
|
||||
Value::String(s) => Some(s.to_string()),
|
||||
Value::Number(n) => Some(n.to_string()),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
expected,
|
||||
"Pointer {pointer:?} Response: {response:?}",
|
||||
);
|
||||
}
|
||||
|
||||
server
|
||||
.store
|
||||
.delete_account_blobs(account_id.document_id())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Blob/upload Complex Example
|
||||
let response = jmap_json_request(
|
||||
r##"[
|
||||
[
|
||||
"Blob/upload",
|
||||
{
|
||||
"accountId" : "$$",
|
||||
"create": {
|
||||
"b4": {
|
||||
"data": [
|
||||
{
|
||||
"data:asText": "The quick brown fox jumped over the lazy dog."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"S4"
|
||||
],
|
||||
[
|
||||
"Blob/upload",
|
||||
{
|
||||
"accountId" : "$$",
|
||||
"create": {
|
||||
"cat": {
|
||||
"data": [
|
||||
{
|
||||
"data:asText": "How"
|
||||
},
|
||||
{
|
||||
"blobId": "#b4",
|
||||
"length": 7,
|
||||
"offset": 3
|
||||
},
|
||||
{
|
||||
"data:asText": "was t"
|
||||
},
|
||||
{
|
||||
"blobId": "#b4",
|
||||
"length": 1,
|
||||
"offset": 1
|
||||
},
|
||||
{
|
||||
"data:asBase64": "YXQ/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"CAT"
|
||||
],
|
||||
[
|
||||
"Blob/get",
|
||||
{
|
||||
"accountId" : "$$",
|
||||
"properties": [
|
||||
"data:asText",
|
||||
"size"
|
||||
],
|
||||
"ids": [
|
||||
"#cat"
|
||||
]
|
||||
},
|
||||
"G4"
|
||||
]
|
||||
]"##
|
||||
.replace("$$", &account_id.to_string()),
|
||||
"jdoe@example.com",
|
||||
"12345",
|
||||
)
|
||||
.await;
|
||||
|
||||
for (pointer, expected) in [
|
||||
(
|
||||
"/methodResponses/2/1/list/0/data:asText",
|
||||
"How quick was that?",
|
||||
),
|
||||
("/methodResponses/2/1/list/0/size", "19"),
|
||||
] {
|
||||
assert_eq!(
|
||||
response
|
||||
.pointer(pointer)
|
||||
.and_then(|v| match v {
|
||||
Value::String(s) => Some(s.to_string()),
|
||||
Value::Number(n) => Some(n.to_string()),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
expected,
|
||||
"Pointer {pointer:?} Response: {response:?}",
|
||||
);
|
||||
}
|
||||
server
|
||||
.store
|
||||
.delete_account_blobs(account_id.document_id())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Blob/get Example with Range and Encoding Errors
|
||||
let response = jmap_json_request(
|
||||
r##"[
|
||||
[
|
||||
"Blob/upload",
|
||||
{
|
||||
"accountId" : "$$",
|
||||
"create": {
|
||||
"b1": {
|
||||
"data": [
|
||||
{
|
||||
"data:asBase64": "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUggYEgZG9nLg=="
|
||||
}
|
||||
]
|
||||
},
|
||||
"b2": {
|
||||
"data": [
|
||||
{
|
||||
"data:asText": "hello world"
|
||||
}
|
||||
],
|
||||
"type" : "text/plain"
|
||||
}
|
||||
}
|
||||
},
|
||||
"S1"
|
||||
],
|
||||
[
|
||||
"Blob/get",
|
||||
{
|
||||
"accountId" : "$$",
|
||||
"ids": [
|
||||
"#b1",
|
||||
"#b2"
|
||||
]
|
||||
},
|
||||
"G1"
|
||||
],
|
||||
[
|
||||
"Blob/get",
|
||||
{
|
||||
"accountId" : "$$",
|
||||
"ids": [
|
||||
"#b1",
|
||||
"#b2"
|
||||
],
|
||||
"properties": [
|
||||
"data:asText",
|
||||
"size"
|
||||
]
|
||||
},
|
||||
"G2"
|
||||
],
|
||||
[
|
||||
"Blob/get",
|
||||
{
|
||||
"accountId" : "$$",
|
||||
"ids": [
|
||||
"#b1",
|
||||
"#b2"
|
||||
],
|
||||
"properties": [
|
||||
"data:asBase64",
|
||||
"size"
|
||||
]
|
||||
},
|
||||
"G3"
|
||||
],
|
||||
[
|
||||
"Blob/get",
|
||||
{
|
||||
"accountId" : "$$",
|
||||
"offset": 0,
|
||||
"length": 5,
|
||||
"ids": [
|
||||
"#b1",
|
||||
"#b2"
|
||||
]
|
||||
},
|
||||
"G4"
|
||||
],
|
||||
[
|
||||
"Blob/get",
|
||||
{
|
||||
"accountId" : "$$",
|
||||
"offset": 20,
|
||||
"length": 100,
|
||||
"ids": [
|
||||
"#b1",
|
||||
"#b2"
|
||||
]
|
||||
},
|
||||
"G5"
|
||||
]
|
||||
]"##
|
||||
.replace("$$", &account_id.to_string()),
|
||||
"jdoe@example.com",
|
||||
"12345",
|
||||
)
|
||||
.await;
|
||||
|
||||
for (pointer, expected) in [
|
||||
(
|
||||
"/methodResponses/1/1/list/0/data:asBase64",
|
||||
"VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUggYEgZG9nLg==",
|
||||
),
|
||||
("/methodResponses/1/1/list/1/data:asText", "hello world"),
|
||||
("/methodResponses/2/1/list/0/isEncodingProblem", "true"),
|
||||
("/methodResponses/2/1/list/1/data:asText", "hello world"),
|
||||
(
|
||||
"/methodResponses/3/1/list/0/data:asBase64",
|
||||
"VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUggYEgZG9nLg==",
|
||||
),
|
||||
(
|
||||
"/methodResponses/3/1/list/1/data:asBase64",
|
||||
"aGVsbG8gd29ybGQ=",
|
||||
),
|
||||
("/methodResponses/4/1/list/0/data:asText", "The q"),
|
||||
("/methodResponses/4/1/list/1/data:asText", "hello"),
|
||||
("/methodResponses/5/1/list/0/isEncodingProblem", "true"),
|
||||
("/methodResponses/5/1/list/0/isTruncated", "true"),
|
||||
("/methodResponses/5/1/list/1/isTruncated", "true"),
|
||||
] {
|
||||
assert_eq!(
|
||||
response
|
||||
.pointer(pointer)
|
||||
.and_then(|v| match v {
|
||||
Value::String(s) => Some(s.to_string()),
|
||||
Value::Number(n) => Some(n.to_string()),
|
||||
Value::Bool(b) => Some(b.to_string()),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
expected,
|
||||
"Pointer {pointer:?} Response: {response:?}",
|
||||
);
|
||||
}
|
||||
server
|
||||
.store
|
||||
.delete_account_blobs(account_id.document_id())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Blob/lookup
|
||||
admin_client.set_default_account_id(account_id.to_string());
|
||||
let blob_id = admin_client
|
||||
.email_import(
|
||||
concat!(
|
||||
"From: bill@example.com\r\n",
|
||||
"To: jdoe@example.com\r\n",
|
||||
"Subject: TPS Report\r\n",
|
||||
"\r\n",
|
||||
"I'm going to need those TPS reports ASAP. ",
|
||||
"So, if you could do that, that'd be great."
|
||||
)
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
[&Id::from(INBOX_ID).to_string()],
|
||||
None::<Vec<&str>>,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.take_blob_id();
|
||||
|
||||
let response = jmap_json_request(
|
||||
r#"[[
|
||||
"Blob/lookup",
|
||||
{
|
||||
"accountId" : "$$",
|
||||
"typeNames": [
|
||||
"Mailbox",
|
||||
"Thread",
|
||||
"Email"
|
||||
],
|
||||
"ids": [
|
||||
"%%",
|
||||
"not-a-blob"
|
||||
]
|
||||
},
|
||||
"R1"
|
||||
]]"#
|
||||
.replace("$$", &account_id.to_string())
|
||||
.replace("%%", &blob_id),
|
||||
"jdoe@example.com",
|
||||
"12345",
|
||||
)
|
||||
.await;
|
||||
|
||||
for pointer in [
|
||||
"/methodResponses/0/1/list/0/matchedIds/Email",
|
||||
"/methodResponses/0/1/list/0/matchedIds/Mailbox",
|
||||
"/methodResponses/0/1/list/0/matchedIds/Thread",
|
||||
] {
|
||||
assert_eq!(
|
||||
response
|
||||
.pointer(pointer)
|
||||
.and_then(|v| v.as_array())
|
||||
.map(|arr| arr.len())
|
||||
.unwrap_or_default(),
|
||||
1,
|
||||
"Pointer {pointer:?} Response: {response:?}",
|
||||
);
|
||||
}
|
||||
|
||||
// Remove test data
|
||||
admin_client.set_default_account_id(account_id.to_string());
|
||||
destroy_all_mailboxes(admin_client).await;
|
||||
server.store.assert_is_empty().await;
|
||||
}
|
||||
@@ -23,10 +23,12 @@
|
||||
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use directory::config::ConfigDirectory;
|
||||
use jmap::{api::JmapSessionManager, services::IPC_CHANNEL_BUFFER, JMAP};
|
||||
use jmap_client::client::{Client, Credentials};
|
||||
use jmap_proto::types::id::Id;
|
||||
use reqwest::header;
|
||||
use smtp::core::{SmtpSessionManager, SMTP};
|
||||
use tokio::sync::{mpsc, watch};
|
||||
use utils::{config::ServerProtocol, UnwrapFailure};
|
||||
@@ -40,6 +42,7 @@ use crate::{
|
||||
pub mod auth_acl;
|
||||
pub mod auth_limits;
|
||||
pub mod auth_oauth;
|
||||
pub mod blob;
|
||||
pub mod crypto;
|
||||
pub mod delivery;
|
||||
pub mod email_changes;
|
||||
@@ -219,12 +222,12 @@ refresh-token-renew = "2s"
|
||||
|
||||
#[tokio::test]
|
||||
pub async fn jmap_tests() {
|
||||
tracing::subscriber::set_global_default(
|
||||
/*tracing::subscriber::set_global_default(
|
||||
tracing_subscriber::FmtSubscriber::builder()
|
||||
.with_max_level(tracing::Level::WARN)
|
||||
.finish(),
|
||||
)
|
||||
.unwrap();
|
||||
.unwrap();*/
|
||||
|
||||
let delete = true;
|
||||
let mut params = init_jmap_tests(delete).await;
|
||||
@@ -251,6 +254,7 @@ pub async fn jmap_tests() {
|
||||
websocket::test(params.server.clone(), &mut params.client).await;
|
||||
quota::test(params.server.clone(), &mut params.client).await;
|
||||
crypto::test(params.server.clone(), &mut params.client).await;
|
||||
blob::test(params.server.clone(), &mut params.client).await;
|
||||
|
||||
if delete {
|
||||
params.temp_dir.delete();
|
||||
@@ -338,6 +342,51 @@ async fn init_jmap_tests(delete_if_exists: bool) -> JMAPTest {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn jmap_raw_request(body: impl AsRef<str>, username: &str, secret: &str) -> String {
|
||||
let mut headers = header::HeaderMap::new();
|
||||
|
||||
headers.insert(
|
||||
header::AUTHORIZATION,
|
||||
header::HeaderValue::from_str(&format!(
|
||||
"Basic {}",
|
||||
general_purpose::STANDARD.encode(format!("{}:{}", username, secret))
|
||||
))
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
const BODY_TEMPLATE: &str = r#"{
|
||||
"using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:ietf:params:jmap:quota" ],
|
||||
"methodCalls": $$
|
||||
}"#;
|
||||
|
||||
String::from_utf8(
|
||||
reqwest::Client::builder()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.timeout(Duration::from_millis(1000))
|
||||
.default_headers(headers)
|
||||
.build()
|
||||
.unwrap()
|
||||
.post("https://127.0.0.1:8899/jmap")
|
||||
.body(BODY_TEMPLATE.replace("$$", body.as_ref()))
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.bytes()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_vec(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub async fn jmap_json_request(
|
||||
body: impl AsRef<str>,
|
||||
username: &str,
|
||||
secret: &str,
|
||||
) -> serde_json::Value {
|
||||
serde_json::from_str(&jmap_raw_request(body, username, secret).await).unwrap()
|
||||
}
|
||||
|
||||
pub fn find_values(string: &str, name: &str) -> Vec<String> {
|
||||
let mut last_pos = 0;
|
||||
let mut values = Vec::new();
|
||||
|
||||
@@ -44,7 +44,7 @@ use jmap::{
|
||||
JMAP,
|
||||
};
|
||||
use jmap_client::{client::Client, mailbox::Role, push_subscription::Keys};
|
||||
use jmap_proto::types::{id::Id, type_state::TypeState};
|
||||
use jmap_proto::types::{id::Id, type_state::DataType};
|
||||
use reqwest::header::CONTENT_ENCODING;
|
||||
use store::ahash::AHashSet;
|
||||
use tokio::{net::TcpStream, sync::mpsc};
|
||||
@@ -138,7 +138,7 @@ pub async fn test(server: Arc<JMAP>, admin_client: &mut Client) {
|
||||
.unwrap()
|
||||
.take_id();
|
||||
|
||||
assert_state(&mut event_rx, &account_id, &[TypeState::Mailbox]).await;
|
||||
assert_state(&mut event_rx, &account_id, &[DataType::Mailbox]).await;
|
||||
|
||||
// Receive states just for the requested types
|
||||
client
|
||||
@@ -192,14 +192,14 @@ pub async fn test(server: Arc<JMAP>, admin_client: &mut Client) {
|
||||
.unwrap();
|
||||
tokio::time::sleep(Duration::from_millis(200)).await;
|
||||
push_server.fail_requests.store(false, Ordering::Relaxed);
|
||||
assert_state(&mut event_rx, &account_id, &[TypeState::Mailbox]).await;
|
||||
assert_state(&mut event_rx, &account_id, &[DataType::Mailbox]).await;
|
||||
|
||||
// Make a mailbox change and expect state change
|
||||
client
|
||||
.mailbox_rename(&mailbox_id, "My Mailbox")
|
||||
.await
|
||||
.unwrap();
|
||||
assert_state(&mut event_rx, &account_id, &[TypeState::Mailbox]).await;
|
||||
assert_state(&mut event_rx, &account_id, &[DataType::Mailbox]).await;
|
||||
//expect_nothing(&mut event_rx).await;
|
||||
|
||||
// Multiple change updates should be grouped and pushed in intervals
|
||||
@@ -209,7 +209,7 @@ pub async fn test(server: Arc<JMAP>, admin_client: &mut Client) {
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
assert_state(&mut event_rx, &account_id, &[TypeState::Mailbox]).await;
|
||||
assert_state(&mut event_rx, &account_id, &[DataType::Mailbox]).await;
|
||||
expect_nothing(&mut event_rx).await;
|
||||
|
||||
// Destroy mailbox
|
||||
@@ -365,7 +365,7 @@ async fn expect_nothing(event_rx: &mut mpsc::Receiver<PushMessage>) {
|
||||
}
|
||||
}
|
||||
|
||||
async fn assert_state(event_rx: &mut mpsc::Receiver<PushMessage>, id: &Id, state: &[TypeState]) {
|
||||
async fn assert_state(event_rx: &mut mpsc::Receiver<PushMessage>, id: &Id, state: &[DataType]) {
|
||||
assert_eq!(
|
||||
expect_push(event_rx)
|
||||
.await
|
||||
@@ -375,8 +375,8 @@ async fn assert_state(event_rx: &mut mpsc::Receiver<PushMessage>, id: &Id, state
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|x| x.0)
|
||||
.collect::<AHashSet<&TypeState>>(),
|
||||
state.iter().collect::<AHashSet<&TypeState>>()
|
||||
.collect::<AHashSet<&DataType>>(),
|
||||
state.iter().collect::<AHashSet<&DataType>>()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,10 @@ use jmap_proto::types::{collection::Collection, id::Id};
|
||||
|
||||
use crate::{
|
||||
directory::sql::{add_to_group, create_test_user_with_email, set_test_quota},
|
||||
jmap::{delivery::SmtpConnection, mailbox::destroy_all_mailboxes, test_account_login},
|
||||
jmap::{
|
||||
delivery::SmtpConnection, jmap_raw_request, mailbox::destroy_all_mailboxes,
|
||||
test_account_login,
|
||||
},
|
||||
};
|
||||
|
||||
pub async fn test(server: Arc<JMAP>, admin_client: &mut Client) {
|
||||
@@ -110,6 +113,26 @@ pub async fn test(server: Arc<JMAP>, admin_client: &mut Client) {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Test JMAP Quotas extension
|
||||
let response = jmap_raw_request(
|
||||
r#"[[ "Quota/get", {
|
||||
"accountId": "$$",
|
||||
"ids": null
|
||||
}, "0" ]]"#
|
||||
.replace("$$", &account_id.to_string()),
|
||||
"robert@example.com",
|
||||
"aabbcc",
|
||||
)
|
||||
.await;
|
||||
assert!(response.contains("\"used\":0"), "{}", response);
|
||||
assert!(response.contains("\"hardLimit\":1024"), "{}", response);
|
||||
assert!(response.contains("\"scope\":\"account\""), "{}", response);
|
||||
assert!(
|
||||
response.contains("\"name\":\"robert@example.com\""),
|
||||
"{}",
|
||||
response
|
||||
);
|
||||
|
||||
// Test Email/import quota
|
||||
let inbox_id = Id::new(INBOX_ID as u64).to_string();
|
||||
let mut message_ids = Vec::new();
|
||||
@@ -143,6 +166,20 @@ pub async fn test(server: Arc<JMAP>, admin_client: &mut Client) {
|
||||
.await,
|
||||
);
|
||||
|
||||
// Test JMAP Quotas extension
|
||||
let response = jmap_raw_request(
|
||||
r#"[[ "Quota/get", {
|
||||
"accountId": "$$",
|
||||
"ids": null
|
||||
}, "0" ]]"#
|
||||
.replace("$$", &account_id.to_string()),
|
||||
"robert@example.com",
|
||||
"aabbcc",
|
||||
)
|
||||
.await;
|
||||
assert!(response.contains("\"used\":1024"), "{}", response);
|
||||
assert!(response.contains("\"hardLimit\":1024"), "{}", response);
|
||||
|
||||
// Delete messages and check available quota
|
||||
for message_id in message_ids {
|
||||
client.email_destroy(&message_id).await.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user