deltachat-rpc-client 1.157.3__tar.gz → 1.159.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. {deltachat_rpc_client-1.157.3/src/deltachat_rpc_client.egg-info → deltachat_rpc_client-1.159.0}/PKG-INFO +3 -2
  2. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/pyproject.toml +9 -1
  3. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/src/deltachat_rpc_client/account.py +10 -5
  4. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/src/deltachat_rpc_client/message.py +4 -0
  5. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/src/deltachat_rpc_client/pytestplugin.py +12 -21
  6. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0/src/deltachat_rpc_client.egg-info}/PKG-INFO +3 -2
  7. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/src/deltachat_rpc_client.egg-info/SOURCES.txt +1 -0
  8. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/tests/test_account_events.py +3 -2
  9. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/tests/test_chatlist_events.py +3 -6
  10. deltachat_rpc_client-1.159.0/tests/test_multidevice.py +110 -0
  11. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/tests/test_securejoin.py +17 -24
  12. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/tests/test_something.py +117 -54
  13. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/tests/test_vcard.py +1 -2
  14. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/tests/test_webxdc.py +2 -4
  15. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/LICENSE +0 -0
  16. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/README.md +0 -0
  17. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/setup.cfg +0 -0
  18. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/src/deltachat_rpc_client/__init__.py +0 -0
  19. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/src/deltachat_rpc_client/_utils.py +0 -0
  20. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/src/deltachat_rpc_client/chat.py +0 -0
  21. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/src/deltachat_rpc_client/client.py +0 -0
  22. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/src/deltachat_rpc_client/const.py +0 -0
  23. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/src/deltachat_rpc_client/contact.py +0 -0
  24. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/src/deltachat_rpc_client/deltachat.py +0 -0
  25. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/src/deltachat_rpc_client/events.py +0 -0
  26. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/src/deltachat_rpc_client/py.typed +0 -0
  27. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/src/deltachat_rpc_client/rpc.py +0 -0
  28. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/src/deltachat_rpc_client.egg-info/dependency_links.txt +0 -0
  29. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/src/deltachat_rpc_client.egg-info/entry_points.txt +0 -0
  30. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/src/deltachat_rpc_client.egg-info/top_level.txt +0 -0
  31. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/tests/test_iroh_webxdc.py +0 -0
  32. {deltachat_rpc_client-1.157.3 → deltachat_rpc_client-1.159.0}/tests/test_key_transfer.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: deltachat-rpc-client
3
- Version: 1.157.3
3
+ Version: 1.159.0
4
4
  Summary: Python client for Delta Chat core JSON-RPC interface
5
5
  Classifier: Development Status :: 5 - Production/Stable
6
6
  Classifier: Intended Audience :: Developers
@@ -19,6 +19,7 @@ Classifier: Topic :: Communications :: Email
19
19
  Requires-Python: >=3.8
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
+ Dynamic: license-file
22
23
 
23
24
  # Delta Chat RPC python client
24
25
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "deltachat-rpc-client"
7
- version = "1.157.3"
7
+ version = "1.159.0"
8
8
  description = "Python client for Delta Chat core JSON-RPC interface"
