deltachat-rpc-client 2.27.0__tar.gz → 2.29.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 (36) hide show
  1. {deltachat_rpc_client-2.27.0/src/deltachat_rpc_client.egg-info → deltachat_rpc_client-2.29.0}/PKG-INFO +3 -5
  2. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/pyproject.toml +4 -6
  3. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/account.py +4 -0
  4. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/message.py +4 -0
  5. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/pytestplugin.py +8 -2
  6. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/rpc.py +23 -48
  7. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0/src/deltachat_rpc_client.egg-info}/PKG-INFO +3 -5
  8. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client.egg-info/SOURCES.txt +2 -0
  9. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_folders.py +1 -1
  10. deltachat_rpc_client-2.29.0/tests/test_multitransport.py +158 -0
  11. deltachat_rpc_client-2.29.0/tests/test_rpc_virtual.py +20 -0
  12. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_securejoin.py +8 -8
  13. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_something.py +27 -17
  14. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/LICENSE +0 -0
  15. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/README.md +0 -0
  16. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/setup.cfg +0 -0
  17. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/__init__.py +0 -0
  18. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/_utils.py +0 -0
  19. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/chat.py +0 -0
  20. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/client.py +0 -0
  21. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/const.py +0 -0
  22. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/contact.py +0 -0
  23. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/deltachat.py +0 -0
  24. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/events.py +0 -0
  25. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client/py.typed +0 -0
  26. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client.egg-info/dependency_links.txt +0 -0
  27. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client.egg-info/entry_points.txt +0 -0
  28. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/src/deltachat_rpc_client.egg-info/top_level.txt +0 -0
  29. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_account_events.py +0 -0
  30. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_calls.py +0 -0
  31. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_chatlist_events.py +0 -0
  32. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_iroh_webxdc.py +0 -0
  33. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_key_transfer.py +0 -0
  34. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_multidevice.py +0 -0
  35. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_vcard.py +0 -0
  36. {deltachat_rpc_client-2.27.0 → deltachat_rpc_client-2.29.0}/tests/test_webxdc.py +0 -0
@@ -1,15 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deltachat-rpc-client
3
- Version: 2.27.0
3
+ Version: 2.29.0
4
4
  Summary: Python client for Delta Chat core JSON-RPC interface
5
+ License-Expression: MPL-2.0
5
6
  Classifier: Development Status :: 5 - Production/Stable
6
7
  Classifier: Intended Audience :: Developers
7
- Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
8
8
  Classifier: Operating System :: POSIX :: Linux
9
9
  Classifier: Operating System :: MacOS :: MacOS X
10
10
  Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.8
12
- Classifier: Programming Language :: Python :: 3.9
13
11
  Classifier: Programming Language :: Python :: 3.10
14
12
  Classifier: Programming Language :: Python :: 3.11
15
13
  Classifier: Programming Language :: Python :: 3.12
@@ -17,7 +15,7 @@ Classifier: Programming Language :: Python :: 3.13
17
15
  Classifier: Programming Language :: Python :: 3.14
18
16
  Classifier: Topic :: Communications :: Chat
19
17
  Classifier: Topic :: Communications :: Email
20
- Requires-Python: >=3.8
18
+ Requires-Python: >=3.10
21
19
  Description-Content-Type: text/markdown
22
20
  License-File: LICENSE
23
21
  Dynamic: license-file
@@ -1,20 +1,18 @@
1
1
  [build-system]
2
- requires = ["setuptools>=45"]
2
+ requires = ["setuptools>=77"]
3
3
  build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "deltachat-rpc-client"
7
- version = "2.27.0"
7
+ version = "2.29.0"
8
+ license = "MPL-2.0"
8
9
  description = "Python client for Delta Chat core JSON-RPC interface"
