mirror of
https://github.com/chatmail/core.git
synced 2026-10-02 11:10:25 +03:00
feat: perform background fetch from all transports
With I/O stopped, `background_fetch()` connected only to the transport of `configured_addr` and we now instead fan out to all transports in a controlled loop. If a first transport finished fetching new messages cancel all other attempts and return. This is meant to address the problem that amzd described where a profile with one functioning and one hanging transport, shows the first notification, then hangs 15 seconds waiting for the hanging transport. meanwhile a second NSE arrives and dies, and the second message is not notified or only generically. Also drop the quota check from this background fetch path: its result is in-memory only, discarded when the iOS notification service exits, and the regular scheduler fetching refreshes it every 60s anyway. Moreover, quota errors/running full is pretty rare since relays generally automatically stay under quota these days. It's another round trip for each transport of each profile and simply not necessary. Also adds previously missing online tests.
This commit is contained in:
@@ -150,9 +150,10 @@ class Account:
|
||||
return transports
|
||||
|
||||
def bring_online(self):
|
||||
"""Start I/O and wait until IMAP becomes IDLE."""
|
||||
"""Start I/O, wait until all transports became IDLE and drop the events seen so far."""
|
||||
self.start_io()
|
||||
self.wait_for_event(EventType.IMAP_INBOX_IDLE)
|
||||
self._rpc.wait_for_all_work_done(self.id)
|
||||
self.clear_all_events()
|
||||
|
||||
def create_contact(self, obj: Union[int, str, Contact, "Account"], name: Optional[str] = None) -> Contact:
|
||||
"""Create a new Contact or return an existing one.
|
||||
|
||||
@@ -22,8 +22,10 @@ ALL = "1:*"
|
||||
class DirectImap:
|
||||
"""Internal Python-level IMAP handling."""
|
||||
|
||||
def __init__(self, account: Account) -> None:
|
||||
def __init__(self, account: Account, addr=None, password=None) -> None:
|
||||
self.account = account
|
||||
self.addr = addr or account.get_config("addr")
|
||||
self.password = password or account.get_config("mail_pw")
|
||||
self.logid = account.get_config("displayname") or id(account)
|
||||
self._idling = False
|
||||
self.connect()
|
||||
@@ -33,9 +35,9 @@ class DirectImap:
|
||||
host = self.account.get_config("configured_mail_server")
|
||||
port = 993
|
||||
|
||||
user = self.account.get_config("addr")
|
||||
user = self.addr
|
||||
host = user.rsplit("@")[-1]
|
||||
pw = self.account.get_config("mail_pw")
|
||||
pw = self.password
|
||||
|
||||
ssl_context = ssl.create_default_context()
|
||||
if host.startswith("_"):
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import time
|
||||
import urllib.parse
|
||||
|
||||
import pytest
|
||||
@@ -7,6 +8,22 @@ from deltachat_rpc_client.const import ChatType, DownloadState
|
||||
from deltachat_rpc_client.rpc import JsonRpcError
|
||||
|
||||
|
||||
def alice_with_two_transports_and_bob(acf):
|
||||
alice, bob = acf.get_online_accounts(2)
|
||||
alice.add_transport_from_qr(acf.get_account_qr())
|
||||
alice.bring_online()
|
||||
return alice, alice.create_chat(bob), bob.create_chat(alice)
|
||||
|
||||
|
||||
def messages_with_text(chat, text):
|
||||
return [msg for msg in chat.get_messages() if msg.get_snapshot().text == text]
|
||||
|
||||
|
||||
def wait_for_imap_message(imap):
|
||||
while not imap.get_all_messages():
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
def test_add_second_address(acf) -> None:
|
||||
account = acf.new_configured_account()
|
||||
assert len(account.list_transports()) == 1
|
||||
@@ -251,11 +268,10 @@ def test_message_info_imap_urls(acf) -> None:
|
||||
alice, bob = acf.get_online_accounts(2)
|
||||
|
||||
qr = acf.get_account_qr()
|
||||
for i in range(3):
|
||||
for _ in range(3):
|
||||
alice.add_transport_from_qr(qr)
|
||||
# Wait for all transports to go IDLE after adding each one.
|
||||
for _ in range(i + 1):
|
||||
alice.bring_online()
|
||||
alice.bring_online()
|
||||
|
||||
# Enable multi-device mode so messages are not deleted immediately.
|
||||
alice.set_config("bcc_self", "1")
|
||||
@@ -287,14 +303,7 @@ def test_message_info_imap_urls(acf) -> None:
|
||||
|
||||
def test_remove_primary_transport(acf, log) -> None:
|
||||
"""Test that after removing the primary relay, Alice can still receive messages."""
|
||||
alice, bob = acf.get_online_accounts(2)
|
||||
qr = acf.get_account_qr()
|
||||
|
||||
alice.add_transport_from_qr(qr)
|
||||
alice.bring_online()
|
||||
|
||||
bob_chat = bob.create_chat(alice)
|
||||
alice.create_chat(bob)
|
||||
alice, alice_chat, bob_chat = alice_with_two_transports_and_bob(acf)
|
||||
|
||||
log.section("Alice sets up second transport")
|
||||
[transport1, transport2] = alice.list_transports()
|
||||
@@ -313,7 +322,7 @@ def test_remove_primary_transport(acf, log) -> None:
|
||||
msg2 = alice.wait_for_incoming_msg().get_snapshot()
|
||||
assert msg2.text == "Hello again!"
|
||||
assert msg2.chat.get_basic_snapshot().chat_type == ChatType.SINGLE
|
||||
assert msg2.chat == alice.create_chat(bob)
|
||||
assert msg2.chat == alice_chat
|
||||
|
||||
|
||||
def test_qr_works_after_removing_primary_transport(acf, log) -> None:
|
||||
@@ -344,3 +353,33 @@ def test_qr_works_after_removing_primary_transport(acf, log) -> None:
|
||||
bob.secure_join(chat_qr)
|
||||
alice.wait_for_securejoin_inviter_success()
|
||||
bob.wait_for_securejoin_joiner_success()
|
||||
|
||||
|
||||
def test_background_fetch_from_second_transport(acf, direct_imap, dc):
|
||||
alice, alice_chat, bob_chat = alice_with_two_transports_and_bob(acf)
|
||||
[transport1, transport2] = alice.list_transports()
|
||||
assert alice.get_config("configured_addr") == transport1["addr"]
|
||||
|
||||
alice.stop_io()
|
||||
bob_chat.send_text("hello")
|
||||
imap1 = direct_imap(alice, transport1["addr"], transport1["password"])
|
||||
wait_for_imap_message(direct_imap(alice, transport2["addr"], transport2["password"]))
|
||||
wait_for_imap_message(imap1)
|
||||
|
||||
# Leave the message on the second transport only.
|
||||
imap1.delete("1:*")
|
||||
|
||||
dc.background_fetch(300)
|
||||
assert len(messages_with_text(alice_chat, "hello")) == 1
|
||||
|
||||
|
||||
def test_background_fetch_no_duplicates(acf, direct_imap, dc):
|
||||
alice, alice_chat, bob_chat = alice_with_two_transports_and_bob(acf)
|
||||
|
||||
alice.stop_io()
|
||||
bob_chat.send_text("hello")
|
||||
for transport in alice.list_transports():
|
||||
wait_for_imap_message(direct_imap(alice, transport["addr"], transport["password"]))
|
||||
|
||||
dc.background_fetch(300)
|
||||
assert len(messages_with_text(alice_chat, "hello")) == 1
|
||||
|
||||
Reference in New Issue
Block a user