9
9
  classifiers = [
10
10
  "Development Status :: 5 - Production/Stable",
@@ -70,3 +70,11 @@ line-length = 120
70
70
 
71
71
  [tool.isort]
72
72
  profile = "black"
73
+
74
+ [dependency-groups]
75
+ dev = [
76
+ "imap-tools",
77
+ "pytest",
78
+ "pytest-timeout",
79
+ "pytest-xdist",
80
+ ]
@@ -115,7 +115,7 @@ class Account:
115
115
  self.start_io()
116
116
  self.wait_for_event(EventType.IMAP_INBOX_IDLE)
117
117
 
118
- def create_contact(self, obj: Union[int, str, Contact], name: Optional[str] = None) -> Contact:
118
+ def create_contact(self, obj: Union[int, str, Contact, "Account"], name: Optional[str] = None) -> Contact:
119
119
  """Create a new Contact or return an existing one.
120
120
 
121
121
  Calling this method will always result in the same
@@ -123,9 +123,15 @@ class Account:
123
123
  with that e-mail address, it is unblocked and its display
124
124
  name is updated if specified.
125
125
 
126
- :param obj: email-address or contact id.
126
+ :param obj: email-address, contact id or account.
127
127
  :param name: (optional) display name for this contact.
128
128
  """
129
+ if isinstance(obj, Account):
130
+ vcard = obj.self_contact.make_vcard()
131
+ [contact] = self.import_vcard(vcard)
132
+ if name:
133
+ contact.set_name(name)
134
+ return contact
129
135
  if isinstance(obj, int):
130
136
  obj = Contact(self, obj)
131
137
  if isinstance(obj, Contact):
@@ -146,9 +152,8 @@ class Account:
146
152
  return [Contact(self, contact_id) for contact_id in contact_ids]
147
153
 
148
154
  def create_chat(self, account: "Account") -> Chat:
149
- vcard = account.self_contact.make_vcard()
150
- [contact] = self.import_vcard(vcard)
151
- return contact.create_chat()
155
+ """Create a 1:1 chat with another account."""
156
+ return self.create_contact(account).create_chat()
152
157
 
153
158
  def get_device_chat(self) -> Chat:
154
159
  """Return device chat."""
@@ -64,6 +64,10 @@ class Message:
64
64
  def get_webxdc_status_updates(self, last_known_serial: int = 0) -> list:
65
65
  return json.loads(self._rpc.get_webxdc_status_updates(self.account.id, self.id, last_known_serial))
66
66
 
67
+ def get_info(self) -> str:
68
+ """Return message info."""
69
+ return self._rpc.get_message_info(self.account.id, self.id)
70
+
67
71
  def get_webxdc_info(self) -> dict:
68
72
  return self._rpc.get_webxdc_info(self.account.id, self.id)
69
73
 
@@ -12,14 +12,6 @@ from ._utils import futuremethod
12
12
  from .rpc import Rpc
13
13
 
14
14
 
15
- def get_temp_credentials() -> dict:
16
- domain = os.getenv("CHATMAIL_DOMAIN")
17
- username = "ci-" + "".join(random.choice("2345789acdefghjkmnpqrstuvwxyz") for i in range(6))
18
- password = f"{username}${username}"
19
- addr = f"{username}@{domain}"
20
- return {"email": addr, "password": password}
21
-
22
-
23
15
  class ACFactory:
24
16
  def __init__(self, deltachat: DeltaChat) -> None:
25
17
  self.deltachat = deltachat
@@ -32,26 +24,25 @@ class ACFactory:
32
24
  def get_unconfigured_bot(self) -> Bot:
33
25
  return Bot(self.get_unconfigured_account())
34
26
 
35
- def new_preconfigured_account(self) -> Account:
36
- """Make a new account with configuration options set, but configuration not started."""
37
- credentials = get_temp_credentials()
38
- account = self.get_unconfigured_account()
39
- account.set_config("addr", credentials["email"])
40
- account.set_config("mail_pw", credentials["password"])
41
- assert not account.is_configured()
42
- return account
27
+ def get_credentials(self) -> (str, str):
28
+ domain = os.getenv("CHATMAIL_DOMAIN")
29
+ username = "ci-" + "".join(random.choice("2345789acdefghjkmnpqrstuvwxyz") for i in range(6))
30
+ return f"{username}@{domain}", f"{username}${username}"
43
31
 
44
32
  @futuremethod
45
33
  def new_configured_account(self):
46
- account = self.new_preconfigured_account()
47
- yield account.configure.future()
34
+ addr, password = self.get_credentials()
35
+ account = self.get_unconfigured_account()
36
+ params = {"addr": addr, "password": password}
37
+ yield account._rpc.add_transport.future(account.id, params)
38
+
48
39
  assert account.is_configured()
49
40
  return account
50
41
 
51
42
  def new_configured_bot(self) -> Bot:
52
- credentials = get_temp_credentials()
43
+ addr, password = self.get_credentials()
53
44
  bot = self.get_unconfigured_bot()
54
- bot.configure(credentials["email"], credentials["password"])
45
+ bot.configure(addr, password)
55
46
  return bot
56
47
 
57
48
  @futuremethod
@@ -88,7 +79,7 @@ class ACFactory:
88
79
  ) -> Message:
89
80
  if not from_account:
90
81
  from_account = (self.get_online_accounts(1))[0]
91
- to_contact = from_account.create_contact(to_account.get_config("addr"))
82
+ to_contact = from_account.create_contact(to_account)
92
83
  if group:
93
84
  to_chat = from_account.create_group(group)
94
85
  to_chat.add_contact(to_contact)
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: deltachat-rpc-client
3
- Version: 1.157.3
3
+ Version: 1.159.0
4
4
  Summary: Python client for Delta Chat core JSON-RPC interface
5
5
  Classifier: Development Status :: 5 - Production/Stable
6
6
  Classifier: Intended Audience :: Developers
@@ -19,6 +19,7 @@ Classifier: Topic :: Communications :: Email
19
19
  Requires-Python: >=3.8
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
+ Dynamic: license-file
22
23
 
23
24
  # Delta Chat RPC python client
24
25
 
@@ -23,6 +23,7 @@ tests/test_account_events.py
23
23
  tests/test_chatlist_events.py
24
24
  tests/test_iroh_webxdc.py
25
25
  tests/test_key_transfer.py
26
+ tests/test_multidevice.py
26
27
  tests/test_securejoin.py
27
28
  tests/test_something.py
28
29
  tests/test_vcard.py
@@ -13,10 +13,11 @@ def test_event_on_configuration(acfactory: ACFactory) -> None:
13
13
  Test if ACCOUNTS_ITEM_CHANGED event is emitted on configure
14
14
  """
15
15
 
16
- account = acfactory.new_preconfigured_account()
16
+ addr, password = acfactory.get_credentials()
17
+ account = acfactory.get_unconfigured_account()
17
18
  account.clear_all_events()
18
19
  assert not account.is_configured()
19
- future = account.configure.future()
20
+ future = account._rpc.add_transport.future(account.id, {"addr": addr, "password": password})
20
21
  while True:
21
22
  event = account.wait_for_event()
22
23
  if event.kind == EventType.ACCOUNTS_ITEM_CHANGED:
@@ -48,8 +48,7 @@ def test_delivery_status(acfactory: ACFactory) -> None:
48
48
  """
