core, python: fix refused renames, extend the client API (#7379)

* core: do not commit refused name changes

* feat(python): add error base and missing commands

* feat(python): let callers drive startup themselves

* style(python): satisfy the linter, skip generated types

* feat(python): expose the message of a command error

* core: update query plans

* core: fix the batch limit parse error on GHC 8.10

* feat(python): reject profile images no client can render
This commit is contained in:
sh
2026-08-17 09:02:14 +01:00
committed by GitHub
parent caac5a61a9
commit 55d18efe24
19 changed files with 718 additions and 82 deletions
@@ -1,3 +1,5 @@
import pytest
from simplex_chat import util
@@ -173,3 +175,73 @@ def test_reaction_text_emoji():
def test_reaction_text_tag():
r = {"chatReaction": {"reaction": {"type": "unknown", "tag": "thumbs_up"}}}
assert util.reaction_text(r) == "thumbs_up"
def test_merged_custom_data_adds_a_key_keeping_the_others():
data = {"other": {"kept": True}}
assert util.merged_custom_data(data, "mine", {"roster": "active"}) == {
"other": {"kept": True},
"mine": {"roster": "active"},
}
def test_merged_custom_data_does_not_mutate_the_original():
data = {"other": 1}
util.merged_custom_data(data, "mine", 2)
assert data == {"other": 1}
def test_merged_custom_data_replaces_an_existing_key():
assert util.merged_custom_data({"mine": "old"}, "mine", "new") == {"mine": "new"}
def test_merged_custom_data_on_an_empty_column():
assert util.merged_custom_data(None, "mine", 1) == {"mine": 1}
def test_merged_custom_data_removes_a_key():
assert util.merged_custom_data({"mine": 1, "other": 2}, "mine", None) == {"other": 2}
def test_merged_custom_data_clears_the_column_when_nothing_is_left():
# None is what the set commands read as "clear"; {} would be a wasted write
# of an empty object.
assert util.merged_custom_data({"mine": 1}, "mine", None) is None
def test_merged_custom_data_removing_a_key_that_is_not_there():
assert util.merged_custom_data({"other": 2}, "mine", None) == {"other": 2}
def test_conn_status_reads_the_tag():
contact = {"activeConn": {"connStatus": {"type": "ready"}}}
assert util.conn_status(contact) == "ready"
def test_conn_status_without_a_connection():
# api_create_member_contact produces exactly this: a contact row before
# any connection exists.
assert util.conn_status({"contactId": 3}) is None
def test_conn_status_with_a_null_connection():
assert util.conn_status({"activeConn": None}) is None
def test_check_profile_image_accepts_what_the_apps_decode():
png = "data:image/png;base64,AAA"
jpg = "data:image/jpg;base64,AAA"
assert util.check_profile_image(png) == png
assert util.check_profile_image(jpg) == jpg
def test_check_profile_image_rejects_another_media_type():
# image/jpeg is the easy mistake: the file extension is .jpeg, and the
# core stores it, but no client strips that prefix before decoding.
with pytest.raises(ValueError, match="must start with"):
util.check_profile_image("data:image/jpeg;base64,AAA")
def test_check_profile_image_rejects_a_remote_url():
with pytest.raises(ValueError, match="must start with"):
util.check_profile_image("https://simplex.chat/logo.png")