Files
inbuxa-migrate/tests/seeder/mod.rs
T
2026-05-29 18:02:15 +02:00

266 lines
8.2 KiB
Rust

/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
pub mod admin;
pub mod data;
pub mod error;
pub mod jmap;
pub mod seed;
use admin::Admin;
use error::SeedResult;
use jmap::Jmap;
use seed::{FileSpec, Layout, MailboxSpec};
pub const ADMIN_USER: &str = "admin";
pub const ADMIN_PASSWORD: &str = "admin";
pub const DOMAIN: &str = "vandelay.org";
pub const USER_PASSWORD: &str = "VandelayUser#2026";
pub const ADMIN_ACCOUNT_PASSWORD: &str = "VandelayAdmin#2026";
pub const SYNC_IN: [&str; 3] = ["test1", "test2", "test3"];
pub const SYNC_OUT: [&str; 3] = ["test4", "test5", "test6"];
pub const ADMIN_LOCALPART: &str = "vandeladmin";
pub const CUSTOM_IDENTITY_NAME: &str = "Vandelay Test (Custom)";
macro_rules! mailbox {
($k:expr, $n:expr, $p:expr, $r:expr) => {
MailboxSpec {
key: $k,
name: $n,
parent: $p,
role: $r,
}
};
}
macro_rules! node {
($k:expr, $n:expr, $p:expr, $d:expr) => {
FileSpec {
key: $k,
name: $n,
parent: $p,
directory: $d,
}
};
}
static LAYOUT1_MAILBOXES: &[MailboxSpec] = &[
mailbox!("proj", "Projects", None, None),
mailbox!("alpha", "Alpha", Some("proj"), None),
mailbox!("sub", "Subtasks", Some("alpha"), None),
mailbox!("done", "Done", Some("sub"), None),
mailbox!("beta", "Beta", Some("proj"), None),
mailbox!("backlog", "Backlog", Some("beta"), None),
mailbox!("arch", "Archive", None, Some("archive")),
];
static LAYOUT2_MAILBOXES: &[MailboxSpec] = &[
mailbox!("work", "Work", None, None),
mailbox!("clients", "Clients", Some("work"), None),
mailbox!("acme", "Acme Corp", Some("clients"), None),
mailbox!("inv", "Invoices", Some("acme"), None),
mailbox!("globex", "Globex", Some("clients"), None),
mailbox!("internal", "Internal", Some("work"), None),
mailbox!("pers", "Personal", None, None),
mailbox!("rcpt", "Receipts", Some("pers"), None),
mailbox!("y2025", "2025", Some("rcpt"), None),
];
static LAYOUT3_MAILBOXES: &[MailboxSpec] = &[
mailbox!("lists", "Lists", None, None),
mailbox!("news", "Newsletters", Some("lists"), None),
mailbox!("tech", "Tech", Some("news"), None),
mailbox!("notif", "Notifications", Some("lists"), None),
mailbox!("misc", "Misc", None, None),
];
static LAYOUT1_FILES: &[FileSpec] = &[
node!("d_docs", "Documents", None, true),
node!("d_rep", "Reports", Some("d_docs"), true),
node!("d_24", "2024", Some("d_rep"), true),
node!("f_q4", "q4-results.bin", Some("d_24"), false),
node!("d_25", "2025", Some("d_rep"), true),
node!("f_q1", "q1-results.bin", Some("d_25"), false),
node!("d_spec", "Specs", Some("d_docs"), true),
node!("f_ovr", "overview.bin", Some("d_spec"), false),
node!("f_readme", "README.bin", None, false),
];
static LAYOUT2_FILES: &[FileSpec] = &[
node!("m_media", "Media", None, true),
node!("m_photos", "Photos", Some("m_media"), true),
node!("m_23", "2023", Some("m_photos"), true),
node!("f_trip", "trip.bin", Some("m_23"), false),
node!("m_audio", "Audio", Some("m_media"), true),
node!("f_notes", "notes.bin", Some("m_audio"), false),
node!("m_proj", "Projects", None, true),
node!("m_src", "src", Some("m_proj"), true),
node!("f_main", "main.bin", Some("m_src"), false),
];
static LAYOUT3_FILES: &[FileSpec] = &[
node!("b_bak", "Backup", None, true),
node!("b_db", "db", Some("b_bak"), true),
node!("f_snap", "snapshot.bin", Some("b_db"), false),
node!("f_scratch", "scratch.bin", None, false),
];
fn layout_for(localpart: &str) -> Layout {
match localpart {
"test1" => Layout {
mailboxes: LAYOUT1_MAILBOXES,
file_tree: LAYOUT1_FILES,
email_count: 400,
sieve_active: Some(true),
identity: true,
extra_address_book: true,
extra_calendar: true,
},
"test2" => Layout {
mailboxes: LAYOUT2_MAILBOXES,
file_tree: LAYOUT2_FILES,
email_count: 110,
sieve_active: Some(false),
identity: true,
extra_address_book: false,
extra_calendar: true,
},
_ => Layout {
mailboxes: LAYOUT3_MAILBOXES,
file_tree: LAYOUT3_FILES,
email_count: 70,
sieve_active: None,
identity: false,
extra_address_book: false,
extra_calendar: false,
},
}
}
pub struct AccountFixture {
pub localpart: String,
pub email: String,
pub password: String,
pub admin_role: bool,
pub account_id: String,
pub seeded: Option<seed::SeedStats>,
}
pub struct Fixture {
pub base_url: String,
pub domain: String,
pub domain_id: String,
pub admin_login: (String, String),
pub accounts: Vec<AccountFixture>,
}
impl Fixture {
pub fn account(&self, localpart: &str) -> Option<&AccountFixture> {
self.accounts.iter().find(|a| a.localpart == localpart)
}
}
fn email_of(localpart: &str) -> String {
format!("{localpart}@{DOMAIN}")
}
pub fn teardown(base_url: &str) -> SeedResult<()> {
let admin = Admin::connect(base_url, ADMIN_USER, ADMIN_PASSWORD)?;
admin.teardown_domain(DOMAIN)?;
admin.invalidate_caches()
}
pub fn provision(base_url: &str) -> SeedResult<Fixture> {
let admin = Admin::connect(base_url, ADMIN_USER, ADMIN_PASSWORD)?;
admin.teardown_domain(DOMAIN)?;
admin.invalidate_caches()?;
let domain_id = admin.ensure_domain(DOMAIN)?;
admin.invalidate_caches()?;
let mut specs: Vec<(String, String, bool)> = Vec::new();
for lp in SYNC_IN.iter().chain(SYNC_OUT.iter()) {
specs.push(((*lp).to_owned(), USER_PASSWORD.to_owned(), false));
}
specs.push((
ADMIN_LOCALPART.to_owned(),
ADMIN_ACCOUNT_PASSWORD.to_owned(),
true,
));
let mut accounts = Vec::new();
for (localpart, password, admin_role) in &specs {
admin.create_account(localpart, &domain_id, password, *admin_role)?;
}
admin.invalidate_caches()?;
for (localpart, password, admin_role) in &specs {
let email = email_of(localpart);
let user = Jmap::connect(base_url, &email, password)?;
accounts.push(AccountFixture {
localpart: localpart.clone(),
email,
password: password.clone(),
admin_role: *admin_role,
account_id: user.account_id.clone(),
seeded: None,
});
}
let messages = data::load_mbox(usize::MAX)?;
let contacts = data::load_contacts()?;
let events = data::load_events()?;
let mut offset = 0;
for localpart in SYNC_IN {
let index = accounts
.iter()
.position(|a| a.localpart == localpart)
.ok_or_else(|| error::SeedError::Shape(format!("missing account {localpart}")))?;
let layout = layout_for(localpart);
let (email, password, account_id) = {
let a = &accounts[index];
(a.email.clone(), a.password.clone(), a.account_id.clone())
};
let user = Jmap::connect(base_url, &email, &password)?;
let end = (offset + layout.email_count).min(messages.len());
let slice = &messages[offset..end];
offset = end;
let (c0, c1, e0, e1) = match localpart {
"test1" => (0, contacts.len().min(3), 0, events.len().min(4)),
"test2" => (
contacts.len().min(3),
contacts.len().min(6),
events.len().min(4),
events.len().min(8),
),
_ => (
contacts.len().min(6),
contacts.len(),
events.len().min(8),
events.len(),
),
};
let stats = seed::seed_account(
&user,
&account_id,
&email,
&layout,
slice,
&contacts[c0..c1],
&events[e0..e1],
)?;
accounts[index].seeded = Some(stats);
}
Ok(Fixture {
base_url: base_url.to_owned(),
domain: DOMAIN.to_owned(),
domain_id,
admin_login: (ADMIN_USER.to_owned(), ADMIN_PASSWORD.to_owned()),
accounts,
})
}