49
49
  alice, bob = acfactory.get_online_accounts(2)
50
50
 
51
- bob_addr = bob.get_config("addr")
52
- alice_contact_bob = alice.create_contact(bob_addr, "Bob")
51
+ alice_contact_bob = alice.create_contact(bob, "Bob")
53
52
  alice_chat_bob = alice_contact_bob.create_chat()
54
53
 
55
54
  alice.clear_all_events()
@@ -119,8 +118,7 @@ def test_download_on_demand(acfactory: ACFactory) -> None:
119
118
  """
120
119
  alice, bob = acfactory.get_online_accounts(2)
121
120
 
122
- bob_addr = bob.get_config("addr")
123
- alice_contact_bob = alice.create_contact(bob_addr, "Bob")
121
+ alice_contact_bob = alice.create_contact(bob, "Bob")
124
122
  alice_chat_bob = alice_contact_bob.create_chat()
125
123
  alice_chat_bob.send_text("hi")
126
124
 
@@ -150,8 +148,7 @@ def test_download_on_demand(acfactory: ACFactory) -> None:
150
148
  def get_multi_account_test_setup(acfactory: ACFactory) -> [Account, Account, Account]:
151
149
  alice, bob = acfactory.get_online_accounts(2)
152
150
 
153
- bob_addr = bob.get_config("addr")
154
- alice_contact_bob = alice.create_contact(bob_addr, "Bob")
151
+ alice_contact_bob = alice.create_contact(bob, "Bob")
155
152
  alice_chat_bob = alice_contact_bob.create_chat()
156
153
  alice_chat_bob.send_text("hi")
157
154
 
@@ -0,0 +1,110 @@
1
+ from imap_tools import AND
2
+
3
+ from deltachat_rpc_client import EventType
4
+ from deltachat_rpc_client.const import MessageState
5
+
6
+
7
+ def test_one_account_send_bcc_setting(acfactory, log, direct_imap):
8
+ ac1, ac2 = acfactory.get_online_accounts(2)
9
+ ac1_clone = ac1.clone()
10
+ ac1_clone.bring_online()
11
+
12
+ log.section("send out message without bcc to ourselves")
13
+ ac1.set_config("bcc_self", "0")
14
+ chat = ac1.create_chat(ac2)
15
+ self_addr = ac1.get_config("addr")
16
+ other_addr = ac2.get_config("addr")
17
+
18
+ msg_out = chat.send_text("message1")
19
+ assert not msg_out.get_snapshot().is_forwarded
20
+
21
+ # wait for send out (no BCC)
22
+ ev = ac1.wait_for_event(EventType.SMTP_MESSAGE_SENT)
23
+ assert ac1.get_config("bcc_self") == "0"
24
+
25
+ assert self_addr not in ev.msg
26
+ assert other_addr in ev.msg
27
+
28
+ log.section("ac1: setting bcc_self=1")
29
+ ac1.set_config("bcc_self", "1")
30
+
31
+ log.section("send out message with bcc to ourselves")
32
+ msg_out = chat.send_text("message2")
33
+
34
+ # wait for send out (BCC)
35
+ ev = ac1.wait_for_event(EventType.SMTP_MESSAGE_SENT)
36
+ assert ac1.get_config("bcc_self") == "1"
37
+
38
+ # Second client receives only second message, but not the first.
39
+ ev_msg = ac1_clone.wait_for_event(EventType.MSGS_CHANGED)
40
+ assert ac1_clone.get_message_by_id(ev_msg.msg_id).get_snapshot().text == msg_out.get_snapshot().text
41
+
42
+ # now make sure we are sending message to ourselves too
43
+ assert self_addr in ev.msg
44
+ assert self_addr in ev.msg
45
+
46
+ # BCC-self messages are marked as seen by the sender device.
47
+ while True:
48
+ event = ac1.wait_for_event()
49
+ if event.kind == EventType.INFO and event.msg.endswith("Marked messages 1 in folder INBOX as seen."):
50
+ break
51
+
52
+ # Check that the message is marked as seen on IMAP.
53
+ ac1_direct_imap = direct_imap(ac1)
54
+ ac1_direct_imap.connect()
55
+ ac1_direct_imap.select_folder("Inbox")
56
+ assert len(list(ac1_direct_imap.conn.fetch(AND(seen=True)))) == 1
57
+
58
+
59
+ def test_multidevice_sync_seen(acfactory, log):
60
+ """Test that message marked as seen on one device is marked as seen on another."""
61
+ ac1, ac2 = acfactory.get_online_accounts(2)
62
+ ac1_clone = ac1.clone()
63
+ ac1_clone.bring_online()
64
+
65
+ ac1.set_config("bcc_self", "1")
66
+ ac1_clone.set_config("bcc_self", "1")
67
+
68
+ ac1_chat = ac1.create_chat(ac2)
69
+ ac1_clone_chat = ac1_clone.create_chat(ac2)
70
+ ac2_chat = ac2.create_chat(ac1)
71
+
72
+ log.section("Send a message from ac2 to ac1 and check that it's 'fresh'")
73
+ ac2_chat.send_text("Hi")
74
+ ac1_message = ac1.wait_for_incoming_msg()
75
+ ac1_clone_message = ac1_clone.wait_for_incoming_msg()
76
+ assert ac1_chat.get_fresh_message_count() == 1
77
+ assert ac1_clone_chat.get_fresh_message_count() == 1
78
+ assert ac1_message.get_snapshot().state == MessageState.IN_FRESH
79
+ assert ac1_clone_message.get_snapshot().state == MessageState.IN_FRESH
80
+
81
+ log.section("ac1 marks message as seen on the first device")
82
+ ac1.mark_seen_messages([ac1_message])
83
+ assert ac1_message.get_snapshot().state == MessageState.IN_SEEN
84
+
85
+ log.section("ac1 clone detects that message is marked as seen")
86
+ ev = ac1_clone.wait_for_event(EventType.MSGS_NOTICED)
87
+ assert ev.chat_id == ac1_clone_chat.id
88
+
89
+ log.section("Send an ephemeral message from ac2 to ac1")
90
+ ac2_chat.set_ephemeral_timer(60)
91
+ ac1.wait_for_event(EventType.CHAT_EPHEMERAL_TIMER_MODIFIED)
92
+ ac1.wait_for_incoming_msg()
93
+ ac1_clone.wait_for_event(EventType.CHAT_EPHEMERAL_TIMER_MODIFIED)
94
+ ac1_clone.wait_for_incoming_msg()
95
+
96
+ ac2_chat.send_text("Foobar")
97
+ ac1_message = ac1.wait_for_incoming_msg()
98
+ ac1_clone_message = ac1_clone.wait_for_incoming_msg()
99
+ assert "Ephemeral timer: 60\n" in ac1_message.get_info()
100
+ assert "Expires: " not in ac1_clone_message.get_info()
101
+ assert "Ephemeral timer: 60\n" in ac1_message.get_info()
102
+ assert "Expires: " not in ac1_clone_message.get_info()
103
+
104
+ ac1_message.mark_seen()
105
+ assert "Expires: " in ac1_message.get_info()
106
+ ev = ac1_clone.wait_for_event(EventType.MSGS_NOTICED)
107
+ assert ev.chat_id == ac1_clone_chat.id
108
+ assert ac1_clone_message.get_snapshot().state == MessageState.IN_SEEN
109
+ # Test that the timer is started on the second device after synchronizing the seen status.
110
+ assert "Expires: " in ac1_clone_message.get_info()
@@ -89,7 +89,7 @@ def test_qr_securejoin(acfactory, protect):
89
89
  assert alice_contact_bob_snapshot.is_verified
90
90
 
91
91
  snapshot = bob.get_message_by_id(bob.wait_for_incoming_msg_event().msg_id).get_snapshot()
92
- assert snapshot.text == "Member Me ({}) added by {}.".format(bob.get_config("addr"), alice.get_config("addr"))
92
+ assert snapshot.text == "Member Me added by {}.".format(alice.get_config("addr"))
93
93
  assert snapshot.chat.get_basic_snapshot().is_protected == protect
94
94
 
95
95
  # Test that Bob verified Alice's profile.
@@ -117,8 +117,7 @@ def test_qr_securejoin_contact_request(acfactory) -> None:
117
117
  """Alice invites Bob to a group when Bob's chat with Alice is in a contact request mode."""