9
10
  classifiers = [
10
11
  "Development Status :: 5 - Production/Stable",
11
12
  "Intended Audience :: Developers",
12
- "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
13
13
  "Operating System :: POSIX :: Linux",
14
14
  "Operating System :: MacOS :: MacOS X",
15
15
  "Programming Language :: Python :: 3",
16
- "Programming Language :: Python :: 3.8",
17
- "Programming Language :: Python :: 3.9",
18
16
  "Programming Language :: Python :: 3.10",
19
17
  "Programming Language :: Python :: 3.11",
20
18
  "Programming Language :: Python :: 3.12",
@@ -24,7 +22,7 @@ classifiers = [
24
22
  "Topic :: Communications :: Email"
25
23
  ]
26
24
  readme = "README.md"
27
- requires-python = ">=3.8"
25
+ requires-python = ">=3.10"
28
26
 
29
27
  [tool.setuptools.package-data]
30
28
  deltachat_rpc_client = [
@@ -130,6 +130,10 @@ class Account:
130
130
  """Add a new transport using a QR code."""
131
131
  yield self._rpc.add_transport_from_qr.future(self.id, qr)
132
132
 
133
+ def delete_transport(self, addr: str):
134
+ """Delete a transport."""
135
+ self._rpc.delete_transport(self.id, addr)
136
+
133
137
  @futuremethod
134
138
  def list_transports(self):
135
139
  """Return the list of all email accounts that are used as a transport in the current profile."""
@@ -60,6 +60,10 @@ class Message:
60
60
  """Mark the message as seen."""
61
61
  self._rpc.markseen_msgs(self.account.id, [self.id])
62
62
 
63
+ def exists(self) -> bool:
64
+ """Return True if the message exists."""
65
+ return bool(self._rpc.get_existing_msg_ids(self.account.id, [self.id]))
66
+
63
67
  def continue_autocrypt_key_transfer(self, setup_code: str) -> None:
64
68
  """Continue the Autocrypt Setup Message key transfer.
65
69
 
@@ -40,12 +40,17 @@ class ACFactory:
40
40
  username = "ci-" + "".join(random.choice("2345789acdefghjkmnpqrstuvwxyz") for i in range(6))
41
41
  return f"{username}@{domain}", f"{username}${username}"
42
42
 
43
+ def get_account_qr(self):
44
+ """Return "dcaccount:" QR code for testing chatmail relay."""
45
+ domain = os.getenv("CHATMAIL_DOMAIN")
46
+ return f"dcaccount:{domain}"
47
+
43
48
  @futuremethod
44
49
  def new_configured_account(self):
45
50
  """Create a new configured account."""
46
51
  account = self.get_unconfigured_account()
47
- domain = os.getenv("CHATMAIL_DOMAIN")
48
- yield account.add_transport_from_qr.future(f"dcaccount:{domain}")
52
+ qr = self.get_account_qr()
53
+ yield account.add_transport_from_qr.future(qr)
49
54
 
50
55
  assert account.is_configured()
51
56
  return account
@@ -77,6 +82,7 @@ class ACFactory:
77
82
  ac_clone = self.get_unconfigured_account()
78
83
  for transport in transports:
79
84
  ac_clone.add_or_update_transport(transport)
85
+ ac_clone.bring_online()
80
86
  return ac_clone
81
87
 
82
88
  def get_accepted_chat(self, ac1: Account, ac2: Account) -> Chat:
@@ -9,7 +9,7 @@ import os
9
9
  import subprocess
10
10
  import sys
11
11
  from queue import Empty, Queue
12
- from threading import Event, Thread
12
+ from threading import Thread
13
13
  from typing import Any, Iterator, Optional
14
14
 
15
15
 
@@ -17,25 +17,6 @@ class JsonRpcError(Exception):
17
17
  """JSON-RPC error."""
18
18
 
19
19
 
20
- class RpcFuture:
21
- """RPC future waiting for RPC call result."""
22
-
23
- def __init__(self, rpc: "Rpc", request_id: int, event: Event):
24
- self.rpc = rpc
25
- self.request_id = request_id
26
- self.event = event
27
-
28
- def __call__(self):
29
- """Wait for the future to return the result."""
30
- self.event.wait()
31
- response = self.rpc.request_results.pop(self.request_id)
32
- if "error" in response:
33
- raise JsonRpcError(response["error"])
34
- if "result" in response:
35
- return response["result"]
36
- return None
37
-
38
-
39
20
  class RpcMethod:
40
21
  """RPC method."""
41
22
 
@@ -57,17 +38,23 @@ class RpcMethod:
57
38
  "params": args,
58
39
  "id": request_id,
59
40
  }
60
- event = Event()
61
- self.rpc.request_events[request_id] = event
41
+ self.rpc.request_results[request_id] = queue = Queue()
62
42
  self.rpc.request_queue.put(request)
63
43
 
64
- return RpcFuture(self.rpc, request_id, event)
44
+ def rpc_future():
45
+ """Wait for the request to receive a result."""
46
+ response = queue.get()
47
+ if "error" in response:
48
+ raise JsonRpcError(response["error"])
49
+ return response.get("result", None)
50
+
51
+ return rpc_future
65
52
 
66
53
 
67
54
  class Rpc:
68
55
  """RPC client."""
69
56
 
70
- def __init__(self, accounts_dir: Optional[str] = None, **kwargs):
57
+ def __init__(self, accounts_dir: Optional[str] = None, rpc_server_path="deltachat-rpc-server", **kwargs):
71
58
  """Initialize RPC client.
72
59
 
73
60
  The given arguments will be passed to subprocess.Popen().
@@ -79,13 +66,12 @@ class Rpc:
79
66
  }
80
67
 
81
68
  self._kwargs = kwargs
69
+ self.rpc_server_path = rpc_server_path
82
70
  self.process: subprocess.Popen
83
71
  self.id_iterator: Iterator[int]
84
72
  self.event_queues: dict[int, Queue]
85
- # Map from request ID to `threading.Event`.
86
- self.request_events: dict[int, Event]
87
- # Map from request ID to the result.
88
- self.request_results: dict[int, Any]
73
+ # Map from request ID to a Queue which provides a single result
74
+ self.request_results: dict[int, Queue]
89
75
  self.request_queue: Queue[Any]
90
76
  self.closing: bool
91
77
  self.reader_thread: Thread
@@ -94,27 +80,18 @@ class Rpc:
94
80
 
95
81
  def start(self) -> None:
96
82
  """Start RPC server subprocess."""
83
+ popen_kwargs = {"stdin": subprocess.PIPE, "stdout": subprocess.PIPE}
97
84
  if sys.version_info >= (3, 11):
98
- self.process = subprocess.Popen(
99
- "deltachat-rpc-server",
100
- stdin=subprocess.PIPE,
101
- stdout=subprocess.PIPE,
102
- # Prevent subprocess from capturing SIGINT.
103
- process_group=0,
104
- **self._kwargs,
105
- )
85
+ # Prevent subprocess from capturing SIGINT.
86
+ popen_kwargs["process_group"] = 0
106
87
  else:
107
- self.process = subprocess.Popen(
108
- "deltachat-rpc-server",
109
- stdin=subprocess.PIPE,
110
- stdout=subprocess.PIPE,
111
- # `process_group` is not supported before Python 3.11.
112
- preexec_fn=os.setpgrp, # noqa: PLW1509
113
- **self._kwargs,
114
- )
88
+ # `process_group` is not supported before Python 3.11.
89
+ popen_kwargs["preexec_fn"] = os.setpgrp # noqa: PLW1509
90
+
91
+ popen_kwargs.update(self._kwargs)
92
+ self.process = subprocess.Popen(self.rpc_server_path, **popen_kwargs)
115
93
  self.id_iterator = itertools.count(start=1)
116
94
  self.event_queues = {}
117
- self.request_events = {}
118
95
  self.request_results = {}
119
96
  self.request_queue = Queue()
120
97
  self.closing = False
@@ -149,9 +126,7 @@ class Rpc:
149
126
  response = json.loads(line)
150
127
  if "id" in response:
151
128
  response_id = response["id"]
152
- event = self.request_events.pop(response_id)
153
- self.request_results[response_id] = response
154
- event.set()
129
+ self.request_results.pop(response_id).put(response)
155
130
  else:
156
131
  logging.warning("Got a response without ID: %s", response)
157
132
  except Exception:
@@ -1,15 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deltachat-rpc-client
3
- Version: 2.27.0
3
+ Version: 2.29.0
4
4
  Summary: Python client for Delta Chat core JSON-RPC interface
5
+ License-Expression: MPL-2.0
5
6
  Classifier: Development Status :: 5 - Production/Stable
6
7
  Classifier: Intended Audience :: Developers
7
- Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
8
8
  Classifier: Operating System :: POSIX :: Linux
9
9
  Classifier: Operating System :: MacOS :: MacOS X
10
10
  Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.8
12
- Classifier: Programming Language :: Python :: 3.9
13
11
  Classifier: Programming Language :: Python :: 3.10
14
12
  Classifier: Programming Language :: Python :: 3.11
15
13
  Classifier: Programming Language :: Python :: 3.12
@@ -17,7 +15,7 @@ Classifier: Programming Language :: Python :: 3.13
17
15
  Classifier: Programming Language :: Python :: 3.14
18
16
  Classifier: Topic :: Communications :: Chat
19
17
  Classifier: Topic :: Communications :: Email
20
- Requires-Python: >=3.8
18
+ Requires-Python: >=3.10
21
19
  Description-Content-Type: text/markdown
22
20
  License-File: LICENSE
23
21
  Dynamic: license-file
@@ -26,6 +26,8 @@ tests/test_folders.py
26
26
  tests/test_iroh_webxdc.py
27
27
  tests/test_key_transfer.py
28
28
  tests/test_multidevice.py
29
+ tests/test_multitransport.py
30
+ tests/test_rpc_virtual.py
29
31
  tests/test_securejoin.py
30
32
  tests/test_something.py
31
33
  tests/test_vcard.py
@@ -143,7 +143,7 @@ def test_delete_deltachat_folder(acfactory, direct_imap):
143
143
  # Wait until new folder is created and UIDVALIDITY is updated.
144
144
  while True:
145
145
  event = ac1.wait_for_event()
146
- if event.kind == EventType.INFO and "uid/validity change folder DeltaChat" in event.msg:
146
+ if event.kind == EventType.INFO and "transport 1: UID validity for folder DeltaChat changed from " in event.msg:
147
147
  break
148
148
 
149
149
  ac2 = acfactory.get_online_account()
@@ -0,0 +1,158 @@
1
+ import pytest
2
+
3
+ from deltachat_rpc_client.rpc import JsonRpcError
4
+
5
+
6
+ def test_add_second_address(acfactory) -> None:
7
+ account = acfactory.new_configured_account()
8
+ assert len(account.list_transports()) == 1
9
+
10
+ # When the first transport is created,
11
+ # mvbox_move and only_fetch_mvbox should be disabled.
12
+ assert account.get_config("mvbox_move") == "0"
13
+ assert account.get_config("only_fetch_mvbox") == "0"
14
+ assert account.get_config("show_emails") == "2"
15
+
16
+ qr = acfactory.get_account_qr()
17
+ account.add_transport_from_qr(qr)
18
+ assert len(account.list_transports()) == 2
19
+
20
+ account.add_transport_from_qr(qr)
21
+ assert len(account.list_transports()) == 3
22
+
23
+ first_addr = account.list_transports()[0]["addr"]
24
+ second_addr = account.list_transports()[1]["addr"]
25
+
26
+ # Cannot delete the first address.
27
+ with pytest.raises(JsonRpcError):
28
+ account.delete_transport(first_addr)
29
+
30
+ account.delete_transport(second_addr)
31
+ assert len(account.list_transports()) == 2
32
+
33
+ # Enabling mvbox_move or only_fetch_mvbox
34
+ # is not allowed when multi-transport is enabled.
35
+ for option in ["mvbox_move", "only_fetch_mvbox"]:
36
+ with pytest.raises(JsonRpcError):
37
+ account.set_config(option, "1")
38
+
39
+ with pytest.raises(JsonRpcError):
40
+ account.set_config("show_emails", "0")
41
+
42
+
43
+ @pytest.mark.parametrize("key", ["mvbox_move", "only_fetch_mvbox"])
44
+ def test_no_second_transport_with_mvbox(acfactory, key) -> None:
45
+ """Test that second transport cannot be configured if mvbox is used."""
46
+ account = acfactory.new_configured_account()
47
+ assert len(account.list_transports()) == 1
48
+
49
+ assert account.get_config("mvbox_move") == "0"
50
+ assert account.get_config("only_fetch_mvbox") == "0"
51
+
52
+ qr = acfactory.get_account_qr()
53
+ account.set_config(key, "1")
54
+
55
+ with pytest.raises(JsonRpcError):
56
+ account.add_transport_from_qr(qr)
57
+
58
+
59
+ def test_no_second_transport_without_classic_emails(acfactory) -> None:
60
+ """Test that second transport cannot be configured if classic emails are not fetched."""
61
+ account = acfactory.new_configured_account()
62
+ assert len(account.list_transports()) == 1
63
+
64
+ assert account.get_config("show_emails") == "2"
65
+
66
+ qr = acfactory.get_account_qr()
67
+ account.set_config("show_emails", "0")
68
+
69
+ with pytest.raises(JsonRpcError):
70
+ account.add_transport_from_qr(qr)
71
+
72
+
73
+ def test_change_address(acfactory) -> None:
74
+ """Test Alice configuring a second transport and setting it as a primary one."""
75
+ alice, bob = acfactory.get_online_accounts(2)
76
+
77
+ bob_addr = bob.get_config("configured_addr")
78
+ bob.create_chat(alice)
79
+
80
+ alice_chat_bob = alice.create_chat(bob)
81
+ alice_chat_bob.send_text("Hello!")
82
+
83
+ msg1 = bob.wait_for_incoming_msg().get_snapshot()
84
+ sender_addr1 = msg1.sender.get_snapshot().address
85
+
86
+ alice.stop_io()
87
+ old_alice_addr = alice.get_config("configured_addr")
88
+ alice_vcard = alice.self_contact.make_vcard()
89
+ assert old_alice_addr in alice_vcard
90
+ qr = acfactory.get_account_qr()
91
+ alice.add_transport_from_qr(qr)
92
+ new_alice_addr = alice.list_transports()[1]["addr"]
93
+ with pytest.raises(JsonRpcError):
94
+ # Cannot use the address that is not
95
+ # configured for any transport.
96
+ alice.set_config("configured_addr", bob_addr)
97
+
98
+ # Load old address so it is cached.
99
+ assert alice.get_config("configured_addr") == old_alice_addr
100
+ alice.set_config("configured_addr", new_alice_addr)
101
+ # Make sure that setting `configured_addr` invalidated the cache.
102
+ assert alice.get_config("configured_addr") == new_alice_addr
103
+
104
+ alice_vcard = alice.self_contact.make_vcard()
105
+ assert old_alice_addr not in alice_vcard
106
+ assert new_alice_addr in alice_vcard
107
+ with pytest.raises(JsonRpcError):
108
+ alice.delete_transport(new_alice_addr)
109
+ alice.start_io()
110
+
111
+ alice_chat_bob.send_text("Hello again!")
112
+
113
+ msg2 = bob.wait_for_incoming_msg().get_snapshot()
114
+ sender_addr2 = msg2.sender.get_snapshot().address
115
+
116
+ assert msg1.sender == msg2.sender
117
+ assert sender_addr1 != sender_addr2
118
+ assert sender_addr1 == old_alice_addr
119
+ assert sender_addr2 == new_alice_addr
120
+
121
+
122
+ @pytest.mark.parametrize("is_chatmail", ["0", "1"])
123
+ def test_mvbox_move_first_transport(acfactory, is_chatmail) -> None:
124
+ """Test that mvbox_move is disabled by default even for non-chatmail accounts.
125
+ Disabling mvbox_move is required to be able to setup a second transport.
126
+ """
127
+ account = acfactory.get_unconfigured_account()
128
+
129
+ account.set_config("fix_is_chatmail", "1")
130
+ account.set_config("is_chatmail", is_chatmail)
131
+
132
+ # The default value when the setting is unset is "1".
133
+ # This is not changed for compatibility with old databases
134
+ # imported from backups.
135
+ assert account.get_config("mvbox_move") == "1"
136
+
137
+ qr = acfactory.get_account_qr()
138
+ account.add_transport_from_qr(qr)
139
+
140
+ # Once the first transport is set up,
141
+ # mvbox_move is disabled.
142
+ assert account.get_config("mvbox_move") == "0"
143
+ assert account.get_config("is_chatmail") == is_chatmail
144
+
145
+
146
+ def test_reconfigure_transport(acfactory) -> None:
147
+ """Test that reconfiguring the transport works
148
+ even if settings not supported for multi-transport
149
+ like mvbox_move are enabled."""
150
+ account = acfactory.get_online_account()
151
+ account.set_config("mvbox_move", "1")
152
+
153
+ [transport] = account.list_transports()
154
+ account.add_or_update_transport(transport)
155
+
156
+ # Reconfiguring the transport should not reset
157
+ # the settings as if when configuring the first transport.
158
+ assert account.get_config("mvbox_move") == "1"
@@ -0,0 +1,20 @@
1
+ import subprocess
2
+ import sys
3
+ from platform import system # noqa
4
+
5
+ import pytest
6
+
7
+ from deltachat_rpc_client import DeltaChat, Rpc
8
+
9
+
10
+ @pytest.mark.skipif("system() == 'Windows'")
11
+ def test_install_venv_and_use_other_core(tmp_path):
12
+ venv = tmp_path.joinpath("venv1")
13
+ subprocess.check_call([sys.executable, "-m", "venv", venv])
14
+ python = venv / "bin" / "python"
15
+ subprocess.check_call([python, "-m", "pip", "install", "deltachat-rpc-server==2.20.0"])
16
+ rpc = Rpc(accounts_dir=tmp_path.joinpath("accounts"), rpc_server_path=venv.joinpath("bin", "deltachat-rpc-server"))
17
+
18
+ with rpc:
19
+ dc = DeltaChat(rpc)
20
+ assert dc.rpc.get_system_info()["deltachat_core_version"] == "v2.20.0"
@@ -158,29 +158,29 @@ def test_qr_securejoin_broadcast(acfactory, all_devices_online):
158
158
  chat = get_broadcast(ac)
159
159
  chat_msgs = chat.get_messages()
160
160
 
161
+ encrypted_msg = chat_msgs.pop(0).get_snapshot()
162
+ assert encrypted_msg.text == "Messages are end-to-end encrypted."
163
+ assert encrypted_msg.is_info
164
+
161
165
  if please_wait_info_msg:
162
166
  first_msg = chat_msgs.pop(0).get_snapshot()
163
- assert first_msg.text == "Establishing guaranteed end-to-end encryption, please wait…"
167
+ assert "invited you to join this channel" in first_msg.text
164
168
  assert first_msg.is_info
165
169
 
166
- encrypted_msg = chat_msgs[0].get_snapshot()
167
- assert encrypted_msg.text == "Messages are end-to-end encrypted."
168
- assert encrypted_msg.is_info
169
-
170
- member_added_msg = chat_msgs[1].get_snapshot()
170
+ member_added_msg = chat_msgs.pop(0).get_snapshot()
171
171
  if inviter_side:
172
172
  assert member_added_msg.text == f"Member {contact_snapshot.display_name} added."
173
173
  else:
174
174
  assert member_added_msg.text == "You joined the channel."
175
175
  assert member_added_msg.is_info
176
176
 
177
- hello_msg = chat_msgs[2].get_snapshot()
177
+ hello_msg = chat_msgs.pop(0).get_snapshot()
178
178
  assert hello_msg.text == "Hello everyone!"
179
179
  assert not hello_msg.is_info
180
180
  assert hello_msg.show_padlock
181
181
  assert hello_msg.error is None
182
182
 
183
- assert len(chat_msgs) == 3
183
+ assert len(chat_msgs) == 0
184
184
 
185
185
  chat_snapshot = chat.get_full_snapshot()
186
186
  assert chat_snapshot.is_encrypted
@@ -467,7 +467,7 @@ def test_bot(acfactory) -> None:
467
467
 
468
468
 
469
469
  def test_wait_next_messages(acfactory) -> None:
470
- alice = acfactory.new_configured_account()
470
+ alice = acfactory.get_online_account()
471
471
 
472
472
  # Create a bot account so it does not receive device messages in the beginning.
473
473
  addr, password = acfactory.get_credentials()
@@ -475,6 +475,7 @@ def test_wait_next_messages(acfactory) -> None:
475
475
  bot.set_config("bot", "1")
476
476
  bot.add_or_update_transport({"addr": addr, "password": password})
477
477
  assert bot.is_configured()
478
+ bot.bring_online()
478
479
 
479
480
  # There are no old messages and the call returns immediately.
480
481
  assert not bot.wait_next_messages()
@@ -660,8 +661,6 @@ def test_download_limit_chat_assignment(acfactory, tmp_path, n_accounts):
660
661
  contact = alice.create_contact(account)
661
662
  alice_group.add_contact(contact)
662
663
 
663
- if n_accounts == 2:
664
- bob_chat_alice = bob.create_chat(alice)
665
664
  bob.set_config("download_limit", str(download_limit))
666
665
 
667
666
  alice_group.send_text("hi")
@@ -677,15 +676,7 @@ def test_download_limit_chat_assignment(acfactory, tmp_path, n_accounts):
677
676
  alice_group.send_file(str(path))
678
677
  snapshot = bob.wait_for_incoming_msg().get_snapshot()
679
678
  assert snapshot.download_state == DownloadState.AVAILABLE
680
- if n_accounts > 2:
681
- assert snapshot.chat == bob_group
682
- else:
683
- # Group contains only Alice and Bob,
684
- # so partially downloaded messages are
685
- # hard to distinguish from private replies to group messages.
686
- #
687
- # Message may be a private reply, so we assign it to 1:1 chat with Alice.
688
- assert snapshot.chat == bob_chat_alice
679
+ assert snapshot.chat == bob_group
689
680
 
690
681
 
691
682
  def test_markseen_contact_request(acfactory):
@@ -867,15 +858,15 @@ def test_leave_broadcast(acfactory, all_devices_online):
867
858
  contact_snapshot = contact.get_snapshot()
868
859
  chat_msgs = chat.get_messages()
869
860
 
870
- if please_wait_info_msg:
871
- first_msg = chat_msgs.pop(0).get_snapshot()
872
- assert first_msg.text == "Establishing guaranteed end-to-end encryption, please wait…"
873
- assert first_msg.is_info
874
-
875
861
  encrypted_msg = chat_msgs.pop(0).get_snapshot()
876
862
  assert encrypted_msg.text == "Messages are end-to-end encrypted."
877
863
  assert encrypted_msg.is_info
878
864
 
865
+ if please_wait_info_msg:
866
+ first_msg = chat_msgs.pop(0).get_snapshot()
867
+ assert "invited you to join this channel" in first_msg.text
868
+ assert first_msg.is_info
869
+
879
870
  member_added_msg = chat_msgs.pop(0).get_snapshot()
880
871
  if inviter_side:
881
872
  assert member_added_msg.text == f"Member {contact_snapshot.display_name} added."
@@ -1002,3 +993,22 @@ def test_background_fetch(acfactory, dc):
1002
993
  snapshot = messages[-1].get_snapshot()
1003
994
  if snapshot.text == "Hello again!":
1004
995
  break
996
+
997
+
998
+ def test_message_exists(acfactory):
999
+ ac1, ac2 = acfactory.get_online_accounts(2)
1000
+ chat = ac1.create_chat(ac2)
1001
+ message1 = chat.send_text("Hello!")
1002
+ message2 = chat.send_text("Hello again!")
1003
+ assert message1.exists()
1004
+ assert message2.exists()
1005
+
1006
+ ac1.delete_messages([message1])
1007
+ assert not message1.exists()
1008
+ assert message2.exists()
1009
+
1010
+ # There is no error when checking if
1011
+ # the message exists for deleted account.
1012
+ ac1.remove()
1013
+ assert not message1.exists()
1014
+ assert not message2.exists()