From 0936c21fbc6dd693e7f12d6a35d15ca7968eaeaa Mon Sep 17 00:00:00 2001 From: mdecimus Date: Sun, 12 Oct 2025 13:03:54 +0200 Subject: [PATCH] JMAP tests cleanup --- crates/directory/src/core/principal.rs | 121 +++------ crates/directory/src/lib.rs | 1 + crates/email/src/mailbox/destroy.rs | 12 +- crates/jmap/src/quota/get.rs | 35 ++- tests/src/directory/internal.rs | 37 +++ tests/src/jmap/auth/limits.rs | 60 +++-- tests/src/jmap/auth/oauth.rs | 32 +-- tests/src/jmap/auth/permissions.rs | 16 +- tests/src/jmap/auth/quota.rs | 104 +++----- tests/src/jmap/core/blob.rs | 326 +++++++++-------------- tests/src/jmap/core/event_source.rs | 50 +--- tests/src/jmap/core/push_subscription.rs | 31 +-- tests/src/jmap/core/websocket.rs | 32 +-- tests/src/jmap/mail/acl.rs | 230 ++++++---------- tests/src/jmap/mail/changes.rs | 19 +- tests/src/jmap/mail/copy.rs | 26 +- tests/src/jmap/mail/crypto.rs | 25 +- tests/src/jmap/mail/delivery.rs | 110 +++----- tests/src/jmap/mail/get.rs | 19 +- tests/src/jmap/mail/mailbox.rs | 24 +- tests/src/jmap/mail/parse.rs | 35 +-- tests/src/jmap/mail/query.rs | 17 +- tests/src/jmap/mail/query_changes.rs | 32 +-- tests/src/jmap/mail/search_snippet.rs | 15 +- tests/src/jmap/mail/set.rs | 21 +- tests/src/jmap/mail/sieve_script.rs | 25 +- tests/src/jmap/mail/submission.rs | 29 +- tests/src/jmap/mail/thread_get.rs | 17 +- tests/src/jmap/mail/thread_merge.rs | 41 ++- tests/src/jmap/mail/vacation_response.rs | 24 +- tests/src/jmap/mod.rs | 293 +++++++++++++++----- tests/src/jmap/server/enterprise.rs | 22 +- tests/src/jmap/server/purge.rs | 31 +-- 33 files changed, 851 insertions(+), 1061 deletions(-) diff --git a/crates/directory/src/core/principal.rs b/crates/directory/src/core/principal.rs index ab413543..655666da 100644 --- a/crates/directory/src/core/principal.rs +++ b/crates/directory/src/core/principal.rs @@ -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::()?; + 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::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::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::()?; @@ -1263,66 +1280,6 @@ impl<'de> serde::Deserialize<'de> for StringOrU64 { } } -#[derive(Debug)] -enum StringOrMany { - One(String), - Many(Vec), -} - -impl<'de> serde::Deserialize<'de> for StringOrMany { - fn deserialize(deserializer: D) -> Result - 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(self, value: &str) -> Result - 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(self, v: String) -> Result - 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(self, mut seq: A) -> Result - where - A: de::SeqAccess<'de>, - { - let mut vec = Vec::new(); - - while let Some(value) = seq.next_element::()? { - vec.push(value); - } - - Ok(StringOrMany::Many(vec)) - } - } - - deserializer.deserialize_any(StringOrManyVisitor) - } -} - impl Permission { pub fn all() -> impl Iterator { (0..Permission::COUNT as u32).filter_map(Permission::from_id) diff --git a/crates/directory/src/lib.rs b/crates/directory/src/lib.rs index 759be326..b8807f88 100644 --- a/crates/directory/src/lib.rs +++ b/crates/directory/src/lib.rs @@ -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) } diff --git a/crates/email/src/mailbox/destroy.rs b/crates/email/src/mailbox/destroy.rs index 815f3911..a0fcf0a2 100644 --- a/crates/email/src/mailbox/destroy.rs +++ b/crates/email/src/mailbox/destroy.rs @@ -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, 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)); } diff --git a/crates/jmap/src/quota/get.rs b/crates/jmap/src/quota/get.rs index eec2c110..338ba81f 100644 --- a/crates/jmap/src/quota/get.rs +++ b/crates/jmap/src/quota/get.rs @@ -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), + Borrowed(&'x AccessToken), +} + +impl AccessTokenRef<'_> { + fn as_ref(&self) -> &AccessToken { + match self { + AccessTokenRef::Owned(token) => token, + AccessTokenRef::Borrowed(token) => token, + } + } +} diff --git a/tests/src/directory/internal.rs b/tests/src/directory/internal.rs index 279d5d7d..0e5edb4a 100644 --- a/tests/src/directory/internal.rs +++ b/tests/src/directory/internal.rs @@ -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); + async fn remove_permissions( + &self, + login: &str, + permissions: impl IntoIterator, + ); 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, + ) { + 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( diff --git a/tests/src/jmap/auth/limits.rs b/tests/src/jmap/auth/limits.rs index 143728c5..1af5e81c 100644 --- a/tests/src/jmap/auth/limits.rs +++ b/tests/src/jmap/auth/limits.rs @@ -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 diff --git a/tests/src/jmap/auth/oauth.rs b/tests/src/jmap/auth/oauth.rs index e7ab4d21..14546cbf 100644 --- a/tests/src/jmap/auth/oauth.rs +++ b/tests/src/jmap/auth/oauth.rs @@ -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::, None::>) @@ -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::, None::>) @@ -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; } diff --git a/tests/src/jmap/auth/permissions.rs b/tests/src/jmap/auth/permissions.rs index 1bf10198..cf8aefef 100644 --- a/tests/src/jmap/auth/permissions.rs +++ b/tests/src/jmap/auth/permissions.rs @@ -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, diff --git a/tests/src/jmap/auth/quota.rs b/tests/src/jmap/auth/quota.rs index c2dc2069..b173c3be 100644 --- a/tests/src/jmap/auth/quota.rs +++ b/tests/src/jmap/auth/quota.rs @@ -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::>, @@ -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::>, @@ -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()) diff --git a/tests/src/jmap/core/blob.rs b/tests/src/jmap/core/blob.rs index 57813636..1ec84b94 100644 --- a/tests/src/jmap/core/blob.rs +++ b/tests/src/jmap/core/blob.rs @@ -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; } diff --git a/tests/src/jmap/core/event_source.rs b/tests/src/jmap/core/event_source.rs index 9e255c5c..a4cae537 100644 --- a/tests/src/jmap/core/event_source.rs +++ b/tests/src/jmap/core/event_source.rs @@ -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::>, 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; } diff --git a/tests/src/jmap/core/push_subscription.rs b/tests/src/jmap/core/push_subscription.rs index bf28348e..1d097e58 100644 --- a/tests/src/jmap/core/push_subscription.rs +++ b/tests/src/jmap/core/push_subscription.rs @@ -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::(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; } diff --git a/tests/src/jmap/core/websocket.rs b/tests/src/jmap/core/websocket.rs index 78ef31be..459d79b6 100644 --- a/tests/src/jmap/core/websocket.rs +++ b/tests/src/jmap/core/websocket.rs @@ -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; } diff --git a/tests/src/jmap/mail/acl.rs b/tests/src/jmap/mail/acl.rs index 53692130..2783e883 100644 --- a/tests/src/jmap/mail/acl.rs +++ b/tests/src/jmap/mail/acl.rs @@ -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::>) .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::>) .await, ); assert_forbidden( john_client - .set_default_account_id(jane_id.to_string()) + .set_default_account_id(jane.id_string()) .email_query(None::, None::>) .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::, None::>) .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::>, @@ -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::>, @@ -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::, None::>) .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::, 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; } diff --git a/tests/src/jmap/mail/changes.rs b/tests/src/jmap/mail/changes.rs index 0fe7c54f..947dacb3 100644 --- a/tests/src/jmap/mail/changes.rs +++ b/tests/src/jmap/mail/changes.rs @@ -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(); diff --git a/tests/src/jmap/mail/copy.rs b/tests/src/jmap/mail/copy.rs index 8ad90caf..d24b1c81 100644 --- a/tests/src/jmap/mail/copy.rs +++ b/tests/src/jmap/mail/copy.rs @@ -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::, 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::, 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::>) .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::>) .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; } diff --git a/tests/src/jmap/mail/crypto.rs b/tests/src/jmap/mail/crypto.rs index 4c0ffa2e..ff9af836 100644 --- a/tests/src/jmap/mail/crypto.rs +++ b/tests/src/jmap/mail/crypto.rs @@ -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(); diff --git a/tests/src/jmap/mail/delivery.rs b/tests/src/jmap/mail/delivery.rs index 7f58bf59..9284b4c1 100644 --- a/tests/src/jmap/mail/delivery.rs +++ b/tests/src/jmap/mail/delivery.rs @@ -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; diff --git a/tests/src/jmap/mail/get.rs b/tests/src/jmap/mail/get.rs index c972021a..448cae4e 100644 --- a/tests/src/jmap/mail/get.rs +++ b/tests/src/jmap/mail/get.rs @@ -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; } diff --git a/tests/src/jmap/mail/mailbox.rs b/tests/src/jmap/mail/mailbox.rs index 4065a7af..981239de 100644 --- a/tests/src/jmap/mail/mailbox.rs +++ b/tests/src/jmap/mail/mailbox.rs @@ -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 { +async fn create_test_mailboxes(client: &Client) -> AHashMap { 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); diff --git a/tests/src/jmap/mail/parse.rs b/tests/src/jmap/mail/parse.rs index 9cc72a70..b9fa37c1 100644 --- a/tests/src/jmap/mail/parse.rs +++ b/tests/src/jmap/mail/parse.rs @@ -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::, 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; } diff --git a/tests/src/jmap/mail/query.rs b/tests/src/jmap/mail/query.rs index a074539b..b4f53a0f 100644 --- a/tests/src/jmap/mail/query.rs +++ b/tests/src/jmap/mail/query.rs @@ -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 { +async fn get_anchor(client: &Client, anchor: &str) -> Option { client .email_query( email::query::Filter::header("Message-Id", anchor.into()).into(), diff --git a/tests/src/jmap/mail/query_changes.rs b/tests/src/jmap/mail/query_changes.rs index a8481ed6..3e6f879f 100644 --- a/tests/src/jmap/mail/query_changes.rs +++ b/tests/src/jmap/mail/query_changes.rs @@ -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::, Role::None) .await .unwrap() .take_id(); - let mailbox2_id = params - .client + let mailbox2_id = client .mailbox_create("JMAP Changes 2", None::, 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::(); 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; } diff --git a/tests/src/jmap/mail/search_snippet.rs b/tests/src/jmap/mail/search_snippet.rs index df269b68..4bc1d0e8 100644 --- a/tests/src/jmap/mail/search_snippet.rs +++ b/tests/src/jmap/mail/search_snippet.rs @@ -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; } diff --git a/tests/src/jmap/mail/set.rs b/tests/src/jmap/mail/set.rs index 811cf7e3..285e62d3 100644 --- a/tests/src/jmap/mail/set.rs +++ b/tests/src/jmap/mail/set.rs @@ -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::, 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::, 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], diff --git a/tests/src/jmap/mail/sieve_script.rs b/tests/src/jmap/mail/sieve_script.rs index 0f1f53fb..7d73210e 100644 --- a/tests/src/jmap/mail/sieve_script.rs +++ b/tests/src/jmap/mail/sieve_script.rs @@ -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; } diff --git a/tests/src/jmap/mail/submission.rs b/tests/src/jmap/mail/submission.rs index 084e864c..0e41b389 100644 --- a/tests/src/jmap/mail/submission.rs +++ b/tests/src/jmap/mail/submission.rs @@ -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; } diff --git a/tests/src/jmap/mail/thread_get.rs b/tests/src/jmap/mail/thread_get.rs index 99ff6294..1470bc0d 100644 --- a/tests/src/jmap/mail/thread_get.rs +++ b/tests/src/jmap/mail/thread_get.rs @@ -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::, 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; } diff --git a/tests/src/jmap/mail/thread_merge.rs b/tests/src/jmap/mail/thread_merge.rs index 811401d8..0df21590 100644 --- a/tests/src/jmap/mail/thread_merge.rs +++ b/tests/src/jmap/mail/thread_merge.rs @@ -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::, 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::>() .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; } diff --git a/tests/src/jmap/mail/vacation_response.rs b/tests/src/jmap/mail/vacation_response.rs index 92ca81e6..7ab88243 100644 --- a/tests/src/jmap/mail/vacation_response.rs +++ b/tests/src/jmap/mail/vacation_response.rs @@ -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; } diff --git a/tests/src/jmap/mod.rs b/tests/src/jmap/mod.rs index 1fa6f56c..cf16e390 100644 --- a/tests/src/jmap/mod.rs +++ b/tests/src/jmap/mod.rs @@ -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(¶ms).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, shutdown_tx: watch::Sender, } +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, 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, - 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 { @@ -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 { diff --git a/tests/src/jmap/server/enterprise.rs b/tests/src/jmap/server/enterprise.rs index 3d71cef9..7b930125 100644 --- a/tests/src/jmap/server/enterprise.rs +++ b/tests/src/jmap/server/enterprise.rs @@ -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(¶ms.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; diff --git a/tests/src/jmap/server/purge.rs b/tests/src/jmap/server/purge.rs index 4275d785..1f820b4e 100644 --- a/tests/src/jmap/server/purge.rs +++ b/tests/src/jmap/server/purge.rs @@ -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;