118
118
  alice, bob = acfactory.get_online_accounts(2)
119
119
 
120
- bob_addr = bob.get_config("addr")
121
- alice_contact_bob = alice.create_contact(bob_addr, "Bob")
120
+ alice_contact_bob = alice.create_contact(bob, "Bob")
122
121
  alice_chat_bob = alice_contact_bob.create_chat()
123
122
  alice_chat_bob.send_text("Hello!")
124
123
 
@@ -155,11 +154,8 @@ def test_qr_readreceipt(acfactory) -> None:
155
154
  logging.info("Alice creates a verified group")
156
155
  group = alice.create_group("Group", protect=True)
157
156
 
158
- bob_addr = bob.get_config("addr")
159
- charlie_addr = charlie.get_config("addr")
160
-
161
- alice_contact_bob = alice.create_contact(bob_addr, "Bob")
162
- alice_contact_charlie = alice.create_contact(charlie_addr, "Charlie")
157
+ alice_contact_bob = alice.create_contact(bob, "Bob")
158
+ alice_contact_charlie = alice.create_contact(charlie, "Charlie")
163
159
 
164
160
  group.add_contact(alice_contact_bob)
165
161
  group.add_contact(alice_contact_charlie)
@@ -186,7 +182,7 @@ def test_qr_readreceipt(acfactory) -> None:
186
182
  charlie_snapshot = charlie_message.get_snapshot()
187
183
  assert charlie_snapshot.text == "Hi from Bob!"
188
184
 
189
- bob_contact_charlie = bob.create_contact(charlie_addr, "Charlie")
185
+ bob_contact_charlie = bob.create_contact(charlie, "Charlie")
190
186
  assert not bob.get_chat_by_contact(bob_contact_charlie)
191
187
 
192
188
  logging.info("Charlie reads Bob's message")
@@ -462,8 +458,7 @@ def test_aeap_flow_verified(acfactory):
462
458
  """Test that a new address is added to a contact when it changes its address."""
