Initial commit
This commit is contained in:
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
mod integration;
|
||||
mod seeder;
|
||||
|
||||
use integration::stalwart::shared as shared_stalwart;
|
||||
use seeder::jmap::Jmap;
|
||||
use seeder::seed::SeedStats;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
const CORE: &str = "urn:ietf:params:jmap:core";
|
||||
const MAIL: &str = "urn:ietf:params:jmap:mail";
|
||||
const SIEVE: &str = "urn:ietf:params:jmap:sieve";
|
||||
const CONTACTS: &str = "urn:ietf:params:jmap:contacts";
|
||||
const CALENDARS: &str = "urn:ietf:params:jmap:calendars";
|
||||
const FILENODE: &str = "urn:ietf:params:jmap:filenode";
|
||||
const PRINCIPALS: &str = "urn:ietf:params:jmap:principals";
|
||||
|
||||
const DEFAULT_MAILBOXES: usize = 5;
|
||||
|
||||
fn list_len(v: &Value) -> usize {
|
||||
v.get("list")
|
||||
.and_then(Value::as_array)
|
||||
.map(|a| a.len())
|
||||
.unwrap_or(usize::MAX)
|
||||
}
|
||||
|
||||
fn ids_len(v: &Value) -> usize {
|
||||
v.get("ids")
|
||||
.and_then(Value::as_array)
|
||||
.map(|a| a.len())
|
||||
.unwrap_or(usize::MAX)
|
||||
}
|
||||
|
||||
fn bare() -> SeedStats {
|
||||
SeedStats {
|
||||
mailboxes_created: 0,
|
||||
emails: 0,
|
||||
contacts: 0,
|
||||
events: 0,
|
||||
sieve_active: None,
|
||||
identity: false,
|
||||
file_nodes: 0,
|
||||
address_books: 1,
|
||||
calendars: 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_account(label: &str, user: &Jmap, account_id: &str, s: &SeedStats) {
|
||||
let mailboxes = user
|
||||
.call(
|
||||
&[CORE, MAIL],
|
||||
"Mailbox/get",
|
||||
account_id,
|
||||
json!({ "properties": ["id", "role"] }),
|
||||
)
|
||||
.expect("Mailbox/get");
|
||||
assert_eq!(
|
||||
list_len(&mailboxes),
|
||||
DEFAULT_MAILBOXES + s.mailboxes_created,
|
||||
"{label}: mailbox count"
|
||||
);
|
||||
|
||||
let emails = user
|
||||
.call(
|
||||
&[CORE, MAIL],
|
||||
"Email/query",
|
||||
account_id,
|
||||
json!({ "calculateTotal": true }),
|
||||
)
|
||||
.expect("Email/query");
|
||||
let email_total = emails
|
||||
.get("total")
|
||||
.and_then(Value::as_u64)
|
||||
.map(|n| n as usize)
|
||||
.unwrap_or_else(|| ids_len(&emails));
|
||||
assert_eq!(email_total, s.emails, "{label}: email count");
|
||||
|
||||
let scripts = user
|
||||
.call(
|
||||
&[CORE, SIEVE],
|
||||
"SieveScript/get",
|
||||
account_id,
|
||||
json!({ "properties": ["id", "name", "isActive"] }),
|
||||
)
|
||||
.expect("SieveScript/get");
|
||||
match s.sieve_active {
|
||||
Some(active) => {
|
||||
let list = scripts
|
||||
.get("list")
|
||||
.and_then(Value::as_array)
|
||||
.expect("sieve list");
|
||||
assert_eq!(list.len(), 2, "{label}: sieve script count");
|
||||
let primary = list
|
||||
.iter()
|
||||
.find(|s| s.get("name").and_then(Value::as_str) == Some("vandelay-test-filter"))
|
||||
.expect("primary sieve script present");
|
||||
assert_eq!(
|
||||
primary.get("isActive").and_then(Value::as_bool),
|
||||
Some(active),
|
||||
"{label}: primary sieve isActive"
|
||||
);
|
||||
}
|
||||
None => assert_eq!(list_len(&scripts), 0, "{label}: expected no sieve scripts"),
|
||||
}
|
||||
|
||||
let identities = user
|
||||
.call(
|
||||
&[CORE, MAIL],
|
||||
"Identity/get",
|
||||
account_id,
|
||||
json!({ "properties": ["id", "name"] }),
|
||||
)
|
||||
.expect("Identity/get");
|
||||
let custom = identities
|
||||
.get("list")
|
||||
.and_then(Value::as_array)
|
||||
.map(|a| {
|
||||
a.iter()
|
||||
.filter(|i| {
|
||||
i.get("name").and_then(Value::as_str) == Some(seeder::CUSTOM_IDENTITY_NAME)
|
||||
})
|
||||
.count()
|
||||
})
|
||||
.unwrap_or(0);
|
||||
assert_eq!(
|
||||
custom,
|
||||
usize::from(s.identity),
|
||||
"{label}: custom identity presence"
|
||||
);
|
||||
|
||||
let files = user
|
||||
.call(
|
||||
&[CORE, FILENODE],
|
||||
"FileNode/query",
|
||||
account_id,
|
||||
json!({ "calculateTotal": true }),
|
||||
)
|
||||
.expect("FileNode/query");
|
||||
let file_total = files
|
||||
.get("total")
|
||||
.and_then(Value::as_u64)
|
||||
.map(|n| n as usize)
|
||||
.unwrap_or_else(|| ids_len(&files));
|
||||
assert_eq!(file_total, s.file_nodes, "{label}: file node count");
|
||||
|
||||
let books = user
|
||||
.call(
|
||||
&[CORE, CONTACTS],
|
||||
"AddressBook/get",
|
||||
account_id,
|
||||
json!({ "properties": ["id"] }),
|
||||
)
|
||||
.expect("AddressBook/get");
|
||||
assert_eq!(
|
||||
list_len(&books),
|
||||
s.address_books,
|
||||
"{label}: address book count"
|
||||
);
|
||||
|
||||
let calendars = user
|
||||
.call(
|
||||
&[CORE, CALENDARS],
|
||||
"Calendar/get",
|
||||
account_id,
|
||||
json!({ "properties": ["id"] }),
|
||||
)
|
||||
.expect("Calendar/get");
|
||||
assert_eq!(list_len(&calendars), s.calendars, "{label}: calendar count");
|
||||
|
||||
let cards = user
|
||||
.call(
|
||||
&[CORE, CONTACTS],
|
||||
"ContactCard/get",
|
||||
account_id,
|
||||
json!({ "properties": ["id"] }),
|
||||
)
|
||||
.expect("ContactCard/get");
|
||||
assert_eq!(list_len(&cards), s.contacts, "{label}: contact card count");
|
||||
|
||||
let cal_events = user
|
||||
.call(
|
||||
&[CORE, CALENDARS],
|
||||
"CalendarEvent/query",
|
||||
account_id,
|
||||
json!({ "calculateTotal": true }),
|
||||
)
|
||||
.expect("CalendarEvent/query");
|
||||
let event_total = cal_events
|
||||
.get("total")
|
||||
.and_then(Value::as_u64)
|
||||
.map(|n| n as usize)
|
||||
.unwrap_or_else(|| ids_len(&cal_events));
|
||||
assert_eq!(event_total, s.events, "{label}: calendar event count");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "requires Docker"]
|
||||
fn provision_seed_verify_and_teardown() {
|
||||
let stalwart = shared_stalwart();
|
||||
let fixture = seeder::provision(stalwart.base_url()).expect("provision");
|
||||
|
||||
assert_eq!(fixture.domain, seeder::DOMAIN);
|
||||
assert!(!fixture.domain_id.is_empty());
|
||||
assert_eq!(
|
||||
fixture.accounts.len(),
|
||||
seeder::SYNC_IN.len() + seeder::SYNC_OUT.len() + 1
|
||||
);
|
||||
|
||||
for lp in seeder::SYNC_IN {
|
||||
let a = fixture.account(lp).expect("sync-in account");
|
||||
let stats = a.seeded.as_ref().expect("sync-in seeded stats");
|
||||
assert!(stats.emails > 0, "{lp}: expected seeded emails");
|
||||
let user = Jmap::connect(&fixture.base_url, &a.email, &a.password).expect("login sync-in");
|
||||
assert_eq!(user.account_id, a.account_id);
|
||||
verify_account(lp, &user, &a.account_id, stats);
|
||||
}
|
||||
|
||||
let baseline = bare();
|
||||
for lp in seeder::SYNC_OUT {
|
||||
let a = fixture.account(lp).expect("sync-out account");
|
||||
assert!(a.seeded.is_none(), "{lp}: targets must not be seeded");
|
||||
let user = Jmap::connect(&fixture.base_url, &a.email, &a.password).expect("login sync-out");
|
||||
verify_account(lp, &user, &a.account_id, &baseline);
|
||||
}
|
||||
|
||||
let admin_fx = fixture
|
||||
.account(seeder::ADMIN_LOCALPART)
|
||||
.expect("admin account");
|
||||
assert!(admin_fx.admin_role);
|
||||
let admin = Jmap::connect(&fixture.base_url, &admin_fx.email, &admin_fx.password)
|
||||
.expect("login admin account");
|
||||
let test1 = fixture.account("test1").expect("test1");
|
||||
let resolved = admin
|
||||
.request(
|
||||
&[CORE, PRINCIPALS],
|
||||
json!([
|
||||
["Principal/query", { "filter": { "name": test1.email } }, "q"],
|
||||
["Principal/get", {
|
||||
"#ids": { "resultOf": "q", "name": "Principal/query", "path": "/ids" },
|
||||
"properties": ["id", "name", "accounts"]
|
||||
}, "g"]
|
||||
]),
|
||||
)
|
||||
.expect("Principal discovery");
|
||||
let principals = resolved
|
||||
.get("methodResponses")
|
||||
.and_then(Value::as_array)
|
||||
.and_then(|r| r.get(1))
|
||||
.and_then(|r| r.get(1))
|
||||
.and_then(|g| g.get("list"))
|
||||
.and_then(Value::as_array)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let exact: Vec<&Value> = principals
|
||||
.iter()
|
||||
.filter(|p| p.get("name").and_then(Value::as_str) == Some(test1.email.as_str()))
|
||||
.collect();
|
||||
assert_eq!(exact.len(), 1, "admin Principal exact-name resolution");
|
||||
let owner = exact[0]
|
||||
.get("accounts")
|
||||
.and_then(Value::as_object)
|
||||
.and_then(|m| {
|
||||
m.iter().find_map(|(_, v)| {
|
||||
v.get("urn:ietf:params:jmap:principals:owner")
|
||||
.and_then(|o| o.get("accountIdForPrincipal"))
|
||||
.and_then(Value::as_str)
|
||||
})
|
||||
})
|
||||
.expect("accountIdForPrincipal");
|
||||
assert_eq!(owner, test1.account_id, "resolved account id matches test1");
|
||||
|
||||
seeder::teardown(stalwart.base_url()).expect("teardown");
|
||||
|
||||
let (admin_user, admin_password) = &fixture.admin_login;
|
||||
let gone = seeder::admin::Admin::connect(&fixture.base_url, admin_user, admin_password)
|
||||
.expect("admin reconnect")
|
||||
.domain_id(seeder::DOMAIN)
|
||||
.expect("domain_id query");
|
||||
assert!(gone.is_none(), "domain should be gone after teardown");
|
||||
}
|
||||
Reference in New Issue
Block a user