mirror of
https://git.quad4.io/RNS-Things/MeshChatX.git
synced 2026-04-27 17:15:51 +00:00
308 lines
8.8 KiB
Python
308 lines
8.8 KiB
Python
# SPDX-License-Identifier: 0BSD
|
|
|
|
import json
|
|
|
|
import pytest
|
|
|
|
import meshchatx.android_push_bridge as android_push_bridge
|
|
from meshchatx.android_push_bridge import (
|
|
install_websocket_hook,
|
|
lxmf_delivery_notification_text,
|
|
)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def reset_ws_hook_flag():
|
|
android_push_bridge._ws_hook_installed = False
|
|
yield
|
|
android_push_bridge._ws_hook_installed = False
|
|
|
|
|
|
def test_lxmf_delivery_skips_wrong_type():
|
|
assert lxmf_delivery_notification_text({"type": "config"}) is None
|
|
|
|
|
|
def test_lxmf_delivery_skips_when_sieve_suppresses():
|
|
assert (
|
|
lxmf_delivery_notification_text(
|
|
{
|
|
"type": "lxmf.delivery",
|
|
"sieve_suppress_notifications": True,
|
|
"remote_identity_name": "Alice",
|
|
"lxmf_message": {"is_incoming": True, "title": "Hi", "content": "Body"},
|
|
},
|
|
)
|
|
is None
|
|
)
|
|
|
|
|
|
def test_lxmf_delivery_skips_outbound():
|
|
assert (
|
|
lxmf_delivery_notification_text(
|
|
{
|
|
"type": "lxmf.delivery",
|
|
"remote_identity_name": "Bob",
|
|
"lxmf_message": {
|
|
"is_incoming": False,
|
|
"title": "Hi",
|
|
"content": "Body",
|
|
},
|
|
},
|
|
)
|
|
is None
|
|
)
|
|
|
|
|
|
def test_lxmf_delivery_skips_telemetry_only():
|
|
assert (
|
|
lxmf_delivery_notification_text(
|
|
{
|
|
"type": "lxmf.delivery",
|
|
"remote_identity_name": "Peer",
|
|
"lxmf_message": {
|
|
"is_incoming": True,
|
|
"title": "",
|
|
"content": "",
|
|
"fields": {"telemetry": {"lat": 1}},
|
|
},
|
|
},
|
|
)
|
|
is None
|
|
)
|
|
|
|
|
|
def test_lxmf_delivery_incoming_title_and_body():
|
|
assert lxmf_delivery_notification_text(
|
|
{
|
|
"type": "lxmf.delivery",
|
|
"remote_identity_name": "Charlie",
|
|
"lxmf_message": {
|
|
"is_incoming": True,
|
|
"title": "Subject",
|
|
"content": "Hello there",
|
|
},
|
|
},
|
|
) == ("Charlie", "Subject\nHello there")
|
|
|
|
|
|
def test_lxmf_delivery_truncates_long_content():
|
|
long_body = "x" * 250
|
|
title, body = lxmf_delivery_notification_text(
|
|
{
|
|
"type": "lxmf.delivery",
|
|
"remote_identity_name": "Dana",
|
|
"lxmf_message": {
|
|
"is_incoming": True,
|
|
"title": "",
|
|
"content": long_body,
|
|
},
|
|
},
|
|
)
|
|
assert title == "Dana"
|
|
assert body.endswith("...")
|
|
assert len(body) == 200
|
|
|
|
|
|
def test_lxmf_delivery_reaction():
|
|
assert lxmf_delivery_notification_text(
|
|
{
|
|
"type": "lxmf.delivery",
|
|
"remote_identity_name": "Eve",
|
|
"lxmf_message": {
|
|
"is_incoming": True,
|
|
"is_reaction": True,
|
|
"reaction_emoji": "thumbsup",
|
|
"title": "",
|
|
"content": "",
|
|
},
|
|
},
|
|
) == ("Eve", "Reaction thumbsup")
|
|
|
|
|
|
def test_lxmf_delivery_default_sender():
|
|
t, b = lxmf_delivery_notification_text(
|
|
{
|
|
"type": "lxmf.delivery",
|
|
"lxmf_message": {
|
|
"is_incoming": True,
|
|
"title": "Only title",
|
|
"content": "",
|
|
},
|
|
},
|
|
)
|
|
assert t == "Mesh"
|
|
assert b == "Only title"
|
|
|
|
|
|
def test_lxmf_delivery_image_fields_only():
|
|
assert lxmf_delivery_notification_text(
|
|
{
|
|
"type": "lxmf.delivery",
|
|
"remote_identity_name": "Fran",
|
|
"lxmf_message": {
|
|
"is_incoming": True,
|
|
"title": "",
|
|
"content": "",
|
|
"fields": {"image": {"image_type": "png", "image_size": 1}},
|
|
},
|
|
},
|
|
) == ("Fran", "Image message")
|
|
|
|
|
|
def test_lxmf_delivery_audio_fields_only():
|
|
assert lxmf_delivery_notification_text(
|
|
{
|
|
"type": "lxmf.delivery",
|
|
"remote_identity_name": "Gus",
|
|
"lxmf_message": {
|
|
"is_incoming": True,
|
|
"title": "",
|
|
"content": "",
|
|
"fields": {"audio": {"audio_mode": 1, "audio_size": 1}},
|
|
},
|
|
},
|
|
) == ("Gus", "Audio message")
|
|
|
|
|
|
def test_lxmf_delivery_attachment_fields_only():
|
|
assert lxmf_delivery_notification_text(
|
|
{
|
|
"type": "lxmf.delivery",
|
|
"remote_identity_name": "Hal",
|
|
"lxmf_message": {
|
|
"is_incoming": True,
|
|
"title": "",
|
|
"content": "",
|
|
"fields": {
|
|
"file_attachments": [{"file_name": "a.bin", "file_size": 1}]
|
|
},
|
|
},
|
|
},
|
|
) == ("Hal", "Attachment")
|
|
|
|
|
|
def test_lxmf_delivery_skips_non_dict_message():
|
|
assert (
|
|
lxmf_delivery_notification_text(
|
|
{
|
|
"type": "lxmf.delivery",
|
|
"remote_identity_name": "Ian",
|
|
"lxmf_message": "not-a-dict",
|
|
},
|
|
)
|
|
is None
|
|
)
|
|
|
|
|
|
def test_after_websocket_broadcast_ignores_non_string(monkeypatch):
|
|
calls = []
|
|
monkeypatch.setattr(android_push_bridge, "_notify_java", lambda *a: calls.append(a))
|
|
android_push_bridge._after_websocket_broadcast(123)
|
|
assert calls == []
|
|
|
|
|
|
def test_after_websocket_broadcast_ignores_invalid_json(monkeypatch):
|
|
calls = []
|
|
monkeypatch.setattr(android_push_bridge, "_notify_java", lambda *a: calls.append(a))
|
|
android_push_bridge._after_websocket_broadcast("{not json")
|
|
assert calls == []
|
|
|
|
|
|
def test_after_websocket_broadcast_skips_when_notification_text_none(monkeypatch):
|
|
calls = []
|
|
monkeypatch.setattr(android_push_bridge, "_notify_java", lambda *a: calls.append(a))
|
|
android_push_bridge._after_websocket_broadcast(json.dumps({"type": "config"}))
|
|
assert calls == []
|
|
|
|
|
|
def test_after_websocket_broadcast_notifies_with_dedupe(monkeypatch):
|
|
calls = []
|
|
monkeypatch.setattr(android_push_bridge, "_notify_java", lambda *a: calls.append(a))
|
|
payload = {
|
|
"type": "lxmf.delivery",
|
|
"remote_identity_name": "Jules",
|
|
"lxmf_message": {
|
|
"is_incoming": True,
|
|
"title": "Hi",
|
|
"content": "There",
|
|
"hash": "abcdef0123456789",
|
|
},
|
|
}
|
|
android_push_bridge._after_websocket_broadcast(json.dumps(payload))
|
|
assert len(calls) == 1
|
|
assert calls[0][0] == "Jules"
|
|
assert calls[0][1] == "Hi\nThere"
|
|
assert calls[0][2] == "abcdef0123456789"
|
|
|
|
|
|
def test_after_websocket_broadcast_short_hash_skips_dedupe(monkeypatch):
|
|
calls = []
|
|
monkeypatch.setattr(android_push_bridge, "_notify_java", lambda *a: calls.append(a))
|
|
payload = {
|
|
"type": "lxmf.delivery",
|
|
"remote_identity_name": "Kim",
|
|
"lxmf_message": {
|
|
"is_incoming": True,
|
|
"title": "T",
|
|
"content": "",
|
|
"hash": "short",
|
|
},
|
|
}
|
|
android_push_bridge._after_websocket_broadcast(json.dumps(payload))
|
|
assert calls[0][2] is None
|
|
|
|
|
|
def test_install_skips_when_not_chaquopy_android(monkeypatch):
|
|
monkeypatch.setattr(android_push_bridge, "_is_chaquopy_android", lambda: False)
|
|
|
|
class Rmc:
|
|
async def websocket_broadcast(self, data):
|
|
return data
|
|
|
|
original = Rmc.websocket_broadcast
|
|
install_websocket_hook(Rmc)
|
|
assert Rmc.websocket_broadcast is original
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_install_wraps_broadcast_and_notifies(monkeypatch):
|
|
monkeypatch.setattr(android_push_bridge, "_is_chaquopy_android", lambda: True)
|
|
calls = []
|
|
monkeypatch.setattr(android_push_bridge, "_notify_java", lambda *a: calls.append(a))
|
|
|
|
class Rmc:
|
|
async def websocket_broadcast(self, data):
|
|
return "ok"
|
|
|
|
unwrapped = Rmc.websocket_broadcast
|
|
install_websocket_hook(Rmc)
|
|
payload = {
|
|
"type": "lxmf.delivery",
|
|
"remote_identity_name": "Lee",
|
|
"lxmf_message": {"is_incoming": True, "title": "A", "content": "B"},
|
|
}
|
|
result = await Rmc().websocket_broadcast(json.dumps(payload))
|
|
assert result == "ok"
|
|
assert len(calls) == 1
|
|
assert calls[0][:2] == ("Lee", "A\nB")
|
|
Rmc.websocket_broadcast = unwrapped
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_install_is_idempotent(monkeypatch):
|
|
monkeypatch.setattr(android_push_bridge, "_is_chaquopy_android", lambda: True)
|
|
invocations = []
|
|
|
|
class Rmc:
|
|
async def websocket_broadcast(self, data):
|
|
invocations.append("orig")
|
|
return None
|
|
|
|
unwrapped = Rmc.websocket_broadcast
|
|
install_websocket_hook(Rmc)
|
|
install_websocket_hook(Rmc)
|
|
monkeypatch.setattr(android_push_bridge, "_notify_java", lambda *a: None)
|
|
await Rmc().websocket_broadcast(json.dumps({"type": "config"}))
|
|
assert invocations == ["orig"]
|
|
Rmc.websocket_broadcast = unwrapped
|