463
459
  ac1, ac2 = acfactory.get_online_accounts(2)
464
460
 
465
- # ac1new is only used to get a new address.
466
- ac1new = acfactory.new_preconfigured_account()
461
+ addr, password = acfactory.get_credentials()
467
462
 
468
463
  logging.info("ac1: create verified-group QR, ac2 scans and joins")
469
464
  chat = ac1.create_group("hello", protect=True)
@@ -483,8 +478,8 @@ def test_aeap_flow_verified(acfactory):
483
478
  assert msg_in_1.text == msg_out.text
484
479
 
485
480
  logging.info("changing email account")
486
- ac1.set_config("addr", ac1new.get_config("addr"))
487
- ac1.set_config("mail_pw", ac1new.get_config("mail_pw"))
481
+ ac1.set_config("addr", addr)
482
+ ac1.set_config("mail_pw", password)
488
483
  ac1.stop_io()
489
484
  ac1.configure()
490
485
  ac1.start_io()
@@ -497,11 +492,9 @@ def test_aeap_flow_verified(acfactory):
497
492
  msg_in_2_snapshot = msg_in_2.get_snapshot()
498
493
  assert msg_in_2_snapshot.text == msg_out.text
499
494
  assert msg_in_2_snapshot.chat.id == msg_in_1.chat.id
500
- assert msg_in_2.get_sender_contact().get_snapshot().address == ac1new.get_config("addr")
495
+ assert msg_in_2.get_sender_contact().get_snapshot().address == addr
501
496
  assert len(msg_in_2_snapshot.chat.get_contacts()) == 2
502
- assert ac1new.get_config("addr") in [
503
- contact.get_snapshot().address for contact in msg_in_2_snapshot.chat.get_contacts()
504
- ]
497
+ assert addr in [contact.get_snapshot().address for contact in msg_in_2_snapshot.chat.get_contacts()]
505
498
 
506
499
 
507
500
  def test_gossip_verification(acfactory) -> None:
@@ -517,9 +510,9 @@ def test_gossip_verification(acfactory) -> None:
517
510
  bob.secure_join(qr_code)
518
511
  bob.wait_for_securejoin_joiner_success()
519
512
 
520
- bob_contact_alice = bob.create_contact(alice.get_config("addr"), "Alice")
521
- bob_contact_carol = bob.create_contact(carol.get_config("addr"), "Carol")
522
- carol_contact_alice = carol.create_contact(alice.get_config("addr"), "Alice")
513
+ bob_contact_alice = bob.create_contact(alice, "Alice")
514
+ bob_contact_carol = bob.create_contact(carol, "Carol")
515
+ carol_contact_alice = carol.create_contact(alice, "Alice")
523
516
 
524
517
  logging.info("Bob creates an Autocrypt group")
525
518
  bob_group_chat = bob.create_group("Autocrypt Group")
@@ -570,7 +563,7 @@ def test_securejoin_after_contact_resetup(acfactory) -> None:
570
563
 
571
564
  # ac1 waits for member added message and creates a QR code.
572
565
  snapshot = ac1.get_message_by_id(ac1.wait_for_incoming_msg_event().msg_id).get_snapshot()
573
- assert snapshot.text == "Member Me ({}) added by {}.".format(ac1.get_config("addr"), ac3.get_config("addr"))
566
+ assert snapshot.text == "Member Me added by {}.".format(ac3.get_config("addr"))
574
567
  ac1_qr_code = snapshot.chat.get_qr_code()
575
568
 
576
569
  # ac2 verifies ac1
@@ -579,7 +572,7 @@ def test_securejoin_after_contact_resetup(acfactory) -> None:
579
572
  ac2.wait_for_securejoin_joiner_success()
580
573
 
581
574
  # ac1 is verified for ac2.
582
- ac2_contact_ac1 = ac2.create_contact(ac1.get_config("addr"), "")
575
+ ac2_contact_ac1 = ac2.create_contact(ac1, "")
583
576
  assert ac2_contact_ac1.get_snapshot().is_verified
584
577
 
585
578
  # ac1 resetups the account.
@@ -594,7 +587,7 @@ def test_securejoin_after_contact_resetup(acfactory) -> None:
594
587
  # header sent by old ac1.
595
588
  while True:
596
589
  # ac1 sends a message to ac2.
597
- ac1_contact_ac2 = ac1.create_contact(ac2.get_config("addr"), "")
590
+ ac1_contact_ac2 = ac1.create_contact(ac2, "")
598
591
  ac1_chat_ac2 = ac1_contact_ac2.create_chat()
599
592
  ac1_chat_ac2.send_text("Hello!")
600
593
 
@@ -653,7 +646,7 @@ def test_withdraw_securejoin_qr(acfactory):
653
646
  alice.clear_all_events()
654
647
 
655
648
  snapshot = bob.get_message_by_id(bob.wait_for_incoming_msg_event().msg_id).get_snapshot()
656
- assert snapshot.text == "Member Me ({}) added by {}.".format(bob.get_config("addr"), alice.get_config("addr"))
649
+ assert snapshot.text == "Member Me added by {}.".format(alice.get_config("addr"))
657
650
  assert snapshot.chat.get_basic_snapshot().is_protected
