JMAP tests cleanup

This commit is contained in:
mdecimus
2025-10-12 13:03:54 +02:00
parent 5df5790210
commit 0936c21fbc
33 changed files with 851 additions and 1061 deletions

View File

@@ -16,6 +16,7 @@ use serde::{
de::{self, IgnoredAny, Visitor},
ser::SerializeMap,
};
use serde_json::Value;
use std::{collections::hash_map::Entry, fmt, str::FromStr};
use store::{
U32_LEN, U64_LEN,
@@ -1126,18 +1127,15 @@ impl<'de> serde::Deserialize<'de> for PrincipalSet {
let mut principal = PrincipalSet::default();
while let Some(key) = map.next_key::<&str>()? {
let key = PrincipalField::try_parse(key)
.or_else(|| {
if key == "id" {
// Ignored
Some(PrincipalField::UsedQuota)
} else {
None
}
})
.ok_or_else(|| {
serde::de::Error::custom(format!("invalid principal field: {}", key))
})?;
if key == "id" {
// Ignored
map.next_value::<IgnoredAny>()?;
continue;
}
let key = PrincipalField::try_parse(key).ok_or_else(|| {
serde::de::Error::custom(format!("invalid principal field: {}", key))
})?;
let value = match key {
PrincipalField::Name => {
@@ -1179,18 +1177,37 @@ impl<'de> serde::Deserialize<'de> for PrincipalSet {
| PrincipalField::EnabledPermissions
| PrincipalField::DisabledPermissions
| PrincipalField::Urls
| PrincipalField::ExternalMembers => {
match map.next_value::<StringOrMany>()? {
StringOrMany::One(v) => PrincipalValue::StringList(vec![v]),
StringOrMany::Many(v) => {
if !v.is_empty() {
PrincipalValue::StringList(v)
} else {
continue;
}
| PrincipalField::ExternalMembers => match map.next_value::<Value>()? {
Value::String(v) => {
if v.len() <= MAX_STRING_LEN {
PrincipalValue::StringList(vec![v])
} else {
return Err(serde::de::Error::custom("string too long"));
}
}
}
Value::Array(v) => {
if !v.is_empty() {
PrincipalValue::StringList(
v.into_iter()
.filter_map(|item| {
if let Value::String(s) = item {
if s.len() <= MAX_STRING_LEN {
Some(s)
} else {
None
}
} else {
None
}
})
.collect(),
)
} else {
continue;
}
}
_ => continue,
},
PrincipalField::UsedQuota => {
// consume and ignore
map.next_value::<IgnoredAny>()?;
@@ -1263,66 +1280,6 @@ impl<'de> serde::Deserialize<'de> for StringOrU64 {
}
}
#[derive(Debug)]
enum StringOrMany {
One(String),
Many(Vec<String>),
}
impl<'de> serde::Deserialize<'de> for StringOrMany {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct StringOrManyVisitor;
impl<'de> Visitor<'de> for StringOrManyVisitor {
type Value = StringOrMany;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string or a sequence of strings")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
if value.len() <= MAX_STRING_LEN {
Ok(StringOrMany::One(value.into()))
} else {
Err(serde::de::Error::custom("string too long"))
}
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: de::Error,
{
if v.len() <= MAX_STRING_LEN {
Ok(StringOrMany::One(v))
} else {
Err(serde::de::Error::custom("string too long"))
}
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let mut vec = Vec::new();
while let Some(value) = seq.next_element::<String>()? {
vec.push(value);
}
Ok(StringOrMany::Many(vec))
}
}
deserializer.deserialize_any(StringOrManyVisitor)
}
}
impl Permission {
pub fn all() -> impl Iterator<Item = Permission> {
(0..Permission::COUNT as u32).filter_map(Permission::from_id)

View File

@@ -417,6 +417,7 @@ pub enum Permission {
JmapParticipantIdentityGet,
JmapParticipantIdentitySet,
JmapParticipantIdentityChanges,
// TODO: Reuse DeleteSystemFolders position for new permission
// WARNING: add new ids at the end (TODO: use static ids)
}

View File

@@ -12,7 +12,6 @@ use crate::{
use common::{
Server, auth::AccessToken, sharing::EffectiveAcl, storage::index::ObjectIndexBuilder,
};
use directory::Permission;
use store::{roaring::RoaringBitmap, write::BatchBuilder};
use trc::AddContext;
use types::{acl::Acl, collection::Collection, field::MailboxField};
@@ -45,17 +44,8 @@ impl MailboxDestroy for Server {
remove_emails: bool,
) -> trc::Result<Result<Option<u64>, MailboxDestroyError>> {
// Internal folders cannot be deleted
#[cfg(feature = "test_mode")]
if [INBOX_ID, TRASH_ID].contains(&document_id)
&& !access_token.has_permission(Permission::DeleteSystemFolders)
{
return Ok(Err(MailboxDestroyError::CannotDestroy));
}
#[cfg(not(feature = "test_mode"))]
if [INBOX_ID, TRASH_ID, JUNK_ID].contains(&document_id)
&& !access_token.has_permission(Permission::DeleteSystemFolders)
{
if [INBOX_ID, TRASH_ID, JUNK_ID].contains(&document_id) {
return Ok(Err(MailboxDestroyError::CannotDestroy));
}

View File

@@ -11,7 +11,8 @@ use jmap_proto::{
types::state::State,
};
use jmap_tools::{Map, Value};
use std::future::Future;
use std::{future::Future, sync::Arc};
use trc::AddContext;
use types::{id::Id, type_state::DataType};
pub trait QuotaGet: Sync + Send {
@@ -59,6 +60,16 @@ impl QuotaGet for Server {
not_found: vec![],
};
let access_token = if account_id == access_token.primary_id() {
AccessTokenRef::Borrowed(access_token)
} else {
AccessTokenRef::Owned(
self.get_access_token(account_id)
.await
.caused_by(trc::location!())?,
)
};
for id in ids {
// Obtain the sieve script object
let document_id = id.document_id();
@@ -73,10 +84,11 @@ impl QuotaGet for Server {
QuotaProperty::Id => Value::Element(id.into()),
QuotaProperty::ResourceType => "octets".to_string().into(),
QuotaProperty::Used => (self.get_used_quota(account_id).await? as u64).into(),
QuotaProperty::HardLimit => access_token.quota.into(),
QuotaProperty::HardLimit => access_token.as_ref().quota.into(),
QuotaProperty::Scope => "account".to_string().into(),
QuotaProperty::Name => access_token.name.to_string().into(),
QuotaProperty::Name => access_token.as_ref().name.to_string().into(),
QuotaProperty::Description => access_token
.as_ref()
.description
.as_ref()
.map(|s| s.to_string())
@@ -84,6 +96,9 @@ impl QuotaGet for Server {
QuotaProperty::Types => vec![
Value::Element(QuotaValue::Types(DataType::Email)),
Value::Element(QuotaValue::Types(DataType::SieveScript)),
Value::Element(QuotaValue::Types(DataType::FileNode)),
Value::Element(QuotaValue::Types(DataType::CalendarEvent)),
Value::Element(QuotaValue::Types(DataType::ContactCard)),
]
.into(),
@@ -97,3 +112,17 @@ impl QuotaGet for Server {
Ok(response)
}
}
enum AccessTokenRef<'x> {
Owned(Arc<AccessToken>),
Borrowed(&'x AccessToken),
}
impl AccessTokenRef<'_> {
fn as_ref(&self) -> &AccessToken {
match self {
AccessTokenRef::Owned(token) => token,
AccessTokenRef::Borrowed(token) => token,
}
}
}

View File

@@ -777,6 +777,11 @@ pub trait TestInternalDirectory {
async fn create_test_list(&self, login: &str, name: &str, emails: &[&str]) -> u32;
async fn set_test_quota(&self, login: &str, quota: u32);
async fn add_permissions(&self, login: &str, permissions: impl IntoIterator<Item = Permission>);
async fn remove_permissions(
&self,
login: &str,
permissions: impl IntoIterator<Item = Permission>,
);
async fn add_to_group(&self, login: &str, group: &str) -> ChangedPrincipals;
async fn remove_from_group(&self, login: &str, group: &str) -> ChangedPrincipals;
async fn remove_test_alias(&self, login: &str, alias: &str);
@@ -815,6 +820,10 @@ impl TestInternalDirectory for Store {
PrincipalField::Roles,
PrincipalValue::String(role.into()),
),
PrincipalUpdate::add_item(
PrincipalField::EnabledPermissions,
PrincipalValue::String(Permission::UnlimitedRequests.name().into()),
),
]))
.await
.unwrap();
@@ -835,6 +844,12 @@ impl TestInternalDirectory for Store {
.with_field(
PrincipalField::Roles,
PrincipalValue::StringList(vec![role.into()]),
)
.with_field(
PrincipalField::EnabledPermissions,
PrincipalValue::StringList(vec![
Permission::UnlimitedRequests.name().into(),
]),
),
None,
None,
@@ -935,6 +950,28 @@ impl TestInternalDirectory for Store {
.unwrap();
}
async fn remove_permissions(
&self,
login: &str,
permissions: impl IntoIterator<Item = Permission>,
) {
self.update_principal(
UpdatePrincipal::by_name(login).with_updates(
permissions
.into_iter()
.map(|p| {
PrincipalUpdate::remove_item(
PrincipalField::EnabledPermissions,
PrincipalValue::String(p.name().to_string()),
)
})
.collect(),
),
)
.await
.unwrap();
}
async fn add_to_group(&self, login: &str, group: &str) -> ChangedPrincipals {
self.update_principal(UpdatePrincipal::by_name(login).with_updates(vec![
PrincipalUpdate::add_item(

View File

@@ -7,9 +7,10 @@
use crate::{
directory::internal::TestInternalDirectory,
imap::{ImapConnection, Type},
jmap::{JMAPTest, assert_is_empty, mail::mailbox::destroy_all_mailboxes},
jmap::{JMAPTest, assert_is_empty},
};
use common::listener::blocked::BLOCKED_IP_KEY;
use directory::Permission;
use imap_proto::ResponseType;
use jmap_client::{
client::{Client, Credentials},
@@ -22,27 +23,21 @@ use std::{
time::Duration,
};
use store::write::now;
use types::id::Id;
pub async fn test(params: &mut JMAPTest) {
println!("Running Authorization tests...");
// Create test account
let server = params.server.clone();
let account_id = Id::from(
server
.core
.storage
.data
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com", "john.doe@example.com"],
)
.await,
)
.to_string();
let account = params.account("jdoe@example.com");
// Remove unlimited requests permission
params
.server
.store()
.remove_permissions(account.name(), [Permission::UnlimitedRequests])
.await;
params.server.inner.cache.access_tokens.clear();
// Reset rate limiters
params.webhook.clear();
@@ -140,7 +135,7 @@ pub async fn test(params: &mut JMAPTest) {
// Valid authentication requests should not be rate limited
for _ in 0..110 {
Client::new()
.credentials(Credentials::basic("jdoe@example.com", "12345"))
.credentials(Credentials::basic(account.name(), account.secret()))
.accept_invalid_certs(true)
.follow_redirects(["127.0.0.1"])
.connect("https://127.0.0.1:8899")
@@ -150,18 +145,28 @@ pub async fn test(params: &mut JMAPTest) {
// Login with the correct credentials
let client = Client::new()
.credentials(Credentials::basic("jdoe@example.com", "12345"))
.credentials(Credentials::basic(account.name(), account.secret()))
.accept_invalid_certs(true)
.follow_redirects(["127.0.0.1"])
.connect("https://127.0.0.1:8899")
.await
.unwrap();
assert_eq!(client.session().username(), "jdoe@example.com");
assert_eq!(client.session().username(), account.name());
assert_eq!(
client.session().account(&account_id).unwrap().name(),
"jdoe@example.com"
client
.session()
.account(account.id_string())
.unwrap()
.name(),
account.name()
);
assert!(
client
.session()
.account(account.id_string())
.unwrap()
.is_personal()
);
assert!(client.session().account(&account_id).unwrap().is_personal());
// Uploads up to 5000000 bytes should be allowed
assert_eq!(
@@ -241,9 +246,16 @@ pub async fn test(params: &mut JMAPTest) {
client.upload(None, b"sleep".to_vec(), None).await,
Err(jmap_client::Error::Problem(err)) if err.status() == Some(400)));
// Add unlimited requests permission
params
.server
.store()
.add_permissions(account.name(), [Permission::UnlimitedRequests])
.await;
params.server.inner.cache.access_tokens.clear();
// Destroy test accounts
params.client.set_default_account_id(&account_id);
destroy_all_mailboxes(params).await;
params.destroy_all_mailboxes(account).await;
assert_is_empty(server).await;
// Check webhook events

View File

@@ -5,15 +5,11 @@
*/
use crate::{
directory::internal::TestInternalDirectory,
imap::{
ImapConnection, Type,
pop::{self, Pop3Connection},
},
jmap::{
JMAPTest, ManagementApi, assert_is_empty,
mail::{delivery::SmtpConnection, mailbox::destroy_all_mailboxes},
},
jmap::{JMAPTest, ManagementApi, assert_is_empty, mail::delivery::SmtpConnection},
};
use base64::{Engine, engine::general_purpose};
use biscuit::{JWT, SingleOrMultiple, jwk::JWKSet};
@@ -35,7 +31,6 @@ use jmap_client::{
use serde::{Serialize, de::DeserializeOwned};
use std::time::{Duration, Instant};
use store::ahash::AHashMap;
use types::id::Id;
#[derive(serde::Deserialize, Debug)]
#[allow(dead_code)]
@@ -50,18 +45,7 @@ pub async fn test(params: &mut JMAPTest) {
// Create test account
let server = params.server.clone();
let john_int_id = server
.core
.storage
.data
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await;
let john_id = Id::from(john_int_id).to_string();
let account = params.account("jdoe@example.com");
// Build API
let api = ManagementApi::new(8899, "jdoe@example.com", "12345");
@@ -145,7 +129,7 @@ pub async fn test(params: &mut JMAPTest) {
.connect("https://127.0.0.1:8899")
.await
.unwrap();
assert_eq!(john_client.default_account_id(), john_id);
assert_eq!(john_client.default_account_id(), account.id_string());
assert!(
!john_client
.mailbox_query(None::<Filter>, None::<Vec<_>>)
@@ -163,7 +147,10 @@ pub async fn test(params: &mut JMAPTest) {
let registered_claims = &claims.registered;
let private_claims = &claims.private;
assert_eq!(registered_claims.issuer, Some(oidc_metadata.issuer));
assert_eq!(registered_claims.subject, Some(john_int_id.to_string()));
assert_eq!(
registered_claims.subject,
Some(account.id().document_id().to_string())
);
assert_eq!(
registered_claims.audience,
Some(SingleOrMultiple::Single(client_id.to_string()))
@@ -324,7 +311,7 @@ pub async fn test(params: &mut JMAPTest) {
.connect("https://127.0.0.1:8899")
.await
.unwrap();
assert_eq!(john_client.default_account_id(), john_id);
assert_eq!(john_client.default_account_id(), account.id_string());
assert!(
!john_client
.mailbox_query(None::<Filter>, None::<Vec<_>>)
@@ -399,8 +386,7 @@ pub async fn test(params: &mut JMAPTest) {
.purge_in_memory_store()
.await
.unwrap();
params.client.set_default_account_id(john_id);
destroy_all_mailboxes(params).await;
params.destroy_all_mailboxes(account).await;
assert_is_empty(server).await;
}

View File

@@ -4,7 +4,10 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::jmap::{JMAPTest, ManagementApi, assert_is_empty, server::List};
use crate::{
directory::internal::TestInternalDirectory,
jmap::{JMAPTest, ManagementApi, assert_is_empty, server::List},
};
use ahash::AHashSet;
use common::auth::{AccessToken, TenantInfo};
use directory::{
@@ -19,6 +22,15 @@ pub async fn test(params: &JMAPTest) {
println!("Running permissions tests...");
let server = params.server.clone();
// Remove unlimited requests permission
for &account in params.accounts.keys() {
params
.server
.store()
.remove_permissions(account, [Permission::UnlimitedRequests])
.await;
}
// Prepare management API
let api = ManagementApi::new(8899, "admin", "secret");
@@ -144,7 +156,7 @@ pub async fn test(params: &JMAPTest) {
.await
.unwrap()
.unwrap_data()
.assert_count(6)
.assert_count(12)
.assert_exists(
"admin",
Type::Individual,

View File

@@ -6,11 +6,7 @@
use crate::{
directory::internal::TestInternalDirectory,
jmap::{
JMAPTest, assert_is_empty, emails_purge_tombstoned, jmap_raw_request,
mail::{delivery::SmtpConnection, mailbox::destroy_all_mailboxes},
test_account_login,
},
jmap::{JMAPTest, assert_is_empty, emails_purge_tombstoned, mail::delivery::SmtpConnection},
smtp::queue::QueuedEvents,
};
use common::config::smtp::queue::QueueName;
@@ -20,38 +16,16 @@ use jmap_client::{
core::set::{SetErrorType, SetObject},
email::EmailBodyPart,
};
use serde_json::json;
use smtp::queue::spool::SmtpSpool;
use types::{collection::Collection, id::Id};
pub async fn test(params: &mut JMAPTest) {
println!("Running quota tests...");
let server = params.server.clone();
let mut account_id = Id::from(0u64);
let mut other_account_id = Id::from(0u64);
for (id, email, password, name) in [
(
&mut other_account_id,
"jdoe@example.com",
"12345",
"John Doe",
),
(
&mut account_id,
"robert@example.com",
"aabbcc",
"Robert Foobar",
),
] {
*id = Id::from(
server
.core
.storage
.data
.create_test_user(email, password, name, &[email][..])
.await,
);
}
let account = params.account("robert@example.com");
let other_account = params.account("jdoe@example.com");
server
.core
@@ -65,13 +39,14 @@ pub async fn test(params: &mut JMAPTest) {
.data
.add_to_group("robert@example.com", "jdoe@example.com")
.await;
server.inner.cache.access_tokens.clear();
// Delete temporary blobs from previous tests
server.core.storage.data.blob_expire_all().await;
// Test temporary blob quota (3 files)
DISABLE_UPLOAD_QUOTA.store(false, std::sync::atomic::Ordering::Relaxed);
let client = test_account_login("robert@example.com", "aabbcc").await;
let client = account.client();
for i in 0..3 {
assert_eq!(
client
@@ -114,16 +89,15 @@ pub async fn test(params: &mut JMAPTest) {
server.core.storage.data.blob_expire_all().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;
let response = account
.jmap_method_call(
"Quota/get",
json!({
"ids": null
}),
)
.await
.to_string();
assert!(response.contains("\"used\":0"), "{}", response);
assert!(response.contains("\"hardLimit\":1024"), "{}", response);
assert!(response.contains("\"scope\":\"account\""), "{}", response);
@@ -168,16 +142,15 @@ pub async fn test(params: &mut JMAPTest) {
);
// 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;
let response = account
.jmap_method_call(
"Quota/get",
json!({
"ids": null
}),
)
.await
.to_string();
assert!(response.contains("\"used\":1024"), "{}", response);
assert!(response.contains("\"hardLimit\":1024"), "{}", response);
@@ -188,7 +161,7 @@ pub async fn test(params: &mut JMAPTest) {
emails_purge_tombstoned(&server).await;
assert_eq!(
server
.get_used_quota(account_id.document_id())
.get_used_quota(account.id().document_id())
.await
.unwrap(),
0
@@ -231,16 +204,16 @@ pub async fn test(params: &mut JMAPTest) {
// Recalculate quota
let prev_quota = server
.get_used_quota(account_id.document_id())
.get_used_quota(account.id().document_id())
.await
.unwrap();
server
.recalculate_quota(account_id.document_id())
.recalculate_quota(account.id().document_id())
.await
.unwrap();
assert_eq!(
server
.get_used_quota(account_id.document_id())
.get_used_quota(account.id().document_id())
.await
.unwrap(),
prev_quota
@@ -253,14 +226,14 @@ pub async fn test(params: &mut JMAPTest) {
emails_purge_tombstoned(&server).await;
assert_eq!(
server
.get_used_quota(account_id.document_id())
.get_used_quota(account.id().document_id())
.await
.unwrap(),
0
);
// Test Email/copy quota
let other_client = test_account_login("jdoe@example.com", "12345").await;
let other_client = other_account.client();
let mut other_message_ids = Vec::new();
let mut message_ids = Vec::new();
for i in 0..3 {
@@ -286,7 +259,7 @@ pub async fn test(params: &mut JMAPTest) {
message_ids.push(
client
.email_copy(
other_account_id.to_string(),
other_account.id_string(),
id,
vec![&inbox_id],
None::<Vec<String>>,
@@ -300,7 +273,7 @@ pub async fn test(params: &mut JMAPTest) {
assert_over_quota(
client
.email_copy(
other_account_id.to_string(),
other_account.id_string(),
&other_message_ids[2],
vec![&inbox_id],
None::<Vec<String>>,
@@ -316,7 +289,7 @@ pub async fn test(params: &mut JMAPTest) {
emails_purge_tombstoned(&server).await;
assert_eq!(
server
.get_used_quota(account_id.document_id())
.get_used_quota(account.id().document_id())
.await
.unwrap(),
0
@@ -339,13 +312,13 @@ pub async fn test(params: &mut JMAPTest) {
.await;
}
let quota = server
.get_used_quota(account_id.document_id())
.get_used_quota(account.id().document_id())
.await
.unwrap();
assert!(quota > 0 && quota <= 1024, "Quota is {}", quota);
assert_eq!(
server
.get_document_ids(account_id.document_id(), Collection::Email)
.get_document_ids(account.id().document_id(), Collection::Email)
.await
.unwrap()
.unwrap()
@@ -356,10 +329,9 @@ pub async fn test(params: &mut JMAPTest) {
DISABLE_UPLOAD_QUOTA.store(true, std::sync::atomic::Ordering::Relaxed);
// Remove test data
for account_id in [&account_id, &other_account_id] {
params.client.set_default_account_id(account_id.to_string());
destroy_all_mailboxes(params).await;
}
params.destroy_all_mailboxes(account).await;
params.destroy_all_mailboxes(other_account).await;
for event in server.all_queued_messages().await.messages {
server
.read_message(event.queue_id, QueueName::default())

View File

@@ -4,38 +4,19 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::{
directory::internal::TestInternalDirectory,
jmap::{JMAPTest, assert_is_empty, jmap_json_request, mail::mailbox::destroy_all_mailboxes},
};
use crate::jmap::{JMAPTest, assert_is_empty};
use email::mailbox::INBOX_ID;
use serde_json::Value;
use serde_json::{Value, json};
use types::id::Id;
pub async fn test(params: &mut JMAPTest) {
println!("Running blob tests...");
let server = params.server.clone();
let account_id = Id::from(
server
.core
.storage
.data
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await,
);
let account = params.account("jdoe@example.com");
server.core.storage.data.blob_expire_all().await;
// Blob/set simple test
let response = jmap_json_request(
r#"[[
"Blob/upload",
{
"accountId": "$$",
let response = account.jmap_method_call("Blob/upload", json!({
"create": {
"abc": {
"data" : [
@@ -46,14 +27,8 @@ pub async fn test(params: &mut JMAPTest) {
"type": "image/png"
}
}
},
"R1"
]]"#
.replace("$$", &account_id.to_string()),
"jdoe@example.com",
"12345",
)
.await;
})).await;
assert_eq!(
response
.pointer("/methodResponses/0/1/created/abc/type")
@@ -74,11 +49,10 @@ pub async fn test(params: &mut JMAPTest) {
);
// Blob/get simple test
let blob_id = jmap_json_request(
r#"[[
let blob_id = account
.jmap_method_call(
"Blob/upload",
{
"accountId": "$$",
json!({
"create": {
"abc": {
"data" : [
@@ -88,61 +62,48 @@ pub async fn test(params: &mut JMAPTest) {
]
}
}
}),
)
.await
.pointer("/methodResponses/0/1/created/abc/id")
.and_then(|v| v.as_str())
.unwrap()
.to_string();
let response = account
.jmap_method_calls(json!([[
"Blob/get",
{
"ids" : [
blob_id
],
"properties" : [
"data:asText",
"digest:sha",
"size"
]
},
"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;
],
[
"Blob/get",
{
"ids" : [
blob_id
],
"properties" : [
"data:asText",
"digest:sha",
"digest:sha-256",
"size"
],
"offset" : 4,
"length" : 9
},
"R2"
]
]))
.await;
for (pointer, expected) in [
(
@@ -174,84 +135,76 @@ pub async fn test(params: &mut JMAPTest) {
})
.unwrap_or_default(),
expected,
"Pointer {pointer:?} Response: {}",
serde_json::to_string_pretty(&response).unwrap()
"Pointer {pointer:?} Response: {response:?}",
);
}
server.core.storage.data.blob_expire_all().await;
// Blob/upload Complex Example
let response = jmap_json_request(
r##"[
[
"Blob/upload",
{
"accountId" : "$$",
"create": {
"b4": {
"data": [
let response = account
.jmap_method_calls(json!([
[
"Blob/upload",
{
"create": {
"b4": {
"data": [
{
"data:asText": "The quick brown fox jumped over the lazy dog."
}
]
}
}
},
"S4"
],
[
"Blob/upload",
{
"create": {
"cat": {
"data": [
{
"data:asText": "The quick brown fox jumped over the lazy dog."
"data:asText": "How"
},
{
"blobId": "#b4",
"length": 7,
"offset": 3
},
{
"data:asText": "was t"
},
{
"blobId": "#b4",
"length": 1,
"offset": 1
},
{
"data:asBase64": "YXQ/"
}
]
}
}
},
"S4"
}
},
"CAT"
],
[
"Blob/get",
{
"properties": [
"data:asText",
"size"
],
[
"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;
"ids": [
"#cat"
]
},
"G4"
]
]))
.await;
for (pointer, expected) in [
(
@@ -276,12 +229,10 @@ pub async fn test(params: &mut JMAPTest) {
server.core.storage.data.blob_expire_all().await;
// Blob/get Example with Range and Encoding Errors
let response = jmap_json_request(
r##"[
let response = account.jmap_method_calls(json!([
[
"Blob/upload",
{
"accountId" : "$$",
"create": {
"b1": {
"data": [
@@ -305,7 +256,6 @@ pub async fn test(params: &mut JMAPTest) {
[
"Blob/get",
{
"accountId" : "$$",
"ids": [
"#b1",
"#b2"
@@ -316,7 +266,6 @@ pub async fn test(params: &mut JMAPTest) {
[
"Blob/get",
{
"accountId" : "$$",
"ids": [
"#b1",
"#b2"
@@ -331,7 +280,6 @@ pub async fn test(params: &mut JMAPTest) {
[
"Blob/get",
{
"accountId" : "$$",
"ids": [
"#b1",
"#b2"
@@ -346,7 +294,6 @@ pub async fn test(params: &mut JMAPTest) {
[
"Blob/get",
{
"accountId" : "$$",
"offset": 0,
"length": 5,
"ids": [
@@ -359,7 +306,6 @@ pub async fn test(params: &mut JMAPTest) {
[
"Blob/get",
{
"accountId" : "$$",
"offset": 20,
"length": 100,
"ids": [
@@ -369,12 +315,7 @@ pub async fn test(params: &mut JMAPTest) {
},
"G5"
]
]"##
.replace("$$", &account_id.to_string()),
"jdoe@example.com",
"12345",
)
.await;
])).await;
for (pointer, expected) in [
(
@@ -415,9 +356,8 @@ pub async fn test(params: &mut JMAPTest) {
server.core.storage.data.blob_expire_all().await;
// Blob/lookup
params.client.set_default_account_id(account_id.to_string());
let blob_id = params
.client
let client = account.client();
let blob_id = client
.email_import(
concat!(
"From: bill@example.com\r\n",
@@ -437,29 +377,22 @@ pub async fn test(params: &mut JMAPTest) {
.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;
let response = account
.jmap_method_call(
"Blob/lookup",
json!({
"typeNames": [
"Mailbox",
"Thread",
"Email"
],
"ids": [
blob_id,
"not-a-blob"
]
}),
)
.await;
for pointer in [
"/methodResponses/0/1/list/0/matchedIds/Email",
@@ -478,7 +411,6 @@ pub async fn test(params: &mut JMAPTest) {
}
// Remove test data
params.client.set_default_account_id(account_id.to_string());
destroy_all_mailboxes(params).await;
params.destroy_all_mailboxes(account).await;
assert_is_empty(server).await;
}

View File

@@ -4,46 +4,22 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use std::time::Duration;
use crate::{
directory::internal::TestInternalDirectory,
jmap::{
JMAPTest, assert_is_empty,
mail::{delivery::SmtpConnection, mailbox::destroy_all_mailboxes},
test_account_login,
},
};
use crate::jmap::{JMAPTest, assert_is_empty, mail::delivery::SmtpConnection};
use email::mailbox::INBOX_ID;
use futures::StreamExt;
use jmap_client::{TypeState, event_source::Changes, mailbox::Role};
use std::time::Duration;
use store::ahash::AHashSet;
use types::id::Id;
use tokio::sync::mpsc;
use types::id::Id;
pub async fn test(params: &mut JMAPTest) {
println!("Running EventSource tests...");
// Create test account
let server = params.server.clone();
let account_id = Id::from(
server
.core
.storage
.data
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await,
)
.to_string();
let client = test_account_login("jdoe@example.com", "12345").await;
let account = params.account("jdoe@example.com");
let client = account.client();
let mut changes = client
.event_source(None::<Vec<_>>, false, 1.into(), None)
@@ -69,7 +45,7 @@ pub async fn test(params: &mut JMAPTest) {
.await
.unwrap()
.take_id();
assert_state(&mut event_rx, &account_id, &[TypeState::Mailbox]).await;
assert_state(&mut event_rx, account.id_string(), &[TypeState::Mailbox]).await;
// Multiple changes should be grouped and delivered in intervals
for num in 0..5 {
@@ -78,7 +54,7 @@ pub async fn test(params: &mut JMAPTest) {
.await
.unwrap();
}
assert_state(&mut event_rx, &account_id, &[TypeState::Mailbox]).await;
assert_state(&mut event_rx, account.id_string(), &[TypeState::Mailbox]).await;
assert_ping(&mut event_rx).await; // Pings are only received in cfg(test)
// Ingest email and expect state change
@@ -100,7 +76,7 @@ pub async fn test(params: &mut JMAPTest) {
assert_state(
&mut event_rx,
&account_id,
account.id_string(),
&[
TypeState::EmailDelivery,
TypeState::Email,
@@ -113,25 +89,23 @@ pub async fn test(params: &mut JMAPTest) {
// Destroy mailbox
client.mailbox_destroy(&mailbox_id, true).await.unwrap();
assert_state(&mut event_rx, &account_id, &[TypeState::Mailbox]).await;
assert_state(&mut event_rx, account.id_string(), &[TypeState::Mailbox]).await;
// Destroy Inbox
params.client.set_default_account_id(account_id.to_string());
params
.client
client
.mailbox_destroy(&Id::from(INBOX_ID).to_string(), true)
.await
.unwrap();
assert_state(
&mut event_rx,
&account_id,
account.id_string(),
&[TypeState::Email, TypeState::Thread, TypeState::Mailbox],
)
.await;
assert_ping(&mut event_rx).await;
assert_ping(&mut event_rx).await;
destroy_all_mailboxes(params).await;
params.destroy_all_mailboxes(account).await;
assert_is_empty(server).await;
}

View File

@@ -6,8 +6,7 @@
use crate::{
AssertConfig, add_test_certs,
directory::internal::TestInternalDirectory,
jmap::{JMAPTest, assert_is_empty, mail::mailbox::destroy_all_mailboxes, test_account_login},
jmap::{JMAPTest, assert_is_empty},
};
use base64::{Engine, engine::general_purpose};
use common::{Caches, Core, Data, Inner, config::server::Listeners, listener::SessionData};
@@ -56,22 +55,8 @@ pub async fn test(params: &mut JMAPTest) {
// Create test account
let server = params.server.clone();
let account_id = Id::from(
server
.core
.storage
.data
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await,
);
params.client.set_default_account_id(account_id);
let client = test_account_login("jdoe@example.com", "12345").await;
let account = params.account("jdoe@example.com");
let client = account.client();
// Create channels
let (event_tx, mut event_rx) = mpsc::channel::<PushMessage>(100);
@@ -140,7 +125,7 @@ pub async fn test(params: &mut JMAPTest) {
.unwrap()
.take_id();
assert_state(&mut event_rx, &account_id, &[DataType::Mailbox]).await;
assert_state(&mut event_rx, account.id(), &[DataType::Mailbox]).await;
// Receive states just for the requested types
client
@@ -194,14 +179,14 @@ pub async fn test(params: &mut JMAPTest) {
.unwrap();
tokio::time::sleep(Duration::from_millis(200)).await;
push_server.fail_requests.store(false, Ordering::Relaxed);
assert_state(&mut event_rx, &account_id, &[DataType::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, &[DataType::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
@@ -211,7 +196,7 @@ pub async fn test(params: &mut JMAPTest) {
.await
.unwrap();
}
assert_state(&mut event_rx, &account_id, &[DataType::Mailbox]).await;
assert_state(&mut event_rx, account.id(), &[DataType::Mailbox]).await;
expect_nothing(&mut event_rx).await;
// Destroy mailbox
@@ -219,7 +204,7 @@ pub async fn test(params: &mut JMAPTest) {
client.mailbox_destroy(&mailbox_id, true).await.unwrap();
expect_nothing(&mut event_rx).await;
destroy_all_mailboxes(params).await;
params.destroy_all_mailboxes(account).await;
assert_is_empty(server).await;
}

View File

@@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::jmap::{JMAPTest, assert_is_empty};
use ahash::AHashSet;
use futures::StreamExt;
use jmap_client::{
@@ -15,35 +16,15 @@ use jmap_client::{
},
};
use std::time::Duration;
use types::id::Id;
use tokio::sync::mpsc;
use crate::{
directory::internal::TestInternalDirectory,
jmap::{JMAPTest, assert_is_empty, mail::mailbox::destroy_all_mailboxes, test_account_login},
};
pub async fn test(params: &mut JMAPTest) {
println!("Running WebSockets tests...");
let server = params.server.clone();
// Authenticate all accounts
let account_id = Id::from(
server
.core
.storage
.data
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await,
)
.to_string();
let client = test_account_login("jdoe@example.com", "12345").await;
let account = params.account("jdoe@example.com");
let client = account.client();
let mut ws_stream = client.connect_ws().await.unwrap();
@@ -86,7 +67,7 @@ pub async fn test(params: &mut JMAPTest) {
.mailbox_update_sort_order(&mailbox_id, 1)
.await
.unwrap();
assert_state(&mut stream_rx, &account_id, &[TypeState::Mailbox]).await;
assert_state(&mut stream_rx, account.id_string(), &[TypeState::Mailbox]).await;
// Multiple changes should be grouped and delivered in intervals
for num in 0..5 {
@@ -96,7 +77,7 @@ pub async fn test(params: &mut JMAPTest) {
.unwrap();
}
tokio::time::sleep(Duration::from_millis(500)).await;
assert_state(&mut stream_rx, &account_id, &[TypeState::Mailbox]).await;
assert_state(&mut stream_rx, account.id_string(), &[TypeState::Mailbox]).await;
expect_nothing(&mut stream_rx).await;
// Disable push notifications
@@ -116,8 +97,7 @@ pub async fn test(params: &mut JMAPTest) {
.unwrap();
expect_nothing(&mut stream_rx).await;
params.client.set_default_account_id(account_id);
destroy_all_mailboxes(params).await;
params.destroy_all_mailboxes(account).await;
assert_is_empty(server).await;
}

View File

@@ -6,7 +6,7 @@
use crate::{
directory::internal::TestInternalDirectory,
jmap::{JMAPTest, assert_is_empty, mail::mailbox::destroy_all_mailboxes, test_account_login},
jmap::{JMAPTest, assert_is_empty},
};
use ::email::mailbox::{INBOX_ID, TRASH_ID};
use jmap_client::{
@@ -30,62 +30,27 @@ pub async fn test(params: &mut JMAPTest) {
let inbox_id = Id::new(INBOX_ID as u64).to_string();
let trash_id = Id::new(TRASH_ID as u64).to_string();
let john_id: Id = server
.core
.storage
.data
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await
.into();
let jane_id: Id = server
.core
.storage
.data
.create_test_user(
"jane.smith@example.com",
"abcde",
"Jane Smith",
&["jane.smith@example.com"],
)
.await
.into();
let bill_id: Id = server
.core
.storage
.data
.create_test_user(
"bill@example.com",
"098765",
"Bill Foobar",
&["bill@example.com"],
)
.await
.into();
let sales_id: Id = server
.core
.storage
.data
.create_test_group("sales@example.com", "Sales Group", &["sales@example.com"])
.await
.into();
let john = params.account("jdoe@example.com");
let jane = params.account("jane.smith@example.com");
let bill = params.account("bill@example.com");
let sales = params.account("sales@example.com");
// Authenticate all accounts
let mut john_client = test_account_login("jdoe@example.com", "12345").await;
let mut jane_client = test_account_login("jane.smith@example.com", "abcde").await;
let mut bill_client = test_account_login("bill@example.com", "098765").await;
let mut john_client = john.client_owned().await;
let mut jane_client = jane.client_owned().await;
let mut bill_client = bill.client_owned().await;
// Insert two emails in each account
let mut email_ids = AHashMap::default();
for (client, account_id, name) in [
(&mut john_client, &john_id, "john"),
(&mut jane_client, &jane_id, "jane"),
(&mut bill_client, &bill_id, "bill"),
(&mut params.client, &sales_id, "sales"),
(&mut john_client, john.id(), "john"),
(&mut jane_client, jane.id(), "jane"),
(&mut bill_client, bill.id(), "bill"),
(
&mut params.account("admin").client_owned().await,
sales.id(),
"sales",
),
] {
let user_name = client.session().username().to_string();
let mut ids = Vec::with_capacity(2);
@@ -133,7 +98,7 @@ pub async fn test(params: &mut JMAPTest) {
);
assert_forbidden(
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.email_get(
email_ids.get("jane").unwrap().first().unwrap(),
[Property::Subject].into(),
@@ -142,13 +107,13 @@ pub async fn test(params: &mut JMAPTest) {
);
assert_forbidden(
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.mailbox_get(&inbox_id, None::<Vec<_>>)
.await,
);
assert_forbidden(
john_client
.set_default_account_id(sales_id.to_string())
.set_default_account_id(sales.id_string())
.email_get(
email_ids.get("sales").unwrap().first().unwrap(),
[Property::Subject].into(),
@@ -157,27 +122,27 @@ pub async fn test(params: &mut JMAPTest) {
);
assert_forbidden(
john_client
.set_default_account_id(sales_id.to_string())
.set_default_account_id(sales.id_string())
.mailbox_get(&inbox_id, None::<Vec<_>>)
.await,
);
assert_forbidden(
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.email_query(None::<Filter>, None::<Vec<_>>)
.await,
);
// Jane grants Inbox ReadItems access to John
jane_client
.mailbox_update_acl(&inbox_id, &john_id.to_string(), [ACL::ReadItems])
.mailbox_update_acl(&inbox_id, john.id_string(), [ACL::ReadItems])
.await
.unwrap();
// John should have ReadItems access to Inbox
assert_eq!(
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.email_get(
email_ids.get("jane").unwrap().first().unwrap(),
[Property::Subject].into(),
@@ -191,7 +156,7 @@ pub async fn test(params: &mut JMAPTest) {
);
assert_eq!(
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.email_query(None::<Filter>, None::<Vec<_>>)
.await
.unwrap()
@@ -204,7 +169,7 @@ pub async fn test(params: &mut JMAPTest) {
assert_eq!(
john_client
.session()
.account(&jane_id.to_string())
.account(jane.id_string())
.unwrap()
.name(),
"jane.smith@example.com"
@@ -213,7 +178,7 @@ pub async fn test(params: &mut JMAPTest) {
// John should not have access to emails in Jane's Trash folder
assert!(
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.email_get(
email_ids.get("jane").unwrap().last().unwrap(),
[Property::Subject].into(),
@@ -234,8 +199,8 @@ pub async fn test(params: &mut JMAPTest) {
.unwrap()
.take_blob_id();
john_client
.set_default_account_id(john_id.to_string())
.blob_copy(jane_id.to_string(), &blob_id)
.set_default_account_id(john.id_string())
.blob_copy(jane.id_string(), &blob_id)
.await
.unwrap();
let blob_id = jane_client
@@ -249,19 +214,19 @@ pub async fn test(params: &mut JMAPTest) {
.take_blob_id();
assert_forbidden(
john_client
.set_default_account_id(john_id.to_string())
.blob_copy(jane_id.to_string(), &blob_id)
.set_default_account_id(john.id_string())
.blob_copy(jane.id_string(), &blob_id)
.await,
);
// John only has ReadItems access to Inbox
jane_client
.mailbox_update_acl(&inbox_id, &john_id.to_string(), [ACL::ReadItems])
.mailbox_update_acl(&inbox_id, john.id_string(), [ACL::ReadItems])
.await
.unwrap();
assert_eq!(
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.mailbox_get(&inbox_id, [mailbox::Property::MyRights].into())
.await
.unwrap()
@@ -274,9 +239,9 @@ pub async fn test(params: &mut JMAPTest) {
// Try to add items using import and copy
let blob_id = john_client
.set_default_account_id(john_id.to_string())
.set_default_account_id(john.id_string())
.upload(
Some(&john_id.to_string()),
Some(john.id_string()),
concat!(
"From: acl_test@example.com\r\n",
"To: jane.smith@example.com\r\n",
@@ -291,9 +256,7 @@ pub async fn test(params: &mut JMAPTest) {
.await
.unwrap()
.take_blob_id();
let mut request = john_client
.set_default_account_id(jane_id.to_string())
.build();
let mut request = john_client.set_default_account_id(jane.id_string()).build();
let email_id = request
.import_email()
.email(&blob_id)
@@ -308,9 +271,9 @@ pub async fn test(params: &mut JMAPTest) {
);
assert_forbidden(
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.email_copy(
&john_id.to_string(),
john.id_string(),
email_ids.get("john").unwrap().last().unwrap(),
[&inbox_id],
None::<Vec<&str>>,
@@ -321,17 +284,11 @@ pub async fn test(params: &mut JMAPTest) {
// Grant access and try again
jane_client
.mailbox_update_acl(
&inbox_id,
&john_id.to_string(),
[ACL::ReadItems, ACL::AddItems],
)
.mailbox_update_acl(&inbox_id, john.id_string(), [ACL::ReadItems, ACL::AddItems])
.await
.unwrap();
let mut request = john_client
.set_default_account_id(jane_id.to_string())
.build();
let mut request = john_client.set_default_account_id(jane.id_string()).build();
let email_id = request
.import_email()
.email(&blob_id)
@@ -345,9 +302,9 @@ pub async fn test(params: &mut JMAPTest) {
.unwrap()
.take_id();
let email_id_2 = john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.email_copy(
&john_id.to_string(),
john.id_string(),
email_ids.get("john").unwrap().last().unwrap(),
[&inbox_id],
None::<Vec<&str>>,
@@ -381,20 +338,20 @@ pub async fn test(params: &mut JMAPTest) {
// Try removing items
assert_forbidden(
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.email_destroy(&email_id)
.await,
);
jane_client
.mailbox_update_acl(
&inbox_id,
&john_id.to_string(),
john.id_string(),
[ACL::ReadItems, ACL::AddItems, ACL::RemoveItems],
)
.await
.unwrap();
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.email_destroy(&email_id)
.await
.unwrap();
@@ -402,14 +359,14 @@ pub async fn test(params: &mut JMAPTest) {
// Try to set keywords
assert_forbidden(
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.email_set_keyword(&email_id_2, "$seen", true)
.await,
);
jane_client
.mailbox_update_acl(
&inbox_id,
&john_id.to_string(),
john.id_string(),
[
ACL::ReadItems,
ACL::AddItems,
@@ -420,12 +377,12 @@ pub async fn test(params: &mut JMAPTest) {
.await
.unwrap();
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.email_set_keyword(&email_id_2, "$seen", true)
.await
.unwrap();
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.email_set_keyword(&email_id_2, "my-keyword", true)
.await
.unwrap();
@@ -433,14 +390,14 @@ pub async fn test(params: &mut JMAPTest) {
// Try to create a child
assert_forbidden(
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.mailbox_create("John's mailbox", None::<&str>, Role::None)
.await,
);
jane_client
.mailbox_update_acl(
&inbox_id,
&john_id.to_string(),
john.id_string(),
[
ACL::ReadItems,
ACL::AddItems,
@@ -452,7 +409,7 @@ pub async fn test(params: &mut JMAPTest) {
.await
.unwrap();
let mailbox_id = john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.mailbox_create("John's mailbox", Some(&inbox_id), Role::None)
.await
.unwrap()
@@ -461,20 +418,16 @@ pub async fn test(params: &mut JMAPTest) {
// Try renaming a mailbox
assert_forbidden(
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.mailbox_rename(&mailbox_id, "John's private mailbox")
.await,
);
jane_client
.mailbox_update_acl(
&mailbox_id,
&john_id.to_string(),
[ACL::ReadItems, ACL::Rename],
)
.mailbox_update_acl(&mailbox_id, john.id_string(), [ACL::ReadItems, ACL::Rename])
.await
.unwrap();
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.mailbox_rename(&mailbox_id, "John's private mailbox")
.await
.unwrap();
@@ -482,20 +435,20 @@ pub async fn test(params: &mut JMAPTest) {
// Try moving a message
assert_forbidden(
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.email_set_mailbox(&email_id_2, &mailbox_id, true)
.await,
);
jane_client
.mailbox_update_acl(
&mailbox_id,
&john_id.to_string(),
john.id_string(),
[ACL::ReadItems, ACL::Rename, ACL::AddItems],
)
.await
.unwrap();
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.email_set_mailbox(&email_id_2, &mailbox_id, true)
.await
.unwrap();
@@ -503,28 +456,28 @@ pub async fn test(params: &mut JMAPTest) {
// Try deleting a mailbox
assert_forbidden(
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.mailbox_destroy(&mailbox_id, true)
.await,
);
jane_client
.mailbox_update_acl(
&mailbox_id,
&john_id.to_string(),
john.id_string(),
[ACL::ReadItems, ACL::Rename, ACL::AddItems, ACL::Delete],
)
.await
.unwrap();
assert_forbidden(
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.mailbox_destroy(&mailbox_id, true)
.await,
);
jane_client
.mailbox_update_acl(
&mailbox_id,
&john_id.to_string(),
john.id_string(),
[
ACL::ReadItems,
ACL::Rename,
@@ -536,7 +489,7 @@ pub async fn test(params: &mut JMAPTest) {
.await
.unwrap();
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.mailbox_destroy(&mailbox_id, true)
.await
.unwrap();
@@ -544,20 +497,20 @@ pub async fn test(params: &mut JMAPTest) {
// Try changing ACL
assert_forbidden(
john_client
.set_default_account_id(jane_id.to_string())
.mailbox_update_acl(&inbox_id, &bill_id.to_string(), [ACL::ReadItems])
.set_default_account_id(jane.id_string())
.mailbox_update_acl(&inbox_id, bill.id_string(), [ACL::ReadItems])
.await,
);
assert_forbidden(
bill_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.email_query(None::<Filter>, None::<Vec<_>>)
.await,
);
jane_client
.mailbox_update_acl(
&inbox_id,
&john_id.to_string(),
john.id_string(),
[
ACL::ReadItems,
ACL::AddItems,
@@ -572,7 +525,7 @@ pub async fn test(params: &mut JMAPTest) {
.unwrap();
assert_eq!(
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.mailbox_get(&inbox_id, [mailbox::Property::MyRights].into())
.await
.unwrap()
@@ -591,13 +544,13 @@ pub async fn test(params: &mut JMAPTest) {
]
);
john_client
.set_default_account_id(jane_id.to_string())
.mailbox_update_acl(&inbox_id, &bill_id.to_string(), [ACL::ReadItems])
.set_default_account_id(jane.id_string())
.mailbox_update_acl(&inbox_id, bill.id_string(), [ACL::ReadItems])
.await
.unwrap();
assert_eq!(
bill_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.email_query(
None::<Filter>,
vec![email::query::Comparator::subject()].into()
@@ -613,12 +566,12 @@ pub async fn test(params: &mut JMAPTest) {
// Revoke all access to John
jane_client
.mailbox_update_acl(&inbox_id, &john_id.to_string(), [])
.mailbox_update_acl(&inbox_id, john.id_string(), [])
.await
.unwrap();
assert_forbidden(
john_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.email_get(
email_ids.get("jane").unwrap().first().unwrap(),
[Property::Subject].into(),
@@ -626,15 +579,10 @@ pub async fn test(params: &mut JMAPTest) {
.await,
);
john_client.refresh_session().await.unwrap();
assert!(
john_client
.session()
.account(&jane_id.to_string())
.is_none()
);
assert!(john_client.session().account(jane.id_string()).is_none());
assert_eq!(
bill_client
.set_default_account_id(jane_id.to_string())
.set_default_account_id(jane.id_string())
.email_get(
email_ids.get("jane").unwrap().first().unwrap(),
[Property::Subject].into(),
@@ -666,7 +614,7 @@ pub async fn test(params: &mut JMAPTest) {
assert_eq!(
john_client
.session()
.account(&sales_id.to_string())
.account(sales.id_string())
.unwrap()
.name(),
"sales@example.com"
@@ -674,30 +622,25 @@ pub async fn test(params: &mut JMAPTest) {
assert!(
!john_client
.session()
.account(&sales_id.to_string())
.account(sales.id_string())
.unwrap()
.is_personal()
);
assert_eq!(
jane_client
.session()
.account(&sales_id.to_string())
.account(sales.id_string())
.unwrap()
.name(),
"sales@example.com"
);
assert!(
bill_client
.session()
.account(&sales_id.to_string())
.is_none()
);
assert!(bill_client.session().account(sales.id_string()).is_none());
// Insert a message in Sales's inbox
let blob_id = john_client
.set_default_account_id(sales_id.to_string())
.set_default_account_id(sales.id_string())
.upload(
Some(&sales_id.to_string()),
Some(sales.id_string()),
concat!(
"From: acl_test@example.com\r\n",
"To: sales@example.com\r\n",
@@ -729,7 +672,7 @@ pub async fn test(params: &mut JMAPTest) {
// Both Jane and John should be able to see this message, but not Bill
assert_eq!(
john_client
.set_default_account_id(sales_id.to_string())
.set_default_account_id(sales.id_string())
.email_get(&email_id, [Property::Subject].into(),)
.await
.unwrap()
@@ -740,7 +683,7 @@ pub async fn test(params: &mut JMAPTest) {
);
assert_eq!(
jane_client
.set_default_account_id(sales_id.to_string())
.set_default_account_id(sales.id_string())
.email_get(&email_id, [Property::Subject].into(),)
.await
.unwrap()
@@ -751,7 +694,7 @@ pub async fn test(params: &mut JMAPTest) {
);
assert_forbidden(
bill_client
.set_default_account_id(sales_id.to_string())
.set_default_account_id(sales.id_string())
.email_get(&email_id, [Property::Subject].into())
.await,
);
@@ -769,15 +712,14 @@ pub async fn test(params: &mut JMAPTest) {
.await;
assert_forbidden(
john_client
.set_default_account_id(sales_id.to_string())
.set_default_account_id(sales.id_string())
.email_get(&email_id, [Property::Subject].into())
.await,
);
// Destroy test account data
for id in [john_id, bill_id, jane_id, sales_id] {
params.client.set_default_account_id(id.to_string());
destroy_all_mailboxes(params).await;
for id in [john, bill, jane, sales] {
params.destroy_all_mailboxes(id).await;
}
assert_is_empty(server).await;
}

View File

@@ -17,7 +17,8 @@ pub async fn test(params: &mut JMAPTest) {
println!("Running Email Changes tests...");
let server = params.server.clone();
params.client.set_default_account_id(Id::new(1));
let account = params.account("jdoe@example.com");
let client = account.client();
let mut states = vec![State::Initial];
for (changes, expected_changelog) in [
@@ -133,7 +134,9 @@ pub async fn test(params: &mut JMAPTest) {
.into_iter()
{
let mut batch = BatchBuilder::new();
batch.with_account_id(1).with_collection(Collection::Email);
batch
.with_account_id(account.id().document_id())
.with_collection(Collection::Email);
for change in changes {
match change {
@@ -175,11 +178,7 @@ pub async fn test(params: &mut JMAPTest) {
let mut new_state = State::Initial;
for (test_num, state) in (states).iter().enumerate() {
let changes = params
.client
.email_changes(state.to_string(), None)
.await
.unwrap();
let changes = client.email_changes(state.to_string(), None).await.unwrap();
assert_eq!(
expected_changelog[test_num],
@@ -220,8 +219,7 @@ pub async fn test(params: &mut JMAPTest) {
let mut int_state = state.clone();
for _ in 0..100 {
let changes = params
.client
let changes = client
.email_changes(int_state.to_string(), max_changes.into())
.await
.unwrap();
@@ -300,8 +298,7 @@ pub async fn test(params: &mut JMAPTest) {
states.push(new_state);
}
let changes = params
.client
let changes = client
.email_changes(State::Initial.to_string(), None)
.await
.unwrap();

View File

@@ -4,17 +4,18 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::jmap::{JMAPTest, assert_is_empty, mail::mailbox::destroy_all_mailboxes};
use crate::jmap::{JMAPTest, assert_is_empty, mail::mailbox::destroy_all_mailboxes_for_account};
use jmap_client::mailbox::Role;
use types::id::Id;
pub async fn test(params: &mut JMAPTest) {
println!("Running Email Copy tests...");
let server = params.server.clone();
let account = params.account("admin");
let mut client = account.client_owned().await;
// Create a mailbox on account 1
let ac1_mailbox_id = params
.client
let ac1_mailbox_id = client
.set_default_account_id(Id::new(1).to_string())
.mailbox_create("Copy Test Ac# 1", None::<String>, Role::None)
.await
@@ -22,8 +23,7 @@ pub async fn test(params: &mut JMAPTest) {
.take_id();
// Insert a message on account 1
let ac1_email_id = params
.client
let ac1_email_id = client
.email_import(
concat!(
"From: bill@example.com\r\n",
@@ -44,8 +44,7 @@ pub async fn test(params: &mut JMAPTest) {
.take_id();
// Create a mailbox on account 2
let ac2_mailbox_id = params
.client
let ac2_mailbox_id = client
.set_default_account_id(Id::new(2).to_string())
.mailbox_create("Copy Test Ac# 2", None::<String>, Role::None)
.await
@@ -53,7 +52,7 @@ pub async fn test(params: &mut JMAPTest) {
.take_id();
// Copy the email and delete it from the first account
let mut request = params.client.build();
let mut request = client.build();
request
.copy_email(Id::new(1).to_string())
.on_success_destroy_original(true)
@@ -73,8 +72,7 @@ pub async fn test(params: &mut JMAPTest) {
.take_id();
// Check that the email was copied
let email = params
.client
let email = client
.email_get(&ac2_email_id, None::<Vec<_>>)
.await
.unwrap()
@@ -90,8 +88,7 @@ pub async fn test(params: &mut JMAPTest) {
// Check that the email was deleted
assert!(
params
.client
client
.set_default_account_id(Id::new(1).to_string())
.email_get(&ac1_email_id, None::<Vec<_>>)
.await
@@ -100,8 +97,7 @@ pub async fn test(params: &mut JMAPTest) {
);
// Empty store
destroy_all_mailboxes(params).await;
params.client.set_default_account_id(Id::new(2).to_string());
destroy_all_mailboxes(params).await;
destroy_all_mailboxes_for_account(1).await;
destroy_all_mailboxes_for_account(2).await;
assert_is_empty(server).await;
}

View File

@@ -4,10 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::{
directory::internal::TestInternalDirectory,
jmap::{JMAPTest, ManagementApi, mail::delivery::SmtpConnection},
};
use crate::jmap::{JMAPTest, ManagementApi, mail::delivery::SmtpConnection};
use email::message::crypto::{
Algorithm, EncryptMessage, EncryptionMethod, EncryptionParams, EncryptionType, try_parse_certs,
};
@@ -17,28 +14,13 @@ use store::{
Deserialize, Serialize,
write::{Archive, Archiver},
};
use types::id::Id;
pub async fn test(params: &mut JMAPTest) {
println!("Running Encryption-at-rest tests...");
// Create test account
let server = params.server.clone();
let client = &mut params.client;
let account_id = Id::from(
server
.core
.storage
.data
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await,
)
.to_string();
let account = params.account("jdoe@example.com");
let client = account.client();
// Build API
let api = ManagementApi::new(8899, "jdoe@example.com", "12345");
@@ -143,7 +125,6 @@ pub async fn test(params: &mut JMAPTest) {
.await;
// Check messages
client.set_default_account_id(&account_id);
let mut request = client.build();
request.get_email();
let emails = request.send_get_email().await.unwrap().take_list();

View File

@@ -6,7 +6,7 @@
use crate::{
directory::internal::TestInternalDirectory,
jmap::{JMAPTest, assert_is_empty, mail::mailbox::destroy_all_mailboxes},
jmap::{JMAPTest, assert_is_empty},
webdav::DummyWebDavClient,
};
use email::{
@@ -14,55 +14,21 @@ use email::{
mailbox::{INBOX_ID, JUNK_ID},
};
use groupware::DavResourceName;
use std::{str::FromStr, time::Duration};
use std::time::Duration;
use tokio::{
io::{AsyncBufReadExt, AsyncWriteExt, BufReader, Lines, ReadHalf, WriteHalf},
net::TcpStream,
};
use types::{collection::Collection, id::Id};
use types::collection::Collection;
pub async fn test(params: &mut JMAPTest) {
println!("Running message delivery tests...");
// Create a domain name and a test account
let server = params.server.clone();
let mut account_id_1 = String::new();
let mut account_id_2 = String::new();
let mut account_id_3 = String::new();
for (id, email, password, name, aliases) in [
(
&mut account_id_1,
"jdoe@example.com",
"12345",
"John Doe",
Some(&["jdoe@example.com", "john.doe@example.com"][..]),
),
(
&mut account_id_2,
"jane@example.com",
"abcdef",
"Jane Smith",
None,
),
(
&mut account_id_3,
"bill@example.com",
"098765",
"Bill Foobar",
None,
),
] {
*id = Id::from(
server
.core
.storage
.data
.create_test_user(email, password, name, aliases.unwrap_or(&[email][..]))
.await,
)
.to_string();
}
let john = params.account("jdoe@example.com");
let jane = params.account("jane.smith@example.com");
let bill = params.account("bill@example.com");
// Create a mailing list
server
@@ -72,7 +38,11 @@ pub async fn test(params: &mut JMAPTest) {
.create_test_list(
"members@example.com",
"Mailing List",
&["jdoe@example.com", "jane@example.com", "bill@example.com"],
&[
"jdoe@example.com",
"jane.smith@example.com",
"bill@example.com",
],
)
.await;
@@ -95,12 +65,14 @@ pub async fn test(params: &mut JMAPTest) {
)
.await;
let john_id = Id::from_str(&account_id_1).unwrap().document_id();
let john_cache = server.get_cached_messages(john_id).await.unwrap();
let john_cache = server
.get_cached_messages(john.id().document_id())
.await
.unwrap();
assert_eq!(
server
.get_document_ids(john_id, Collection::Email)
.get_document_ids(john.id().document_id(), Collection::Email)
.await
.unwrap()
.unwrap()
@@ -126,11 +98,14 @@ pub async fn test(params: &mut JMAPTest) {
),
)
.await;
let john_cache = server.get_cached_messages(john_id).await.unwrap();
let john_cache = server
.get_cached_messages(john.id().document_id())
.await
.unwrap();
assert_eq!(
server
.get_document_ids(john_id, Collection::Email)
.get_document_ids(john.id().document_id(), Collection::Email)
.await
.unwrap()
.unwrap()
@@ -175,11 +150,14 @@ END:VCARD
),
)
.await;
let john_cache = server.get_cached_messages(john_id).await.unwrap();
let john_cache = server
.get_cached_messages(john.id().document_id())
.await
.unwrap();
assert_eq!(
server
.get_document_ids(john_id, Collection::Email)
.get_document_ids(john.id().document_id(), Collection::Email)
.await
.unwrap()
.unwrap()
@@ -194,7 +172,7 @@ END:VCARD
lmtp.expn("members@example.com", 2)
.await
.assert_contains("jdoe@example.com")
.assert_contains("jane@example.com")
.assert_contains("jane.smith@example.com")
.assert_contains("bill@example.com");
lmtp.expn("non_existant@example.com", 5).await;
lmtp.expn("jdoe@example.com", 5).await;
@@ -217,20 +195,17 @@ END:VCARD
)
.await;
for (account_id, num_messages) in [(&account_id_1, 4), (&account_id_2, 1), (&account_id_3, 1)] {
for (account, num_messages) in [(john, 4), (jane, 1), (bill, 1)] {
assert_eq!(
server
.get_document_ids(
Id::from_str(account_id).unwrap().document_id(),
Collection::Email
)
.get_document_ids(account.id().document_id(), Collection::Email)
.await
.unwrap()
.unwrap()
.len(),
num_messages,
"for {}",
account_id
account.id_string()
);
}
@@ -257,20 +232,17 @@ END:VCARD
)
.await;
for (account_id, num_messages) in [(&account_id_1, 4), (&account_id_2, 2), (&account_id_3, 2)] {
for (account, num_messages) in [(john, 4), (jane, 2), (bill, 2)] {
assert_eq!(
server
.get_document_ids(
Id::from_str(account_id).unwrap().document_id(),
Collection::Email
)
.get_document_ids(account.id().document_id(), Collection::Email)
.await
.unwrap()
.unwrap()
.len(),
num_messages,
"for {}",
account_id
account.id_string()
);
}
@@ -281,7 +253,7 @@ END:VCARD
"members@example.com",
"jdoe@example.com",
"john.doe@example.com",
"jane@example.com",
"jane.smith@example.com",
"bill@example.com",
],
concat!(
@@ -295,27 +267,23 @@ END:VCARD
)
.await;
for (account_id, num_messages) in [(&account_id_1, 5), (&account_id_2, 3), (&account_id_3, 3)] {
for (account, num_messages) in [(john, 5), (jane, 3), (bill, 3)] {
assert_eq!(
server
.get_document_ids(
Id::from_str(account_id).unwrap().document_id(),
Collection::Email
)
.get_document_ids(account.id().document_id(), Collection::Email)
.await
.unwrap()
.unwrap()
.len(),
num_messages,
"for {}",
account_id
account.id_string()
);
}
// Remove test data
for account_id in [&account_id_1, &account_id_2, &account_id_3] {
params.client.set_default_account_id(account_id);
destroy_all_mailboxes(params).await;
for account in [john, jane, bill] {
params.destroy_all_mailboxes(account).await;
}
assert_is_empty(server).await;

View File

@@ -4,9 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::jmap::{
JMAPTest, assert_is_empty, mail::mailbox::destroy_all_mailboxes, replace_blob_ids,
};
use crate::jmap::{JMAPTest, assert_is_empty, replace_blob_ids};
use ::email::mailbox::INBOX_ID;
use jmap_client::email::{self, Header, HeaderForm, import::EmailImportResponse};
use mail_parser::HeaderName;
@@ -23,7 +21,8 @@ pub async fn test(params: &mut JMAPTest) {
test_dir.push("email_get");
let mailbox_id = Id::from(INBOX_ID).to_string();
params.client.set_default_account_id(Id::from(1u64));
let account = params.account("jdoe@example.com");
let client = account.client();
for file_name in fs::read_dir(&test_dir).unwrap() {
let mut file_name = file_name.as_ref().unwrap().path();
@@ -36,13 +35,11 @@ pub async fn test(params: &mut JMAPTest) {
let blob_len = blob.len();
// Import email
let mut request = params.client.build();
let mut request = client.build();
let import_request = request
.import_email()
.account_id(Id::from(1u64).to_string())
.email(
params
.client
client
.upload(None, blob, None)
.await
.unwrap()
@@ -56,7 +53,7 @@ pub async fn test(params: &mut JMAPTest) {
assert_ne!(response.old_state(), Some(response.new_state()));
let email = response.created(&id).unwrap();
let mut request = params.client.build();
let mut request = client.build();
request
.get_email()
.ids([email.id().unwrap()])
@@ -139,7 +136,7 @@ pub async fn test(params: &mut JMAPTest) {
if is_headers_test {
for property in all_headers() {
let mut request = params.client.build();
let mut request = client.build();
request
.get_email()
.ids([email.id().unwrap()])
@@ -169,7 +166,7 @@ pub async fn test(params: &mut JMAPTest) {
}
}
destroy_all_mailboxes(params).await;
params.destroy_all_mailboxes(account).await;
assert_is_empty(server).await;
}

View File

@@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::jmap::{JMAPTest, assert_is_empty, wait_for_index};
use crate::jmap::{Account, JMAPTest, assert_is_empty, wait_for_index};
use jmap_client::{
Error, Set,
client::{Client, Credentials},
@@ -23,11 +23,12 @@ use types::id::Id;
pub async fn test(params: &mut JMAPTest) {
println!("Running Mailbox tests...");
let server = params.server.clone();
let client = &mut params.client;
let account = params.account("admin");
let mut client = account.client_owned().await;
// Create test mailboxes
client.set_default_account_id(Id::from(0u64));
let id_map = create_test_mailboxes(client).await;
let id_map = create_test_mailboxes(&client).await;
// Sort by name
assert_eq!(
@@ -607,12 +608,11 @@ pub async fn test(params: &mut JMAPTest) {
["inbox", "sent", "spam"]
);
destroy_all_mailboxes(params).await;
params.client.set_default_account_id(Id::from(1u64));
destroy_all_mailboxes_no_wait(&client).await;
assert_is_empty(server).await;
}
async fn create_test_mailboxes(client: &mut Client) -> AHashMap<String, String> {
async fn create_test_mailboxes(client: &Client) -> AHashMap<String, String> {
let mut mailbox_map = AHashMap::default();
let mut request = client.build();
build_create_query(
@@ -657,6 +657,13 @@ fn build_create_query(
}
}
impl JMAPTest {
pub async fn destroy_all_mailboxes(&self, account: &Account) {
wait_for_index(&self.server).await;
destroy_all_mailboxes_no_wait(account.client()).await;
}
}
pub async fn destroy_all_mailboxes_for_account(account_id: u32) {
let mut client = Client::new()
.credentials(Credentials::basic("admin", "secret"))
@@ -670,11 +677,6 @@ pub async fn destroy_all_mailboxes_for_account(account_id: u32) {
destroy_all_mailboxes_no_wait(&client).await;
}
pub async fn destroy_all_mailboxes(test: &JMAPTest) {
wait_for_index(&test.server).await;
destroy_all_mailboxes_no_wait(&test.client).await;
}
pub async fn destroy_all_mailboxes_no_wait(client: &Client) {
let mut request = client.build();
request.query_mailbox().arguments().sort_as_tree(true);

View File

@@ -4,30 +4,25 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::jmap::{
JMAPTest, assert_is_empty,
mail::{get::all_headers, mailbox::destroy_all_mailboxes},
replace_blob_ids,
};
use crate::jmap::{JMAPTest, assert_is_empty, mail::get::all_headers, replace_blob_ids};
use jmap_client::{
email::{self, Header, HeaderForm},
mailbox::Role,
};
use std::{fs, path::PathBuf};
use types::id::Id;
pub async fn test(params: &mut JMAPTest) {
println!("Running Email Parse tests...");
let server = params.server.clone();
let account = params.account("jdoe@example.com");
let client = account.client();
let mut test_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
test_dir.push("resources");
test_dir.push("jmap");
test_dir.push("email_parse");
let mailbox_id = params
.client
.set_default_account_id(Id::new(1).to_string())
let mailbox_id = client
.mailbox_create("JMAP Parse", None::<String>, Role::None)
.await
.unwrap()
@@ -38,8 +33,7 @@ pub async fn test(params: &mut JMAPTest) {
let mut test_file = test_dir.clone();
test_file.push(test_name);
let email = params
.client
let email = client
.email_import(
fs::read(&test_file).unwrap(),
[mailbox_id.clone()],
@@ -49,8 +43,7 @@ pub async fn test(params: &mut JMAPTest) {
.await
.unwrap();
let blob_id = params
.client
let blob_id = client
.email_get(email.id().unwrap(), Some([email::Property::Attachments]))
.await
.unwrap()
@@ -63,8 +56,7 @@ pub async fn test(params: &mut JMAPTest) {
.unwrap()
.to_string();
let email = params
.client
let email = client
.email_parse(
&blob_id,
[
@@ -123,7 +115,7 @@ pub async fn test(params: &mut JMAPTest) {
for part in parts {
let blob_id = part.blob_id().unwrap();
let inner_blob = params.client.download(blob_id).await.unwrap();
let inner_blob = client.download(blob_id).await.unwrap();
test_file.set_extension(format!("part{}", part.part_id().unwrap()));
@@ -154,15 +146,13 @@ pub async fn test(params: &mut JMAPTest) {
// Test header parsing on a temporary blob
let mut test_file = test_dir;
test_file.push("headers.eml");
let blob_id = params
.client
let blob_id = client
.upload(None, fs::read(&test_file).unwrap(), None)
.await
.unwrap()
.take_blob_id();
let mut email = params
.client
let mut email = client
.email_parse(
&blob_id,
[
@@ -213,8 +203,7 @@ pub async fn test(params: &mut JMAPTest) {
for property in all_headers() {
email.headers.extend(
params
.client
client
.email_parse(&blob_id, [property].into(), [].into(), None)
.await
.unwrap()
@@ -233,6 +222,6 @@ pub async fn test(params: &mut JMAPTest) {
panic!("Test failed, output saved to {}", test_file.display());
}
destroy_all_mailboxes(params).await;
params.destroy_all_mailboxes(account).await;
assert_is_empty(server).await;
}

View File

@@ -5,7 +5,7 @@
*/
use crate::{
jmap::{JMAPTest, assert_is_empty, mail::mailbox::destroy_all_mailboxes, wait_for_index},
jmap::{JMAPTest, assert_is_empty, wait_for_index},
store::{deflate_test_resource, query::FIELDS},
};
use ::email::{cache::MessageCacheFetch, mailbox::Mailbox};
@@ -31,8 +31,9 @@ const MAX_MESSAGES_PER_THREAD: usize = 100;
pub async fn test(params: &mut JMAPTest, insert: bool) {
println!("Running Email Query tests...");
let server = params.server.clone();
let client = &mut params.client;
client.set_default_account_id(Id::new(1));
let account = params.account("jdoe@example.com");
let client = account.client();
if insert {
// Add some "virtual" mailbox ids so create doesn't fail
let mut batch = BatchBuilder::new();
@@ -105,11 +106,11 @@ pub async fn test(params: &mut JMAPTest, insert: bool) {
.unwrap_set_email()
.unwrap();
destroy_all_mailboxes(params).await;
params.destroy_all_mailboxes(account).await;
assert_is_empty(server).await;
}
pub async fn query(client: &mut Client) {
pub async fn query(client: &Client) {
for (filter, sort, expected_results) in [
(
Filter::and(vec![
@@ -403,7 +404,7 @@ pub async fn query(client: &mut Client) {
}
}
pub async fn query_options(client: &mut Client) {
pub async fn query_options(client: &Client) {
for (query, expected_results, expected_results_collapsed) in [
(
EmailQuery {
@@ -698,7 +699,7 @@ pub async fn query_options(client: &mut Client) {
}
}
pub async fn create(client: &mut Client) {
pub async fn create(client: &Client) {
let sent_at = now();
let now = Instant::now();
let mut fields = AHashMap::default();
@@ -838,7 +839,7 @@ pub async fn create(client: &mut Client) {
);
}
async fn get_anchor(client: &mut Client, anchor: &str) -> Option<String> {
async fn get_anchor(client: &Client, anchor: &str) -> Option<String> {
client
.email_query(
email::query::Filter::header("Message-Id", anchor.into()).into(),

View File

@@ -6,10 +6,7 @@
use crate::jmap::{
JMAPTest, assert_is_empty,
mail::{
changes::{LogAction, ParseState},
mailbox::destroy_all_mailboxes,
},
mail::changes::{LogAction, ParseState},
};
use ::email::message::metadata::MessageData;
use common::storage::index::ObjectIndexBuilder;
@@ -32,15 +29,15 @@ use types::{
pub async fn test(params: &mut JMAPTest) {
println!("Running Email QueryChanges tests...");
let server = params.server.clone();
let mailbox1_id = params
.client
.set_default_account_id(Id::new(1).to_string())
let account = params.account("jdoe@example.com");
let client = account.client();
let mailbox1_id = client
.mailbox_create("JMAP Changes 1", None::<String>, Role::None)
.await
.unwrap()
.take_id();
let mailbox2_id = params
.client
let mailbox2_id = client
.mailbox_create("JMAP Changes 2", None::<String>, Role::None)
.await
.unwrap()
@@ -91,8 +88,7 @@ pub async fn test(params: &mut JMAPTest) {
match &change {
LogAction::Insert(id) => {
let jmap_id = Id::from_str(
params
.client
client
.email_import(
format!(
"From: test_{}\nSubject: test_{}\n\ntest",
@@ -131,7 +127,7 @@ pub async fn test(params: &mut JMAPTest) {
}
LogAction::Delete(id) => {
let id = *id_map.get(id).unwrap();
params.client.email_destroy(&id.to_string()).await.unwrap();
client.email_destroy(&id.to_string()).await.unwrap();
removed_ids.insert(id);
}
LogAction::Move(from, to) => {
@@ -141,7 +137,11 @@ pub async fn test(params: &mut JMAPTest) {
//let new_thread_id = store::rand::random::<u32>();
let old_message_ = server
.get_archive(1, Collection::Email, id.document_id())
.get_archive(
account.id().document_id(),
Collection::Email,
id.document_id(),
)
.await
.unwrap()
.unwrap();
@@ -155,7 +155,7 @@ pub async fn test(params: &mut JMAPTest) {
.data
.write(
BatchBuilder::new()
.with_account_id(1)
.with_account_id(account.id().document_id())
.with_collection(Collection::Email)
.update_document(id.document_id())
.custom(
@@ -220,7 +220,7 @@ pub async fn test(params: &mut JMAPTest) {
if test_num == 3 && query.up_to_id.is_none() {
continue;
}
let mut request = params.client.build();
let mut request = client.build();
let query_request = request
.query_email_changes(query.since_query_state.to_string())
.sort(query.sort);
@@ -276,7 +276,7 @@ pub async fn test(params: &mut JMAPTest) {
states.push(new_state);
}
destroy_all_mailboxes(params).await;
params.destroy_all_mailboxes(account).await;
assert_is_empty(server).await;
}

View File

@@ -4,9 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::jmap::{
JMAPTest, assert_is_empty, mail::mailbox::destroy_all_mailboxes, wait_for_index,
};
use crate::jmap::{JMAPTest, assert_is_empty, wait_for_index};
use email::mailbox::INBOX_ID;
use jmap_client::{core::query, email::query::Filter};
use std::{fs, path::PathBuf};
@@ -16,9 +14,9 @@ use types::id::Id;
pub async fn test(params: &mut JMAPTest) {
println!("Running SearchSnippet tests...");
let server = params.server.clone();
let account = params.account("jdoe@example.com");
let client = account.client();
let mailbox_id = Id::from(INBOX_ID).to_string();
params.client.set_default_account_id(Id::from(1u64));
let mut email_ids = AHashMap::default();
@@ -37,8 +35,7 @@ pub async fn test(params: &mut JMAPTest) {
] {
let mut file_name = test_dir.clone();
file_name.push(format!("{}.eml", email_name));
let email_id = params
.client
let email_id = client
.email_import(
fs::read(&file_name).unwrap(),
[&mailbox_id],
@@ -135,7 +132,7 @@ pub async fn test(params: &mut JMAPTest) {
)),
),
] {
let mut request = params.client.build();
let mut request = client.build();
let result_ref = request
.query_email()
.filter(filter.clone())
@@ -166,6 +163,6 @@ pub async fn test(params: &mut JMAPTest) {
}
// Destroy test data
destroy_all_mailboxes(params).await;
params.destroy_all_mailboxes(account).await;
assert_is_empty(server).await;
}

View File

@@ -5,8 +5,7 @@
*/
use crate::jmap::{
JMAPTest, assert_is_empty, find_values, mail::mailbox::destroy_all_mailboxes, replace_blob_ids,
replace_boundaries, replace_values,
JMAPTest, assert_is_empty, find_values, replace_blob_ids, replace_boundaries, replace_values,
};
use ::email::mailbox::INBOX_ID;
use ahash::AHashSet;
@@ -23,18 +22,18 @@ use types::id::Id;
pub async fn test(params: &mut JMAPTest) {
println!("Running Email Set tests...");
let server = params.server.clone();
let account = params.account("jdoe@example.com");
let client = account.client();
let mailbox_id = Id::from(INBOX_ID).to_string();
params.client.set_default_account_id(Id::from(1u64));
create(&mut params.client, &mailbox_id).await;
update(&mut params.client, &mailbox_id).await;
create(client, &mailbox_id).await;
update(client, &mailbox_id).await;
destroy_all_mailboxes(params).await;
params.destroy_all_mailboxes(account).await;
assert_is_empty(server).await;
}
async fn create(client: &mut Client, mailbox_id: &str) {
async fn create(client: &Client, mailbox_id: &str) {
let mut test_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
test_dir.push("resources");
test_dir.push("jmap");
@@ -162,7 +161,7 @@ async fn create(client: &mut Client, mailbox_id: &str) {
}
}
async fn update(client: &mut Client, root_mailbox_id: &str) {
async fn update(client: &Client, root_mailbox_id: &str) {
// Obtain all messageIds previously created
let mailbox = client
.email_query(
@@ -174,13 +173,11 @@ async fn update(client: &mut Client, root_mailbox_id: &str) {
// Create two test mailboxes
let test_mailbox1_id = client
.set_default_account_id(Id::new(1).to_string())
.mailbox_create("Test 1", None::<String>, Role::None)
.await
.unwrap()
.take_id();
let test_mailbox2_id = client
.set_default_account_id(Id::new(1).to_string())
.mailbox_create("Test 2", None::<String>, Role::None)
.await
.unwrap()
@@ -296,7 +293,7 @@ async fn update(client: &mut Client, root_mailbox_id: &str) {
}
pub async fn assert_email_properties(
client: &mut Client,
client: &Client,
message_id: &str,
mailbox_ids: &[&str],
keywords: &[&str],

View File

@@ -5,12 +5,10 @@
*/
use crate::{
directory::internal::TestInternalDirectory,
jmap::{
JMAPTest, assert_is_empty,
mail::{
delivery::SmtpConnection,
mailbox::destroy_all_mailboxes,
submission::{MockMessage, assert_message_delivery, spawn_mock_smtp_server},
},
},
@@ -27,29 +25,12 @@ use std::{
path::PathBuf,
time::{Duration, Instant},
};
use types::id::Id;
pub async fn test(params: &mut JMAPTest) {
println!("Running Sieve tests...");
let server = params.server.clone();
let client = &mut params.client;
// Create test account
let account_id = Id::from(
server
.core
.storage
.data
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await,
)
.to_string();
client.set_default_account_id(&account_id);
let account = params.account("jdoe@example.com");
let client = account.client();
// Validate scripts
client
@@ -515,7 +496,7 @@ pub async fn test(params: &mut JMAPTest) {
for id in request.send_query_sieve_script().await.unwrap().take_ids() {
client.sieve_script_destroy(&id).await.unwrap();
}
destroy_all_mailboxes(params).await;
params.destroy_all_mailboxes(account).await;
assert_is_empty(server).await;
}

View File

@@ -5,11 +5,7 @@
*/
use crate::{
directory::internal::TestInternalDirectory,
jmap::{
JMAPTest, assert_is_empty,
mail::{mailbox::destroy_all_mailboxes, set::assert_email_properties},
},
jmap::{JMAPTest, assert_is_empty, mail::set::assert_email_properties},
smtp::DnsCache,
};
use ahash::AHashMap;
@@ -66,7 +62,8 @@ pub async fn test(params: &mut JMAPTest) {
println!("Running E-mail submissions tests...");
// Start mock SMTP server
let server = params.server.clone();
let client = &mut params.client;
let account = params.account("jdoe@example.com");
let client = account.client();
let (mut smtp_rx, smtp_settings) = spawn_mock_smtp_server();
server.ipv4_add(
"localhost",
@@ -74,25 +71,7 @@ pub async fn test(params: &mut JMAPTest) {
Instant::now() + std::time::Duration::from_secs(10),
);
// Create a test account
let server = params.server.clone();
let account_id = Id::from(
server
.core
.storage
.data
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com", "john.doe@example.com"],
)
.await,
)
.to_string();
// Test automatic identity creation
client.set_default_account_id(&account_id);
for (identity_id, email) in [(2u64, "jdoe@example.com"), (1u64, "john.doe@example.com")] {
let identity = client
.identity_get(&Id::from(identity_id).to_string(), None)
@@ -497,7 +476,7 @@ pub async fn test(params: &mut JMAPTest) {
.await;
client.email_submission_destroy(&id).await.unwrap();
}
destroy_all_mailboxes(params).await;
params.destroy_all_mailboxes(account).await;
assert_is_empty(server).await;
}

View File

@@ -4,17 +4,16 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::jmap::{JMAPTest, assert_is_empty, mail::mailbox::destroy_all_mailboxes};
use crate::jmap::{JMAPTest, assert_is_empty};
use jmap_client::mailbox::Role;
use types::id::Id;
pub async fn test(params: &mut JMAPTest) {
println!("Running Email Thread tests...");
let server = params.server.clone();
let account = params.account("jdoe@example.com");
let client = account.client();
let mailbox_id = params
.client
.set_default_account_id(Id::new(1).to_string())
let mailbox_id = client
.mailbox_create("JMAP Get", None::<String>, Role::None)
.await
.unwrap()
@@ -24,8 +23,7 @@ pub async fn test(params: &mut JMAPTest) {
let mut thread_id = "".to_string();
for num in [5, 3, 1, 2, 4] {
let mut email = params
.client
let mut email = client
.email_import(
format!("Subject: test\nReferences: <1234>\n\n{}", num).into_bytes(),
[&mailbox_id],
@@ -39,8 +37,7 @@ pub async fn test(params: &mut JMAPTest) {
}
assert_eq!(
params
.client
client
.thread_get(&thread_id)
.await
.unwrap()
@@ -49,6 +46,6 @@ pub async fn test(params: &mut JMAPTest) {
expected_result
);
destroy_all_mailboxes(params).await;
params.destroy_all_mailboxes(account).await;
assert_is_empty(server).await;
}

View File

@@ -5,10 +5,13 @@
*/
use crate::{
jmap::{JMAPTest, assert_is_empty, mail::mailbox::destroy_all_mailboxes},
jmap::{JMAPTest, assert_is_empty, mail::mailbox::destroy_all_mailboxes_no_wait},
store::deflate_test_resource,
};
use ::email::message::ingest::{EmailIngest, IngestEmail, IngestSource};
use ::email::{
mailbox::INBOX_ID,
message::ingest::{EmailIngest, IngestEmail, IngestSource},
};
use common::auth::AccessToken;
use jmap_client::{email, mailbox::Role};
use mail_parser::{MessageParser, mailbox::mbox::MessageIterator};
@@ -27,7 +30,8 @@ pub async fn test(params: &mut JMAPTest) {
async fn test_single_thread(params: &mut JMAPTest) {
println!("Running Email Merge Threads tests...");
let server = params.server.clone();
let client = &mut params.client;
let account = params.account("admin");
let mut client = account.client_owned().await;
let mut all_mailboxes = AHashMap::default();
for (base_test_num, test) in [test_1(), test_2(), test_3()].iter().enumerate() {
@@ -195,10 +199,8 @@ async fn test_single_thread(params: &mut JMAPTest) {
// Delete all messages and make sure no keys are left in the store.
for (base_test_num, mailbox_ids) in all_mailboxes {
for (test_num, _) in mailbox_ids.into_iter().enumerate() {
params
.client
.set_default_account_id(Id::new((base_test_num + test_num) as u64).to_string());
destroy_all_mailboxes(params).await;
client.set_default_account_id(Id::new((base_test_num + test_num) as u64).to_string());
destroy_all_mailboxes_no_wait(&client).await;
}
}
@@ -208,38 +210,25 @@ async fn test_single_thread(params: &mut JMAPTest) {
#[allow(dead_code)]
async fn test_multi_thread(params: &mut JMAPTest) {
println!("Running Email Merge Threads tests (multi-threaded)...");
//let semaphore = sync::Arc::Arc::new(tokio::sync::Semaphore::new(100));
let mut handles = vec![];
let mailbox_id = Id::from_str(
params
.client
.set_default_account_id(Id::new(0u64).to_string())
.mailbox_create("Inbox", None::<String>, Role::None)
.await
.unwrap()
.id()
.unwrap(),
)
.unwrap()
.document_id();
let account = params.account("jdoe@example.com");
let account_id = account.id().document_id();
let mailbox_id = INBOX_ID;
for message in MessageIterator::new(Cursor::new(deflate_test_resource("mailbox.gz")))
.collect::<Vec<_>>()
.into_iter()
{
//let permit = Arc::clone(&semaphore);
let message = message.unwrap();
let server = params.server.clone();
handles.push(tokio::task::spawn(async move {
//let _permit = permit.acquire().await.expect("Failed to acquire permit");
let mut retry_count = 0;
loop {
match server
.email_ingest(IngestEmail {
raw_message: message.contents(),
message: MessageParser::new().parse(message.contents()),
access_token: &AccessToken::from_id(0),
access_token: &AccessToken::from_id(account_id),
mailbox_ids: vec![mailbox_id],
keywords: vec![],
received_at: None,
@@ -278,14 +267,14 @@ async fn test_multi_thread(params: &mut JMAPTest) {
messages as u64,
params
.server
.get_document_ids(0, Collection::Email)
.get_document_ids(account_id, Collection::Email)
.await
.unwrap()
.unwrap()
.len()
);
println!("Deleting all messages...");
destroy_all_mailboxes(params).await;
params.destroy_all_mailboxes(account).await;
assert_is_empty(params.server.clone()).await;
}

View File

@@ -5,12 +5,10 @@
*/
use crate::{
directory::internal::TestInternalDirectory,
jmap::{
JMAPTest, assert_is_empty,
mail::{
delivery::SmtpConnection,
mailbox::destroy_all_mailboxes,
submission::{
MockMessage, assert_message_delivery, expect_nothing, spawn_mock_smtp_server,
},
@@ -20,29 +18,14 @@ use crate::{
};
use chrono::{TimeDelta, Utc};
use std::time::Instant;
use types::id::Id;
pub async fn test(params: &mut JMAPTest) {
println!("Running Vacation Response tests...");
// Create test account
let server = params.server.clone();
let client = &mut params.client;
let account_id = Id::from(
server
.core
.storage
.data
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await,
)
.to_string();
client.set_default_account_id(&account_id);
let account = params.account("jdoe@example.com");
let client = account.client();
// Start mock SMTP server
let (mut smtp_rx, smtp_settings) = spawn_mock_smtp_server();
@@ -54,7 +37,6 @@ pub async fn test(params: &mut JMAPTest) {
// Let people know that we'll be down in Kokomo
client
.set_default_account_id(&account_id)
.vacation_response_create(
"Off the Florida Keys there's a place called Kokomo",
"That's where you wanna go to get away from it all".into(),
@@ -179,6 +161,6 @@ pub async fn test(params: &mut JMAPTest) {
// Remove test data
client.vacation_response_destroy().await.unwrap();
destroy_all_mailboxes(params).await;
params.destroy_all_mailboxes(account).await;
assert_is_empty(server).await;
}

View File

@@ -13,6 +13,7 @@ use crate::{
},
store::TempDir,
};
use ahash::AHashMap;
use base64::{
Engine,
engine::general_purpose::{self, STANDARD},
@@ -40,9 +41,15 @@ use managesieve::core::ManageSieveSessionManager;
use pop3::Pop3SessionManager;
use reqwest::header;
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use serde_json::{Value, json};
use services::SpawnServices;
use smtp::{SpawnQueueManager, core::SmtpSessionManager};
use std::{fmt::Debug, path::PathBuf, sync::Arc, time::Duration};
use std::{
fmt::{Debug, Display},
path::PathBuf,
sync::Arc,
time::Duration,
};
use store::{
IterateParams, SUBSPACE_PROPERTY, Stores, ValueKey,
roaring::RoaringBitmap,
@@ -83,7 +90,7 @@ async fn jmap_tests() {
mail::thread_merge::test(&mut params).await;
mail::mailbox::test(&mut params).await;
mail::delivery::test(&mut params).await;
mail::acl::test(&mut params).await;*/
mail::acl::test(&mut params).await;
auth::limits::test(&mut params).await;
auth::oauth::test(&mut params).await;
core::event_source::test(&mut params).await;
@@ -93,7 +100,7 @@ async fn jmap_tests() {
mail::submission::test(&mut params).await;
core::websocket::test(&mut params).await;
auth::quota::test(&mut params).await;
mail::crypto::test(&mut params).await;
mail::crypto::test(&mut params).await;*/
core::blob::test(&mut params).await;
auth::permissions::test(&params).await;
server::purge::test(&mut params).await;
@@ -120,12 +127,63 @@ pub async fn jmap_metric_tests() {
#[allow(dead_code)]
pub struct JMAPTest {
server: Server,
client: Client,
accounts: AHashMap<&'static str, Account>,
temp_dir: TempDir,
webhook: Arc<MockWebhookEndpoint>,
shutdown_tx: watch::Sender<bool>,
}
pub struct Account {
name: &'static str,
secret: &'static str,
emails: &'static [&'static str],
id: Id,
id_string: String,
client: Client,
}
impl JMAPTest {
pub fn account(&self, name: &str) -> &Account {
self.accounts.get(name).unwrap()
}
}
impl Account {
pub fn id(&self) -> &Id {
&self.id
}
pub fn id_string(&self) -> &str {
&self.id_string
}
pub fn client(&self) -> &Client {
&self.client
}
pub fn name(&self) -> &str {
self.name
}
pub fn secret(&self) -> &str {
self.secret
}
pub fn emails(&self) -> &[&str] {
self.emails
}
pub async fn client_owned(&self) -> Client {
Client::new()
.credentials(Credentials::basic(self.name(), self.secret()))
.timeout(Duration::from_secs(3600))
.accept_invalid_certs(true)
.follow_redirects(["127.0.0.1"])
.connect("https://127.0.0.1:8899")
.await
.unwrap()
}
}
pub async fn wait_for_index(server: &Server) {
loop {
let mut has_index_tasks = false;
@@ -344,77 +402,177 @@ async fn init_jmap_tests(store_id: &str, delete_if_exists: bool) -> JMAPTest {
}
// Create tables
inner
.shared_core
.load()
.storage
.data
.create_test_user("admin", "secret", "Superuser", &[])
.await;
let server = inner.build_server();
let mut accounts = AHashMap::new();
// Create client
let mut client = Client::new()
.credentials(Credentials::basic("admin", "secret"))
.timeout(Duration::from_secs(3600))
.accept_invalid_certs(true)
.follow_redirects(["127.0.0.1"])
.connect("https://127.0.0.1:8899")
.await
.unwrap();
client.set_default_account_id(Id::new(1));
for (name, secret, description, emails) in [
("admin", "secret", "Superuser", &[][..]),
(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com", "john.doe@example.com"][..],
),
(
"jane.smith@example.com",
"abcde",
"Jane Smith",
&["jane.smith@example.com"],
),
(
"bill@example.com",
"098765",
"Bill Foobar",
&["bill@example.com"],
),
(
"robert@example.com",
"aabbcc",
"Robert Foobar",
&["robert@example.com"][..],
),
] {
let id: Id = server
.store()
.create_test_user(name, secret, description, emails)
.await
.into();
let id_string = id.to_string();
let mut client = Client::new()
.credentials(Credentials::basic(name, secret))
.timeout(Duration::from_secs(3600))
.accept_invalid_certs(true)
.follow_redirects(["127.0.0.1"])
.connect("https://127.0.0.1:8899")
.await
.unwrap();
client.set_default_account_id(id_string.clone());
accounts.insert(
name,
Account {
name,
secret,
emails,
id,
id_string,
client,
},
);
}
for (name, description, emails) in
[("sales@example.com", "Sales Group", &["sales@example.com"])]
{
let id: Id = server
.store()
.create_test_group(name, description, emails)
.await
.into();
let id_string = id.to_string();
let mut client = Client::new()
.credentials(Credentials::basic("admin", "secret"))
.timeout(Duration::from_secs(3600))
.accept_invalid_certs(true)
.follow_redirects(["127.0.0.1"])
.connect("https://127.0.0.1:8899")
.await
.unwrap();
client.set_default_account_id(id_string.clone());
accounts.insert(
name,
Account {
name,
secret: "",
emails,
id,
id_string,
client,
},
);
}
JMAPTest {
server: inner.build_server(),
server,
temp_dir,
client,
accounts,
shutdown_tx,
webhook: spawn_mock_webhook_endpoint(),
}
}
pub async fn jmap_raw_request(body: impl AsRef<str>, username: &str, secret: &str) -> String {
let mut headers = header::HeaderMap::new();
pub struct JmapResponse(pub Value);
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()
impl Account {
pub async fn jmap_method_call(&self, method_name: &str, body: Value) -> JmapResponse {
self.jmap_method_calls(json!([[method_name, body, "0"]]))
.await
.unwrap()
.bytes()
.await
.unwrap()
.to_vec(),
)
.unwrap()
}
pub async fn jmap_method_calls(&self, calls: Value) -> JmapResponse {
let mut headers = header::HeaderMap::new();
headers.insert(
header::AUTHORIZATION,
header::HeaderValue::from_str(&format!(
"Basic {}",
general_purpose::STANDARD.encode(format!("{}:{}", self.name(), self.secret()))
))
.unwrap(),
);
let body = json!({
"using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:ietf:params:jmap:quota" ],
"methodCalls": calls
});
JmapResponse(
serde_json::from_slice(
&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.to_string())
.send()
.await
.unwrap()
.bytes()
.await
.unwrap(),
)
.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()
impl JmapResponse {
pub fn pointer(&self, pointer: &str) -> Option<&Value> {
self.0.pointer(pointer)
}
pub fn into_inner(self) -> Value {
self.0
}
}
impl Display for JmapResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
impl Debug for JmapResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
serde_json::to_string_pretty(&self.0)
.map_err(|_| std::fmt::Error)
.and_then(|s| std::fmt::Display::fmt(&s, f))
}
}
pub fn find_values(string: &str, name: &str) -> Vec<String> {
@@ -473,17 +631,6 @@ pub fn replace_blob_ids(string: String) -> String {
}
}
pub async fn test_account_login(login: &str, secret: &str) -> Client {
Client::new()
.credentials(Credentials::basic(login, secret))
.timeout(Duration::from_secs(5))
.accept_invalid_certs(true)
.follow_redirects(["127.0.0.1"])
.connect("https://127.0.0.1:8899")
.await
.unwrap()
}
#[derive(Deserialize)]
#[serde(untagged)]
pub enum Response<T> {

View File

@@ -127,22 +127,18 @@ pub async fn test(params: &mut JMAPTest) {
);
// Create test account
params
.server
.inner
.shared_core
.load()
.storage
.data
let server = params.server.inner.build_server();
server
.store()
.create_test_user(
"jdoe@example.com",
"secret",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await;
alerts(&params.server.inner.build_server()).await;
alerts(&server).await;
undelete(params).await;
tracing(params).await;
metrics(params).await;
@@ -351,9 +347,7 @@ async fn metrics(params: &mut JMAPTest) {
async fn undelete(_params: &mut JMAPTest) {
// Authenticate
let mut imap = ImapConnection::connect(b"_x ").await;
imap.send("AUTHENTICATE PLAIN {32+}\r\nAGpkb2VAZXhhbXBsZS5jb20Ac2VjcmV0")
.await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
imap.authenticate("jdoe@example.com", "12345").await;
// Insert test message
imap.send("STATUS INBOX (MESSAGES)").await;
@@ -390,9 +384,7 @@ async fn undelete(_params: &mut JMAPTest) {
imap.send("LOGOUT").await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
let mut imap = ImapConnection::connect(b"_x ").await;
imap.send("AUTHENTICATE PLAIN {32+}\r\nAGpkb2VAZXhhbXBsZS5jb20Ac2VjcmV0")
.await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
imap.authenticate("jdoe@example.com", "12345").await;
// Make sure the message is gone
imap.send("STATUS INBOX (MESSAGES)").await;

View File

@@ -5,7 +5,6 @@
*/
use crate::{
directory::internal::TestInternalDirectory,
imap::{AssertResult, ImapConnection, Type},
jmap::{JMAPTest, assert_is_empty},
};
@@ -24,23 +23,11 @@ use types::{collection::Collection, id::Id};
pub async fn test(params: &mut JMAPTest) {
println!("Running purge tests...");
let server = params.server.clone();
let client = &mut params.client;
let inbox_id = Id::from(INBOX_ID).to_string();
let trash_id = Id::from(TRASH_ID).to_string();
let junk_id = Id::from(JUNK_ID).to_string();
// Connect to IMAP
let account_id = server
.core
.storage
.data
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await;
let account = params.account("jdoe@example.com");
let client = account.client();
let mut imap = ImapConnection::connect(b"_x ").await;
imap.assert_read(Type::Untagged, ResponseType::Ok).await;
@@ -52,7 +39,6 @@ pub async fn test(params: &mut JMAPTest) {
.assert_contains("MESSAGES 0");
// Create test messages
client.set_default_account_id(Id::from(account_id));
let mut message_ids = Vec::new();
let mut pass = 0;
let mut changes = AHashSet::new();
@@ -107,7 +93,7 @@ pub async fn test(params: &mut JMAPTest) {
// Make sure both messages and changes are present
assert_eq!(
server
.get_document_ids(account_id, Collection::Email)
.get_document_ids(account.id().document_id(), Collection::Email)
.await
.unwrap()
.unwrap()
@@ -116,13 +102,16 @@ pub async fn test(params: &mut JMAPTest) {
);
// Purge junk/trash messages and old changes
server.purge_account(account_id).await;
let cache = server.get_cached_messages(account_id).await.unwrap();
server.purge_account(account.id().document_id()).await;
let cache = server
.get_cached_messages(account.id().document_id())
.await
.unwrap();
// Only 4 messages should remain
assert_eq!(
server
.get_document_ids(account_id, Collection::Email)
.get_document_ids(account.id().document_id(), Collection::Email)
.await
.unwrap()
.unwrap()
@@ -161,7 +150,7 @@ pub async fn test(params: &mut JMAPTest) {
.core
.storage
.data
.delete_principal(QueryBy::Id(account_id))
.delete_principal(QueryBy::Id(account.id().document_id()))
.await
.unwrap();
assert_is_empty(server).await;