Compare commits

...

7 Commits

6 changed files with 46 additions and 24 deletions

6
Cargo.lock generated
View File

@@ -297,9 +297,9 @@ dependencies = [
[[package]]
name = "async-imap"
version = "0.10.2"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5488cd022c3c7bc41a9b34a540d9ac0d9c5cd42fdb106a67616521b7592d5b4e"
checksum = "932fcbfc3b9e93f73022bf70446cfd9fe540ba62779944d5363797ff7009cb16"
dependencies = [
"async-channel 2.3.1",
"async-compression",
@@ -4174,7 +4174,7 @@ dependencies = [
"aes-gcm",
"aes-kw",
"argon2",
"base64 0.21.7",
"base64 0.22.1",
"bitfield",
"block-padding",
"blowfish",

View File

@@ -41,7 +41,7 @@ ratelimit = { path = "./deltachat-ratelimit" }
anyhow = { workspace = true }
async-broadcast = "0.7.2"
async-channel = { workspace = true }
async-imap = { version = "0.10.2", default-features = false, features = ["runtime-tokio", "compress"] }
async-imap = { version = "0.10.3", default-features = false, features = ["runtime-tokio", "compress"] }
async-native-tls = { version = "0.5", default-features = false, features = ["runtime-tokio"] }
async-smtp = { version = "0.10", default-features = false, features = ["runtime-tokio"] }
async_zip = { version = "0.0.17", default-features = false, features = ["deflate", "tokio-fs"] }

View File

@@ -1,6 +1,8 @@
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import TYPE_CHECKING, Optional, Union
from warnings import warn
@@ -38,6 +40,16 @@ class Account:
"""Remove the account."""
self._rpc.remove_account(self.id)
def clone(self) -> "Account":
"""Clone given account."""
with TemporaryDirectory() as tmp_dir:
tmp_path = Path(tmp_dir)
self.export_backup(tmp_path)
files = list(tmp_path.glob("*.tar"))
new_account = self.manager.add_account()
new_account.import_backup(files[0])
return new_account
def start_io(self) -> None:
"""Start the account I/O."""
self._rpc.start_io(self.id)
@@ -83,6 +95,10 @@ class Account:
return self.get_config("selfavatar")
def check_qr(self, qr):
"""Parse QR code contents.
This function takes the raw text scanned
and checks what can be done with it."""
return self._rpc.check_qr(self.id, qr)
def set_config_from_qr(self, qr: str):
@@ -125,6 +141,9 @@ class Account:
return self._rpc.make_vcard(self.id, contact_ids)
def import_vcard(self, vcard: str) -> list[Contact]:
"""Import vCard.
Return created or modified contacts in the order they appear in vCard."""
contact_ids = self._rpc.import_vcard_contents(self.id, vcard)
return [Contact(self, contact_id) for contact_id in contact_ids]
@@ -133,6 +152,10 @@ class Account:
[contact] = self.import_vcard(vcard)
return contact.create_chat()
def get_device_chat(self) -> Chat:
"""Return device chat."""
return self.device_contact.create_chat()
def get_contact_by_id(self, contact_id: int) -> Contact:
"""Return Contact instance for the given contact ID."""
return Contact(self, contact_id)
@@ -193,6 +216,11 @@ class Account:
"""This account's identity as a Contact."""
return Contact(self, SpecialContactId.SELF)
@property
def device_contact(self) -> Chat:
"""This account's device contact."""
return Contact(self, SpecialContactId.DEVICE)
def get_chatlist(
self,
query: Optional[str] = None,
@@ -318,6 +346,13 @@ class Account:
if event.kind == EventType.MSGS_CHANGED:
return event
def wait_for_msgs_noticed_event(self):
"""Wait for messages noticed event and return it."""
while True:
event = self.wait_for_event()
if event.kind == EventType.MSGS_NOTICED:
return event
def wait_for_incoming_msg(self):
"""Wait for incoming message and return it.

View File

@@ -157,11 +157,7 @@ def get_multi_account_test_setup(acfactory: ACFactory) -> [Account, Account, Acc
bob.wait_for_incoming_msg_event()
alice_second_device: Account = acfactory.get_unconfigured_account()
alice._rpc.provide_backup.future(alice.id)
backup_code = alice._rpc.get_backup_qr(alice.id)
alice_second_device._rpc.get_backup(alice_second_device.id, backup_code)
alice_second_device = alice.clone()
alice_second_device.start_io()
alice.clear_all_events()
alice_second_device.clear_all_events()

View File

@@ -60,15 +60,12 @@ def test_qr_setup_contact_svg(acfactory) -> None:
@pytest.mark.parametrize("protect", [True, False])
def test_qr_securejoin(acfactory, protect, tmp_path):
def test_qr_securejoin(acfactory, protect):
alice, bob, fiona = acfactory.get_online_accounts(3)
# Setup second device for Alice
# to test observing securejoin protocol.
alice.export_backup(tmp_path)
files = list(tmp_path.glob("*.tar"))
alice2 = acfactory.get_unconfigured_account()
alice2.import_backup(files[0])
alice2 = alice.clone()
logging.info("Alice creates a group")
alice_chat = alice.create_group("Group", protect=protect)

View File

@@ -287,12 +287,9 @@ def test_message(acfactory) -> None:
assert reactions == snapshot.reactions
def test_reaction_seen_on_another_dev(acfactory, tmp_path) -> None:
def test_reaction_seen_on_another_dev(acfactory) -> None:
alice, bob = acfactory.get_online_accounts(2)
alice.export_backup(tmp_path)
files = list(tmp_path.glob("*.tar"))
alice2 = acfactory.get_unconfigured_account()
alice2.import_backup(files[0])
alice2 = alice.clone()
alice2.start_io()
bob_addr = bob.get_config("addr")
@@ -661,7 +658,7 @@ def test_download_limit_chat_assignment(acfactory, tmp_path, n_accounts):
assert snapshot.chat == bob_chat_alice
def test_markseen_contact_request(acfactory, tmp_path):
def test_markseen_contact_request(acfactory):
"""
Test that seen status is synchronized for contact request messages
even though read receipt is not sent.
@@ -669,10 +666,7 @@ def test_markseen_contact_request(acfactory, tmp_path):
alice, bob = acfactory.get_online_accounts(2)
# Bob sets up a second device.
bob.export_backup(tmp_path)
files = list(tmp_path.glob("*.tar"))
bob2 = acfactory.get_unconfigured_account()
bob2.import_backup(files[0])
bob2 = bob.clone()
bob2.start_io()
alice_chat_bob = alice.create_chat(bob)