658
651
  bob_chat.leave()
659
652
 
@@ -61,52 +61,77 @@ def test_acfactory(acfactory) -> None:
61
61
 
62
62
 
63
63
  def test_configure_starttls(acfactory) -> None:
64
- account = acfactory.new_preconfigured_account()
65
-
66
- # Use STARTTLS
67
- account.set_config("mail_security", "2")
68
- account.set_config("send_security", "2")
69
- account.configure()
64
+ addr, password = acfactory.get_credentials()
65
+ account = acfactory.get_unconfigured_account()
66
+ account._rpc.add_transport(
67
+ account.id,
68
+ {
69
+ "addr": addr,
70
+ "password": password,
71
+ "imapSecurity": "starttls",
72
+ "smtpSecurity": "starttls",
73
+ },
74
+ )
70
75
  assert account.is_configured()
71
76
 
72
77
 
73
78
  def test_configure_ip(acfactory) -> None:
74
- account = acfactory.new_preconfigured_account()
75
-
76
- domain = account.get_config("addr").rsplit("@")[-1]
77
- ip_address = socket.gethostbyname(domain)
79
+ addr, password = acfactory.get_credentials()
80
+ account = acfactory.get_unconfigured_account()
81
+ ip_address = socket.gethostbyname(addr.rsplit("@")[-1])
78
82
 
79
- # This should fail TLS check.
80
- account.set_config("mail_server", ip_address)
81
83
  with pytest.raises(JsonRpcError):
82
- account.configure()
84
+ account._rpc.add_transport(
85
+ account.id,
86
+ {
87
+ "addr": addr,
88
+ "password": password,
89
+ # This should fail TLS check.
90
+ "imapServer": ip_address,
91
+ },
92
+ )
83
93
 
84
94
 
85
95
  def test_configure_alternative_port(acfactory) -> None:
86
96
  """Test that configuration with alternative port 443 works."""
87
- account = acfactory.new_preconfigured_account()
88
-
89
- account.set_config("mail_port", "443")
90
- account.set_config("send_port", "443")
91
-
92
- account.configure()
93
-
94
-
95
- def test_configure_username(acfactory) -> None:
96
- account = acfactory.new_preconfigured_account()
97
+ addr, password = acfactory.get_credentials()
98
+ account = acfactory.get_unconfigured_account()
99
+ account._rpc.add_transport(
100
+ account.id,
101
+ {
102
+ "addr": addr,
103
+ "password": password,
104
+ "imapPort": 443,
105
+ "smtpPort": 443,
106
+ },
107
+ )
108
+ assert account.is_configured()
97
109
 
98
- addr = account.get_config("addr")
99
- account.set_config("mail_user", addr)
100
- account.configure()
101
110
 
102
- assert account.get_config("configured_mail_user") == addr
111
+ def test_list_transports(acfactory) -> None:
112
+ addr, password = acfactory.get_credentials()
113
+ account = acfactory.get_unconfigured_account()
114
+ account._rpc.add_transport(
115
+ account.id,
116
+ {
117
+ "addr": addr,
118
+ "password": password,
119
+ "imapUser": addr,
120
+ },
121
+ )
122
+ transports = account._rpc.list_transports(account.id)
123
+ assert len(transports) == 1
124
+ params = transports[0]
125
+ assert params["addr"] == addr
126
+ assert params["password"] == password
127
+ assert params["imapUser"] == addr
103
128
 
104
129
 
105
130
  def test_account(acfactory) -> None:
106
131
  alice, bob = acfactory.get_online_accounts(2)
107
132
 
108
133
  bob_addr = bob.get_config("addr")
109
- alice_contact_bob = alice.create_contact(bob_addr, "Bob")
134
+ alice_contact_bob = alice.create_contact(bob, "Bob")
110
135
  alice_chat_bob = alice_contact_bob.create_chat()
111
136
  alice_chat_bob.send_text("Hello!")
112
137
 
@@ -171,8 +196,7 @@ def test_account(acfactory) -> None:
171
196
  def test_chat(acfactory) -> None:
172
197
  alice, bob = acfactory.get_online_accounts(2)
173
198
 
174
- bob_addr = bob.get_config("addr")
175
- alice_contact_bob = alice.create_contact(bob_addr, "Bob")
199
+ alice_contact_bob = alice.create_contact(bob, "Bob")
176
200
  alice_chat_bob = alice_contact_bob.create_chat()
177
201
  alice_chat_bob.send_text("Hello!")
178
202
 
@@ -238,7 +262,7 @@ def test_contact(acfactory) -> None:
238
262
  alice, bob = acfactory.get_online_accounts(2)
239
263
 
240
264
  bob_addr = bob.get_config("addr")
241
- alice_contact_bob = alice.create_contact(bob_addr, "Bob")
265
+ alice_contact_bob = alice.create_contact(bob, "Bob")
242
266
 
243
267
  assert alice_contact_bob == alice.get_contact_by_id(alice_contact_bob.id)
244
268
  assert repr(alice_contact_bob)
@@ -255,8 +279,7 @@ def test_contact(acfactory) -> None:
255
279
  def test_message(acfactory) -> None:
256
280
  alice, bob = acfactory.get_online_accounts(2)
