mirror of
https://github.com/chatmail/core.git
synced 2026-04-20 06:56:29 +03:00
Add more detail to securejoin tests
This also checks that some of the correct user interactions happen, checking we get a joiner event and the verified chat messages. It also extends the test utils with the ability to distinguish the different context logs by having them named.
This commit is contained in:
@@ -64,7 +64,10 @@ pub struct InnerContext {
|
||||
|
||||
pub(crate) last_full_folder_scan: Mutex<Option<Instant>>,
|
||||
|
||||
/// Id for this context on the current device.
|
||||
/// ID for this `Context` in the current process.
|
||||
///
|
||||
/// This allows for multiple `Context`s open in a single process where each context can
|
||||
/// be identified by this ID.
|
||||
pub(crate) id: u32,
|
||||
|
||||
creation_time: SystemTime,
|
||||
|
||||
@@ -73,9 +73,25 @@ impl async_std::stream::Stream for EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
/// The event emitted by a [`Context`] from an [`EventEmitter`].
|
||||
///
|
||||
/// Events are documented on the C/FFI API in `deltachat.h` as `DC_EVENT_*` contants. The
|
||||
/// context emits them in relation to various operations happening, a lot of these are again
|
||||
/// documented in `deltachat.h`.
|
||||
///
|
||||
/// This struct [`Deref`]s to the [`EventType`].
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Event {
|
||||
/// The ID of the [`Context`] which emitted this event.
|
||||
///
|
||||
/// This allows using multiple [`Context`]s in a single process as they are identified
|
||||
/// by this ID.
|
||||
///
|
||||
/// [`Context`]: crate::context::Context
|
||||
pub id: u32,
|
||||
/// The event payload.
|
||||
///
|
||||
/// These are documented in `deltachat.h` as the `DC_EVENT_*` constants.
|
||||
pub typ: EventType,
|
||||
}
|
||||
|
||||
@@ -88,7 +104,9 @@ impl Deref for Event {
|
||||
}
|
||||
|
||||
impl EventType {
|
||||
/// Returns the corresponding Event id.
|
||||
/// Returns the corresponding Event ID.
|
||||
///
|
||||
/// These are the IDs used in the `DC_EVENT_*` constants in `deltachat.h`.
|
||||
pub fn as_id(&self) -> i32 {
|
||||
self.get_str("id")
|
||||
.expect("missing id")
|
||||
|
||||
@@ -1103,8 +1103,11 @@ fn encrypted_and_signed(
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use async_std::prelude::*;
|
||||
|
||||
use crate::chat;
|
||||
use crate::chat::ProtectionStatus;
|
||||
use crate::events::Event;
|
||||
use crate::peerstate::Peerstate;
|
||||
use crate::test_utils::TestContext;
|
||||
|
||||
@@ -1113,12 +1116,24 @@ mod tests {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
|
||||
// Generate QR-code, ChatId(0) indicates setup-contact
|
||||
// Setup JoinerProgress sinks.
|
||||
let (joiner_progress_tx, joiner_progress_rx) = async_std::sync::channel(100);
|
||||
bob.add_event_sink(move |event: Event| {
|
||||
let joiner_progress_tx = joiner_progress_tx.clone();
|
||||
async move {
|
||||
if let EventType::SecurejoinJoinerProgress { .. } = event.typ {
|
||||
joiner_progress_tx.try_send(event).unwrap();
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
// Step 1: Generate QR-code, ChatId(0) indicates setup-contact
|
||||
let qr = dc_get_securejoin_qr(&alice.ctx, ChatId::new(0))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Bob scans QR-code, sends vc-request
|
||||
// Step 2: Bob scans QR-code, sends vc-request
|
||||
dc_join_securejoin(&bob.ctx, &qr).await.unwrap();
|
||||
|
||||
let sent = bob.pop_sent_msg().await;
|
||||
@@ -1128,7 +1143,7 @@ mod tests {
|
||||
assert_eq!(msg.get(HeaderDef::SecureJoin).unwrap(), "vc-request");
|
||||
assert!(msg.get(HeaderDef::SecureJoinInvitenumber).is_some());
|
||||
|
||||
// Alice receives vc-request, sends vc-auth-required
|
||||
// Step 3: Alice receives vc-request, sends vc-auth-required
|
||||
alice.recv_msg(&sent).await;
|
||||
|
||||
let sent = alice.pop_sent_msg().await;
|
||||
@@ -1136,9 +1151,33 @@ mod tests {
|
||||
assert!(msg.was_encrypted());
|
||||
assert_eq!(msg.get(HeaderDef::SecureJoin).unwrap(), "vc-auth-required");
|
||||
|
||||
// Bob receives vc-auth-required, sends vc-request-with-auth
|
||||
// Step 4: Bob receives vc-auth-required, sends vc-request-with-auth
|
||||
bob.recv_msg(&sent).await;
|
||||
|
||||
// Check Bob emitted the JoinerProgress event.
|
||||
{
|
||||
let evt = joiner_progress_rx
|
||||
.recv()
|
||||
.timeout(Duration::from_secs(10))
|
||||
.await
|
||||
.expect("timeout waiting for JoinerProgress event")
|
||||
.expect("missing JoinerProgress event");
|
||||
match evt.typ {
|
||||
EventType::SecurejoinJoinerProgress {
|
||||
contact_id,
|
||||
progress,
|
||||
} => {
|
||||
let alice_contact_id =
|
||||
Contact::lookup_id_by_addr(&bob.ctx, "alice@example.com", Origin::Unknown)
|
||||
.await;
|
||||
assert_eq!(contact_id, alice_contact_id);
|
||||
assert_eq!(progress, 400);
|
||||
}
|
||||
_ => panic!("Wrong event type"),
|
||||
}
|
||||
}
|
||||
|
||||
// Check Bob sent the right message.
|
||||
let sent = bob.pop_sent_msg().await;
|
||||
let msg = alice.parse_msg(&sent).await;
|
||||
assert!(msg.was_encrypted());
|
||||
@@ -1167,13 +1206,32 @@ mod tests {
|
||||
VerifiedStatus::Unverified
|
||||
);
|
||||
|
||||
// Alice receives vc-request-with-auth, sends vc-contact-confirm
|
||||
// Step 5+6: Alice receives vc-request-with-auth, sends vc-contact-confirm
|
||||
alice.recv_msg(&sent).await;
|
||||
assert_eq!(
|
||||
contact_bob.is_verified(&alice.ctx).await,
|
||||
VerifiedStatus::BidirectVerified
|
||||
);
|
||||
|
||||
// Check Alice got the verified message in her 1:1 chat.
|
||||
{
|
||||
let chat = alice.create_chat(&bob).await;
|
||||
let msg_id = chat::get_chat_msgs(&alice.ctx, chat.get_id(), 0x1, None)
|
||||
.await
|
||||
.into_iter()
|
||||
.filter_map(|item| match item {
|
||||
chat::ChatItem::Message { msg_id } => Some(msg_id),
|
||||
_ => None,
|
||||
})
|
||||
.max()
|
||||
.expect("No messages in Alice's 1:1 chat");
|
||||
let msg = Message::load_from_db(&alice.ctx, msg_id).await.unwrap();
|
||||
assert!(msg.is_info());
|
||||
let text = msg.get_text().unwrap();
|
||||
assert!(text.contains("bob@example.net verified"));
|
||||
}
|
||||
|
||||
// Check Alice sent the right message to Bob.
|
||||
let sent = alice.pop_sent_msg().await;
|
||||
let msg = bob.parse_msg(&sent).await;
|
||||
assert!(msg.was_encrypted());
|
||||
@@ -1193,13 +1251,32 @@ mod tests {
|
||||
VerifiedStatus::Unverified
|
||||
);
|
||||
|
||||
// Bob receives vc-contact-confirm, sends vc-contact-confirm-received
|
||||
// Step 7: Bob receives vc-contact-confirm, sends vc-contact-confirm-received
|
||||
bob.recv_msg(&sent).await;
|
||||
assert_eq!(
|
||||
contact_alice.is_verified(&bob.ctx).await,
|
||||
VerifiedStatus::BidirectVerified
|
||||
);
|
||||
|
||||
// Check Bob got the verified message in his 1:1 chat.
|
||||
{
|
||||
let chat = bob.create_chat(&alice).await;
|
||||
let msg_id = chat::get_chat_msgs(&bob.ctx, chat.get_id(), 0x1, None)
|
||||
.await
|
||||
.into_iter()
|
||||
.filter_map(|item| match item {
|
||||
chat::ChatItem::Message { msg_id } => Some(msg_id),
|
||||
_ => None,
|
||||
})
|
||||
.max()
|
||||
.expect("No messages in Bob's 1:1 chat");
|
||||
let msg = Message::load_from_db(&bob.ctx, msg_id).await.unwrap();
|
||||
assert!(msg.is_info());
|
||||
let text = msg.get_text().unwrap();
|
||||
assert!(text.contains("alice@example.com verified"));
|
||||
}
|
||||
|
||||
// Check Bob sent the final message
|
||||
let sent = bob.pop_sent_msg().await;
|
||||
let msg = alice.parse_msg(&sent).await;
|
||||
assert!(msg.was_encrypted());
|
||||
@@ -1221,6 +1298,18 @@ mod tests {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
|
||||
// Setup JoinerProgress sinks.
|
||||
let (joiner_progress_tx, joiner_progress_rx) = async_std::sync::channel(100);
|
||||
bob.add_event_sink(move |event: Event| {
|
||||
let joiner_progress_tx = joiner_progress_tx.clone();
|
||||
async move {
|
||||
if let EventType::SecurejoinJoinerProgress { .. } = event.typ {
|
||||
joiner_progress_tx.try_send(event).unwrap();
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
// Ensure Bob knows Alice_FP
|
||||
let alice_pubkey = SignedPublicKey::load_self(&alice.ctx).await.unwrap();
|
||||
let peerstate = Peerstate {
|
||||
@@ -1241,14 +1330,38 @@ mod tests {
|
||||
};
|
||||
peerstate.save_to_db(&bob.ctx.sql, true).await.unwrap();
|
||||
|
||||
// Generate QR-code, ChatId(0) indicates setup-contact
|
||||
// Step 1: Generate QR-code, ChatId(0) indicates setup-contact
|
||||
let qr = dc_get_securejoin_qr(&alice.ctx, ChatId::new(0))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Bob scans QR-code, sends vc-request-with-auth, skipping vc-request
|
||||
// Step 2+4: Bob scans QR-code, sends vc-request-with-auth, skipping vc-request
|
||||
dc_join_securejoin(&bob.ctx, &qr).await.unwrap();
|
||||
|
||||
// Check Bob emitted the JoinerProgress event.
|
||||
{
|
||||
let evt = joiner_progress_rx
|
||||
.recv()
|
||||
.timeout(Duration::from_secs(10))
|
||||
.await
|
||||
.expect("timeout waiting for JoinerProgress event")
|
||||
.expect("missing JoinerProgress event");
|
||||
match evt.typ {
|
||||
EventType::SecurejoinJoinerProgress {
|
||||
contact_id,
|
||||
progress,
|
||||
} => {
|
||||
let alice_contact_id =
|
||||
Contact::lookup_id_by_addr(&bob.ctx, "alice@example.com", Origin::Unknown)
|
||||
.await;
|
||||
assert_eq!(contact_id, alice_contact_id);
|
||||
assert_eq!(progress, 400);
|
||||
}
|
||||
_ => panic!("Wrong event type"),
|
||||
}
|
||||
}
|
||||
|
||||
// Check Bob sent the right handshake message.
|
||||
let sent = bob.pop_sent_msg().await;
|
||||
let msg = alice.parse_msg(&sent).await;
|
||||
assert!(msg.was_encrypted());
|
||||
@@ -1283,7 +1396,7 @@ mod tests {
|
||||
VerifiedStatus::Unverified
|
||||
);
|
||||
|
||||
// Alice receives vc-request-with-auth, sends vc-contact-confirm
|
||||
// Step 5+6: Alice receives vc-request-with-auth, sends vc-contact-confirm
|
||||
alice.recv_msg(&sent).await;
|
||||
assert_eq!(
|
||||
contact_bob.is_verified(&alice.ctx).await,
|
||||
@@ -1309,7 +1422,7 @@ mod tests {
|
||||
VerifiedStatus::Unverified
|
||||
);
|
||||
|
||||
// Bob receives vc-contact-confirm, sends vc-contact-confirm-received
|
||||
// Step 7: Bob receives vc-contact-confirm, sends vc-contact-confirm-received
|
||||
bob.recv_msg(&sent).await;
|
||||
assert_eq!(
|
||||
contact_alice.is_verified(&bob.ctx).await,
|
||||
@@ -1330,14 +1443,26 @@ mod tests {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
|
||||
// Setup JoinerProgress sinks.
|
||||
let (joiner_progress_tx, joiner_progress_rx) = async_std::sync::channel(100);
|
||||
bob.add_event_sink(move |event: Event| {
|
||||
let joiner_progress_tx = joiner_progress_tx.clone();
|
||||
async move {
|
||||
if let EventType::SecurejoinJoinerProgress { .. } = event.typ {
|
||||
joiner_progress_tx.try_send(event).unwrap();
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
let chatid = chat::create_group_chat(&alice.ctx, ProtectionStatus::Protected, "the chat")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Generate QR-code, secure-join implied by chatid
|
||||
// Step 1: Generate QR-code, secure-join implied by chatid
|
||||
let qr = dc_get_securejoin_qr(&alice.ctx, chatid).await.unwrap();
|
||||
|
||||
// Bob scans QR-code, sends vg-request; blocks on ongoing process
|
||||
// Step 2: Bob scans QR-code, sends vg-request; blocks on ongoing process
|
||||
let joiner = {
|
||||
let qr = qr.clone();
|
||||
let ctx = bob.ctx.clone();
|
||||
@@ -1351,7 +1476,7 @@ mod tests {
|
||||
assert_eq!(msg.get(HeaderDef::SecureJoin).unwrap(), "vg-request");
|
||||
assert!(msg.get(HeaderDef::SecureJoinInvitenumber).is_some());
|
||||
|
||||
// Alice receives vg-request, sends vg-auth-required
|
||||
// Step 3: Alice receives vg-request, sends vg-auth-required
|
||||
alice.recv_msg(&sent).await;
|
||||
|
||||
let sent = alice.pop_sent_msg().await;
|
||||
@@ -1359,9 +1484,34 @@ mod tests {
|
||||
assert!(msg.was_encrypted());
|
||||
assert_eq!(msg.get(HeaderDef::SecureJoin).unwrap(), "vg-auth-required");
|
||||
|
||||
// Bob receives vg-auth-required, sends vg-request-with-auth
|
||||
// Step 4: Bob receives vg-auth-required, sends vg-request-with-auth
|
||||
bob.recv_msg(&sent).await;
|
||||
let sent = bob.pop_sent_msg().await;
|
||||
|
||||
// Check Bob emitted the JoinerProgress event.
|
||||
{
|
||||
let evt = joiner_progress_rx
|
||||
.recv()
|
||||
.timeout(Duration::from_secs(10))
|
||||
.await
|
||||
.expect("timeout waiting for JoinerProgress event")
|
||||
.expect("missing JoinerProgress event");
|
||||
match evt.typ {
|
||||
EventType::SecurejoinJoinerProgress {
|
||||
contact_id,
|
||||
progress,
|
||||
} => {
|
||||
let alice_contact_id =
|
||||
Contact::lookup_id_by_addr(&bob.ctx, "alice@example.com", Origin::Unknown)
|
||||
.await;
|
||||
assert_eq!(contact_id, alice_contact_id);
|
||||
assert_eq!(progress, 400);
|
||||
}
|
||||
_ => panic!("Wrong event type"),
|
||||
}
|
||||
}
|
||||
|
||||
// Check Bob sent the right handshake message.
|
||||
let msg = alice.parse_msg(&sent).await;
|
||||
assert!(msg.was_encrypted());
|
||||
assert_eq!(
|
||||
@@ -1389,7 +1539,7 @@ mod tests {
|
||||
VerifiedStatus::Unverified
|
||||
);
|
||||
|
||||
// Alice receives vg-request-with-auth, sends vg-member-added
|
||||
// Step 5+6: Alice receives vg-request-with-auth, sends vg-member-added
|
||||
alice.recv_msg(&sent).await;
|
||||
assert_eq!(
|
||||
contact_bob.is_verified(&alice.ctx).await,
|
||||
@@ -1412,7 +1562,7 @@ mod tests {
|
||||
VerifiedStatus::Unverified
|
||||
);
|
||||
|
||||
// Bob receives vg-member-added, sends vg-member-added-received
|
||||
// Step 7: Bob receives vg-member-added, sends vg-member-added-received
|
||||
bob.recv_msg(&sent).await;
|
||||
assert_eq!(
|
||||
contact_alice.is_verified(&bob.ctx).await,
|
||||
|
||||
@@ -2,50 +2,61 @@
|
||||
//!
|
||||
//! This module is only compiled for test runs.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{ops::Deref, str::FromStr};
|
||||
|
||||
use ansi_term::Color;
|
||||
use async_std::future::Future;
|
||||
use async_std::path::PathBuf;
|
||||
use async_std::sync::RwLock;
|
||||
use async_std::pin::Pin;
|
||||
use async_std::sync::{Arc, RwLock};
|
||||
use once_cell::sync::Lazy;
|
||||
use tempfile::{tempdir, TempDir};
|
||||
|
||||
use crate::chat::{self, Chat, ChatId, ChatItem};
|
||||
use crate::chatlist::Chatlist;
|
||||
use crate::config::Config;
|
||||
use crate::constants::DC_CONTACT_ID_SELF;
|
||||
use crate::contact::Contact;
|
||||
use crate::contact::{Contact, Origin};
|
||||
use crate::context::Context;
|
||||
use crate::dc_receive_imf::dc_receive_imf;
|
||||
use crate::dc_tools::EmailAddress;
|
||||
use crate::events::{Event, EventType};
|
||||
use crate::job::Action;
|
||||
use crate::key::{self, DcKey};
|
||||
use crate::message::{update_msg_state, Message, MessageState, MsgId};
|
||||
use crate::mimeparser::MimeMessage;
|
||||
use crate::param::{Param, Params};
|
||||
use crate::{chat, chatlist::Chatlist};
|
||||
use crate::{chat::ChatItem, EventType};
|
||||
use crate::{
|
||||
chat::{Chat, ChatId},
|
||||
contact::Origin,
|
||||
};
|
||||
|
||||
use crate::constants::Viewtype;
|
||||
use crate::constants::DC_MSG_ID_DAYMARKER;
|
||||
use crate::constants::DC_MSG_ID_MARKER1;
|
||||
|
||||
type EventSink =
|
||||
dyn Fn(Event) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> + Send + Sync + 'static;
|
||||
|
||||
/// Map of [`Context::id`] to names for [`TestContext`]s.
|
||||
static CONTEXT_NAMES: Lazy<std::sync::RwLock<BTreeMap<u32, String>>> =
|
||||
Lazy::new(|| std::sync::RwLock::new(BTreeMap::new()));
|
||||
|
||||
/// A Context and temporary directory.
|
||||
///
|
||||
/// The temporary directory can be used to store the SQLite database,
|
||||
/// see e.g. [test_context] which does this.
|
||||
#[derive(Debug)]
|
||||
// #[derive(Debug)]
|
||||
pub(crate) struct TestContext {
|
||||
pub ctx: Context,
|
||||
pub dir: TempDir,
|
||||
/// Counter for fake IMAP UIDs in [recv_msg], for private use in that function only.
|
||||
recv_idx: RwLock<u32>,
|
||||
/// Functions to call for events received.
|
||||
event_sinks: Arc<RwLock<Vec<Box<EventSink>>>>,
|
||||
}
|
||||
|
||||
impl TestContext {
|
||||
/// Create a new [TestContext].
|
||||
/// Creates a new [TestContext].
|
||||
///
|
||||
/// The [Context] will be created and have an SQLite database named "db.sqlite" in the
|
||||
/// [TestContext.dir] directory. This directory is cleaned up when the [TestContext] is
|
||||
@@ -53,25 +64,48 @@ impl TestContext {
|
||||
///
|
||||
/// [Context]: crate::context::Context
|
||||
pub async fn new() -> Self {
|
||||
Self::new_named(None).await
|
||||
}
|
||||
|
||||
/// Creates a new [`TestContext`] with a set name used in event logging.
|
||||
pub async fn with_name(name: impl Into<String>) -> Self {
|
||||
Self::new_named(Some(name.into())).await
|
||||
}
|
||||
|
||||
async fn new_named(name: Option<String>) -> Self {
|
||||
use rand::Rng;
|
||||
|
||||
let dir = tempdir().unwrap();
|
||||
let dbfile = dir.path().join("db.sqlite");
|
||||
let id = rand::thread_rng().gen();
|
||||
if let Some(name) = name {
|
||||
let mut context_names = CONTEXT_NAMES.write().unwrap();
|
||||
context_names.insert(id, name);
|
||||
}
|
||||
let ctx = Context::new("FakeOS".into(), dbfile.into(), id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let events = ctx.get_event_emitter();
|
||||
let event_sinks: Arc<RwLock<Vec<Box<EventSink>>>> = Arc::new(RwLock::new(Vec::new()));
|
||||
let sinks = Arc::clone(&event_sinks);
|
||||
async_std::task::spawn(async move {
|
||||
while let Some(event) = events.recv().await {
|
||||
receive_event(event.typ);
|
||||
{
|
||||
let sinks = sinks.read().await;
|
||||
for sink in sinks.iter() {
|
||||
sink(event.clone()).await;
|
||||
}
|
||||
}
|
||||
receive_event(event);
|
||||
}
|
||||
});
|
||||
|
||||
Self {
|
||||
ctx,
|
||||
dir,
|
||||
recv_idx: RwLock::new(0),
|
||||
event_sinks,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +114,7 @@ impl TestContext {
|
||||
/// This is a shortcut which automatically calls [TestContext::configure_alice] after
|
||||
/// creating the context.
|
||||
pub async fn new_alice() -> Self {
|
||||
let t = Self::new().await;
|
||||
let t = Self::with_name("alice").await;
|
||||
t.configure_alice().await;
|
||||
t
|
||||
}
|
||||
@@ -89,7 +123,7 @@ impl TestContext {
|
||||
///
|
||||
/// This is a shortcut which configures bob@example.net with a fixed key.
|
||||
pub async fn new_bob() -> Self {
|
||||
let t = Self::new().await;
|
||||
let t = Self::with_name("bob").await;
|
||||
let keypair = bob_keypair();
|
||||
t.configure_addr(&keypair.addr.to_string()).await;
|
||||
key::store_self_keypair(&t, &keypair, key::KeyPairUse::Default)
|
||||
@@ -98,6 +132,31 @@ impl TestContext {
|
||||
t
|
||||
}
|
||||
|
||||
/// Sets a name for this [`TestContext`] if one isn't yet set.
|
||||
///
|
||||
/// This will show up in events logged in the test output.
|
||||
pub fn set_name(&self, name: impl Into<String>) {
|
||||
let mut context_names = CONTEXT_NAMES.write().unwrap();
|
||||
context_names
|
||||
.entry(self.ctx.get_id())
|
||||
.or_insert_with(|| name.into());
|
||||
}
|
||||
|
||||
/// Add a new callback which will receive events.
|
||||
///
|
||||
/// The test context runs an async task receiving all events from the [`Context`], which
|
||||
/// are logged to stdout. This allows you to register additional callbacks which will
|
||||
/// receive all events in case your tests need to watch for a specific event.
|
||||
pub async fn add_event_sink<F, R>(&self, sink: F)
|
||||
where
|
||||
// Aka `F: EventSink` but type aliases are not allowed.
|
||||
F: Fn(Event) -> R + Send + Sync + 'static,
|
||||
R: Future<Output = ()> + Send + 'static,
|
||||
{
|
||||
let mut sinks = self.event_sinks.write().await;
|
||||
sinks.push(Box::new(move |evt| Box::pin(sink(evt))));
|
||||
}
|
||||
|
||||
/// Configure with alice@example.com.
|
||||
///
|
||||
/// The context will be fake-configured as the alice user, with a pre-generated secret
|
||||
@@ -125,6 +184,9 @@ impl TestContext {
|
||||
.set_config(Config::Configured, Some("1"))
|
||||
.await
|
||||
.unwrap();
|
||||
if let Some(name) = addr.split('@').next() {
|
||||
self.set_name(name);
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve a sent message from the jobs table.
|
||||
@@ -390,95 +452,105 @@ pub(crate) fn bob_keypair() -> key::KeyPair {
|
||||
}
|
||||
}
|
||||
|
||||
fn receive_event(event: EventType) {
|
||||
/// Pretty-print an event to stdout
|
||||
///
|
||||
/// Done during tests this is captured by `cargo test` and associated with the test itself.
|
||||
fn receive_event(event: Event) {
|
||||
let green = Color::Green.normal();
|
||||
let yellow = Color::Yellow.normal();
|
||||
let red = Color::Red.normal();
|
||||
|
||||
match event {
|
||||
let msg = match event.typ {
|
||||
EventType::Info(msg) => {
|
||||
/* do not show the event as this would fill the screen */
|
||||
println!("{}", msg);
|
||||
format!("INFO: {}", msg)
|
||||
}
|
||||
EventType::SmtpConnected(msg) => {
|
||||
println!("[SMTP_CONNECTED] {}", msg);
|
||||
format!("[SMTP_CONNECTED] {}", msg)
|
||||
}
|
||||
EventType::ImapConnected(msg) => {
|
||||
println!("[IMAP_CONNECTED] {}", msg);
|
||||
format!("[IMAP_CONNECTED] {}", msg)
|
||||
}
|
||||
EventType::SmtpMessageSent(msg) => {
|
||||
println!("[SMTP_MESSAGE_SENT] {}", msg);
|
||||
format!("[SMTP_MESSAGE_SENT] {}", msg)
|
||||
}
|
||||
EventType::Warning(msg) => {
|
||||
println!("{}", yellow.paint(msg));
|
||||
format!("WARN: {}", yellow.paint(msg))
|
||||
}
|
||||
EventType::Error(msg) => {
|
||||
println!("{}", red.paint(msg));
|
||||
format!("ERROR: {}", red.paint(msg))
|
||||
}
|
||||
EventType::ErrorNetwork(msg) => {
|
||||
println!("{}", red.paint(format!("[NETWORK] msg={}", msg)));
|
||||
format!("{}", red.paint(format!("[NETWORK] msg={}", msg)))
|
||||
}
|
||||
EventType::ErrorSelfNotInGroup(msg) => {
|
||||
println!("{}", red.paint(format!("[SELF_NOT_IN_GROUP] {}", msg)));
|
||||
format!("{}", red.paint(format!("[SELF_NOT_IN_GROUP] {}", msg)))
|
||||
}
|
||||
EventType::MsgsChanged { chat_id, msg_id } => {
|
||||
println!(
|
||||
format!(
|
||||
"{}",
|
||||
green.paint(format!(
|
||||
"Received MSGS_CHANGED(chat_id={}, msg_id={})",
|
||||
chat_id, msg_id,
|
||||
))
|
||||
);
|
||||
)
|
||||
}
|
||||
EventType::ContactsChanged(_) => {
|
||||
println!("{}", green.paint("Received CONTACTS_CHANGED()"));
|
||||
format!("{}", green.paint("Received CONTACTS_CHANGED()"))
|
||||
}
|
||||
EventType::LocationChanged(contact) => {
|
||||
println!(
|
||||
format!(
|
||||
"{}",
|
||||
green.paint(format!("Received LOCATION_CHANGED(contact={:?})", contact))
|
||||
);
|
||||
)
|
||||
}
|
||||
EventType::ConfigureProgress { progress, comment } => {
|
||||
if let Some(comment) = comment {
|
||||
println!(
|
||||
format!(
|
||||
"{}",
|
||||
green.paint(format!(
|
||||
"Received CONFIGURE_PROGRESS({} ‰, {})",
|
||||
progress, comment
|
||||
))
|
||||
);
|
||||
)
|
||||
} else {
|
||||
println!(
|
||||
format!(
|
||||
"{}",
|
||||
green.paint(format!("Received CONFIGURE_PROGRESS({} ‰)", progress))
|
||||
);
|
||||
)
|
||||
}
|
||||
}
|
||||
EventType::ImexProgress(progress) => {
|
||||
println!(
|
||||
format!(
|
||||
"{}",
|
||||
green.paint(format!("Received IMEX_PROGRESS({} ‰)", progress))
|
||||
);
|
||||
)
|
||||
}
|
||||
EventType::ImexFileWritten(file) => {
|
||||
println!(
|
||||
format!(
|
||||
"{}",
|
||||
green.paint(format!("Received IMEX_FILE_WRITTEN({})", file.display()))
|
||||
);
|
||||
)
|
||||
}
|
||||
EventType::ChatModified(chat) => {
|
||||
println!(
|
||||
format!(
|
||||
"{}",
|
||||
green.paint(format!("Received CHAT_MODIFIED({})", chat))
|
||||
);
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
println!("Received {:?}", event);
|
||||
format!("Received {:?}", event)
|
||||
}
|
||||
};
|
||||
let context_names = CONTEXT_NAMES.read().unwrap();
|
||||
match context_names.get(&event.id) {
|
||||
Some(ref name) => println!("{} {}", name, msg),
|
||||
None => println!("{} {}", event.id, msg),
|
||||
}
|
||||
}
|
||||
|
||||
/// Logs and individual message to stdout.
|
||||
///
|
||||
/// This includes a bunch of the message meta-data as well.
|
||||
async fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
||||
let contact = Contact::get_by_id(context, msg.get_from_id())
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user