257
281
 
258
- bob_addr = bob.get_config("addr")
259
- alice_contact_bob = alice.create_contact(bob_addr, "Bob")
282
+ alice_contact_bob = alice.create_contact(bob, "Bob")
260
283
  alice_chat_bob = alice_contact_bob.create_chat()
261
284
  alice_chat_bob.send_text("Hello!")
262
285
 
@@ -314,8 +337,7 @@ def test_reaction_seen_on_another_dev(acfactory) -> None:
314
337
  alice2 = alice.clone()
315
338
  alice2.start_io()
316
339
 
317
- bob_addr = bob.get_config("addr")
318
- alice_contact_bob = alice.create_contact(bob_addr, "Bob")
340
+ alice_contact_bob = alice.create_contact(bob, "Bob")
319
341
  alice_chat_bob = alice_contact_bob.create_chat()
320
342
  alice_chat_bob.send_text("Hello!")
321
343
 
@@ -332,8 +354,7 @@ def test_reaction_seen_on_another_dev(acfactory) -> None:
332
354
  alice2.clear_all_events()
333
355
  alice_chat_bob.mark_noticed()
334
356
  chat_id = alice2.wait_for_event(EventType.MSGS_NOTICED).chat_id
335
- alice2_contact_bob = alice2.get_contact_by_addr(bob_addr)
336
- alice2_chat_bob = alice2_contact_bob.create_chat()
357
+ alice2_chat_bob = alice2.create_chat(bob)
337
358
  assert chat_id == alice2_chat_bob.id
338
359
 
339
360
 
@@ -341,8 +362,7 @@ def test_is_bot(acfactory) -> None:
341
362
  """Test that we can recognize messages submitted by bots."""
342
363
  alice, bob = acfactory.get_online_accounts(2)
343
364
 
344
- bob_addr = bob.get_config("addr")
345
- alice_contact_bob = alice.create_contact(bob_addr, "Bob")
365
+ alice_contact_bob = alice.create_contact(bob, "Bob")
346
366
  alice_chat_bob = alice_contact_bob.create_chat()
347
367
 
348
368
  # Alice becomes a bot.
@@ -401,9 +421,11 @@ def test_wait_next_messages(acfactory) -> None:
401
421
  alice = acfactory.new_configured_account()
402
422
 
403
423
  # Create a bot account so it does not receive device messages in the beginning.
404
- bot = acfactory.new_preconfigured_account()
424
+ addr, password = acfactory.get_credentials()
425
+ bot = acfactory.get_unconfigured_account()
405
426
  bot.set_config("bot", "1")
406
- bot.configure()
427
+ bot._rpc.add_transport(bot.id, {"addr": addr, "password": password})
428
+ assert bot.is_configured()
407
429
 
408
430
  # There are no old messages and the call returns immediately.
409
431
  assert not bot.wait_next_messages()
@@ -412,8 +434,7 @@ def test_wait_next_messages(acfactory) -> None:
412
434
  # Bot starts waiting for messages.
413
435
  next_messages_task = executor.submit(bot.wait_next_messages)
414
436
 
415
- bot_addr = bot.get_config("addr")
416
- alice_contact_bot = alice.create_contact(bot_addr, "Bot")
437
+ alice_contact_bot = alice.create_contact(bot, "Bot")
417
438
  alice_chat_bot = alice_contact_bot.create_chat()
418
439
  alice_chat_bot.send_text("Hello!")
419
440
 
@@ -437,9 +458,7 @@ def test_import_export_backup(acfactory, tmp_path) -> None:
437
458
  def test_import_export_keys(acfactory, tmp_path) -> None:
438
459
  alice, bob = acfactory.get_online_accounts(2)
439
460
 
440
- bob_addr = bob.get_config("addr")
441
- alice_contact_bob = alice.create_contact(bob_addr, "Bob")
442
- alice_chat_bob = alice_contact_bob.create_chat()
461
+ alice_chat_bob = alice.create_chat(bob)
443
462
  alice_chat_bob.send_text("Hello Bob!")
444
463
 
445
464
  snapshot = bob.get_message_by_id(bob.wait_for_incoming_msg_event().msg_id).get_snapshot()
@@ -489,9 +508,7 @@ def test_provider_info(rpc) -> None:
489
508
  def test_mdn_doesnt_break_autocrypt(acfactory) -> None:
490
509
  alice, bob = acfactory.get_online_accounts(2)
491
510
 
492
- bob_addr = bob.get_config("addr")
493
-
494
- alice_contact_bob = alice.create_contact(bob_addr, "Bob")
511
+ alice_contact_bob = alice.create_contact(bob, "Bob")
495
512
 
496
513
  # Bob creates chat manually so chat with Alice is accepted.
497
514
  alice_chat_bob = alice_contact_bob.create_chat()
@@ -587,9 +604,13 @@ def test_reactions_for_a_reordering_move(acfactory, direct_imap):
587
604
  messages they refer to and thus dropped.
588
605
  """
589
606
  (ac1,) = acfactory.get_online_accounts(1)
590
- ac2 = acfactory.new_preconfigured_account()
591
- ac2.configure()
607
+
608
+ addr, password = acfactory.get_credentials()
609
+ ac2 = acfactory.get_unconfigured_account()
610
+ ac2._rpc.add_transport(ac2.id, {"addr": addr, "password": password})
592
611
  ac2.set_config("mvbox_move", "1")
612
+ assert ac2.is_configured()
613
+
593
614
  ac2.bring_online()
594
615
  chat1 = acfactory.get_accepted_chat(ac1, ac2)
595
616
  ac2.stop_io()
@@ -633,9 +654,7 @@ def test_download_limit_chat_assignment(acfactory, tmp_path, n_accounts):
633
654
  chat.send_text("Hello Alice!")
634
655
  assert alice.get_message_by_id(alice.wait_for_incoming_msg_event().msg_id).get_snapshot().text == "Hello Alice!"
635
656
 
636
- contact_addr = account.get_config("addr")
637
- contact = alice.create_contact(contact_addr, "")
638
-
657
+ contact = alice.create_contact(account)
639
658
  alice_group.add_contact(contact)
640
659
 
641
660
  if n_accounts == 2:
@@ -742,3 +761,47 @@ def test_no_old_msg_is_fresh(acfactory):
742
761
  assert ev.chat_id == first_msg.get_snapshot().chat_id
743
762
  assert ac1.create_chat(ac2).get_fresh_message_count() == 0
744
763
  assert len(list(ac1.get_fresh_messages())) == 0
764
+
765
+
766
+ def test_rename_synchronization(acfactory):
767
+ """Test synchronization of contact renaming."""
768
+ alice, bob = acfactory.get_online_accounts(2)
769
+ alice2 = alice.clone()
770
+ alice2.bring_online()
771
+
772
+ bob.set_config("displayname", "Bob")
773
+ bob.create_chat(alice).send_text("Hello!")
774
+ alice_msg = alice.wait_for_incoming_msg().get_snapshot()
775
+ alice2_msg = alice2.wait_for_incoming_msg().get_snapshot()
776
+
777
+ assert alice2_msg.sender.get_snapshot().display_name == "Bob"
778
+ alice_msg.sender.set_name("Bobby")
779
+ alice2.wait_for_event(EventType.CONTACTS_CHANGED)
780
+ assert alice2_msg.sender.get_snapshot().display_name == "Bobby"
781
+
782
+
783
+ def test_rename_group(acfactory):
784
+ """Test renaming the group."""
785
+ alice, bob = acfactory.get_online_accounts(2)
786
+
787
+ alice_group = alice.create_group("Test group")
788
+ alice_contact_bob = alice.create_contact(bob)
789
+ alice_group.add_contact(alice_contact_bob)
790
+ alice_group.send_text("Hello!")
791
+
792
+ bob_msg = bob.wait_for_incoming_msg()
793
+ bob_chat = bob_msg.get_snapshot().chat
794
+ assert bob_chat.get_basic_snapshot().name == "Test group"
795
+
796
+ for name in ["Baz", "Foo bar", "Xyzzy"]:
797
+ alice_group.set_name(name)
798
+ bob.wait_for_incoming_msg_event()
799
+ assert bob_chat.get_basic_snapshot().name == name
800
+
801
+
802
+ def test_get_all_accounts_deadlock(rpc):
803
+ """Regression test for get_all_accounts deadlock."""
804
+ for _ in range(100):
805
+ all_accounts = rpc.get_all_accounts.future()
806
+ rpc.add_account()
807
+ all_accounts()
@@ -1,8 +1,7 @@
1
1
  def test_vcard(acfactory) -> None:
2
2
  alice, bob = acfactory.get_online_accounts(2)
3
3
 
4
- bob_addr = bob.get_config("addr")
5
- alice_contact_bob = alice.create_contact(bob_addr, "Bob")
4
+ alice_contact_bob = alice.create_contact(bob, "Bob")
6
5
  alice_contact_charlie = alice.create_contact("charlie@example.org", "Charlie")
7
6
 
8
7
  alice_chat_bob = alice_contact_bob.create_chat()
@@ -1,8 +1,7 @@
1
1
  def test_webxdc(acfactory) -> None:
2
2
  alice, bob = acfactory.get_online_accounts(2)
3
3
 
4
- bob_addr = bob.get_config("addr")
5
- alice_contact_bob = alice.create_contact(bob_addr, "Bob")
4
+ alice_contact_bob = alice.create_contact(bob, "Bob")
6
5
  alice_chat_bob = alice_contact_bob.create_chat()
7
6
  alice_chat_bob.send_message(text="Let's play chess!", file="../test-data/webxdc/chess.xdc")
8
7
 
@@ -45,8 +44,7 @@ def test_webxdc(acfactory) -> None:
45
44
  def test_webxdc_insert_lots_of_updates(acfactory) -> None:
46
45
  alice, bob = acfactory.get_online_accounts(2)
47
46
 
48
- bob_addr = bob.get_config("addr")
49
- alice_contact_bob = alice.create_contact(bob_addr, "Bob")
47
+ alice_contact_bob = alice.create_contact(bob, "Bob")
50
48
  alice_chat_bob = alice_contact_bob.create_chat()
51
49
  message = alice_chat_bob.send_message(text="Let's play chess!", file="../test-data/webxdc/chess.xdc")
52
50