mirror of
https://git.quad4.io/RNS-Things/MeshChatX.git
synced 2026-05-12 13:44:43 +00:00
220 lines
6.2 KiB
Python
220 lines
6.2 KiB
Python
# SPDX-License-Identifier: 0BSD
|
|
|
|
import time
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
|
|
from meshchatx.src.backend import local_message_retention as lmr
|
|
from meshchatx.src.backend.database import Database
|
|
from meshchatx.src.backend.database.provider import DatabaseProvider
|
|
from meshchatx.src.backend.database.schema import DatabaseSchema
|
|
|
|
|
|
def test_normalize_unit():
|
|
assert lmr.normalize_unit("DAYS") == lmr.UNIT_DAYS
|
|
assert lmr.normalize_unit("Month") == lmr.UNIT_MONTHS
|
|
assert lmr.normalize_unit("m") == lmr.UNIT_MONTHS
|
|
assert lmr.normalize_unit(None) == lmr.UNIT_DAYS
|
|
|
|
|
|
def test_retention_window_seconds():
|
|
assert lmr.retention_window_seconds(1, "days") == 86400
|
|
assert lmr.retention_window_seconds(2, "months") == 2 * 30 * 86400
|
|
assert lmr.retention_window_seconds(20000, "days") == lmr.MAX_VALUE_DAYS * 86400
|
|
assert (
|
|
lmr.retention_window_seconds(200, "months") == lmr.MAX_VALUE_MONTHS * 30 * 86400
|
|
)
|
|
|
|
|
|
def test_local_message_retention_cutoff_ts():
|
|
now = 1_000_000.0
|
|
c = lmr.local_message_retention_cutoff_ts(now, 1, "days")
|
|
assert c == now - 86400.0
|
|
|
|
|
|
@pytest.fixture
|
|
def _db_path(tmp_path):
|
|
return str(tmp_path / "t.db")
|
|
|
|
|
|
def test_apply_deletes_and_prunes(_db_path):
|
|
provider = DatabaseProvider(_db_path)
|
|
DatabaseSchema(provider).initialize()
|
|
db = Database(_db_path)
|
|
now = time.time()
|
|
old_ts = now - 10 * 86400
|
|
new_ts = now - 86400
|
|
peer = "a" * 32
|
|
base = {
|
|
"source_hash": peer,
|
|
"destination_hash": peer,
|
|
"peer_hash": peer,
|
|
"state": "delivered",
|
|
"progress": 1.0,
|
|
"is_incoming": 1,
|
|
"method": "ephemeral",
|
|
"delivery_attempts": 0,
|
|
"next_delivery_attempt_at": None,
|
|
"title": "t",
|
|
"content": "c",
|
|
"fields": None,
|
|
"rssi": None,
|
|
"snr": None,
|
|
"quality": None,
|
|
"is_spam": 0,
|
|
"reply_to_hash": None,
|
|
"attachments_stripped": 0,
|
|
}
|
|
db.messages.upsert_lxmf_message(
|
|
{**base, "hash": "a" * 32, "timestamp": old_ts},
|
|
)
|
|
db.messages.upsert_lxmf_message(
|
|
{**base, "hash": "b" * 32, "timestamp": new_ts},
|
|
)
|
|
db.provider.execute(
|
|
"INSERT INTO lxmf_conversation_read_state (destination_hash) VALUES (?)",
|
|
(peer,),
|
|
)
|
|
n = lmr.apply_local_message_retention(
|
|
db.messages,
|
|
None,
|
|
value=2,
|
|
unit=lmr.UNIT_DAYS,
|
|
now=now,
|
|
)
|
|
assert n == 1
|
|
assert db.messages.count_lxmf_messages() == 1
|
|
left = db.provider.fetchone(
|
|
"SELECT 1 AS ok FROM lxmf_messages WHERE hash = ?", ("b" * 32,)
|
|
)
|
|
assert left is not None
|
|
rs = db.provider.fetchall(
|
|
"SELECT 1 AS ok FROM lxmf_conversation_read_state WHERE destination_hash = ?",
|
|
(peer,),
|
|
)
|
|
assert len(rs) == 1
|
|
db.close_all()
|
|
provider.close_all()
|
|
|
|
|
|
def test_prune_clears_read_state_when_conversation_empty(_db_path):
|
|
provider = DatabaseProvider(_db_path)
|
|
DatabaseSchema(provider).initialize()
|
|
db = Database(_db_path)
|
|
now = time.time()
|
|
peer = "c" * 32
|
|
h = "d" * 32
|
|
old_ts = now - 3 * 86400
|
|
db.messages.upsert_lxmf_message(
|
|
{
|
|
"hash": h,
|
|
"source_hash": peer,
|
|
"destination_hash": peer,
|
|
"peer_hash": peer,
|
|
"state": "delivered",
|
|
"progress": 1.0,
|
|
"is_incoming": 1,
|
|
"method": "ephemeral",
|
|
"delivery_attempts": 0,
|
|
"next_delivery_attempt_at": None,
|
|
"title": "t",
|
|
"content": "c",
|
|
"fields": None,
|
|
"timestamp": old_ts,
|
|
"rssi": None,
|
|
"snr": None,
|
|
"quality": None,
|
|
"is_spam": 0,
|
|
"reply_to_hash": None,
|
|
"attachments_stripped": 0,
|
|
},
|
|
)
|
|
db.provider.execute(
|
|
"INSERT INTO lxmf_conversation_read_state (destination_hash) VALUES (?)",
|
|
(peer,),
|
|
)
|
|
lmr.apply_local_message_retention(
|
|
db.messages,
|
|
None,
|
|
value=1,
|
|
unit=lmr.UNIT_DAYS,
|
|
now=now,
|
|
)
|
|
assert db.messages.count_lxmf_messages() == 0
|
|
assert (
|
|
len(
|
|
db.provider.fetchall(
|
|
"SELECT 1 AS x FROM lxmf_conversation_read_state WHERE destination_hash = ?",
|
|
(peer,),
|
|
)
|
|
)
|
|
== 0
|
|
)
|
|
db.close_all()
|
|
provider.close_all()
|
|
|
|
|
|
async def test_config_patch_local_message_retention_keys(mock_app):
|
|
await mock_app.update_config(
|
|
{
|
|
"local_message_auto_delete_enabled": True,
|
|
"local_message_auto_delete_value": 3,
|
|
"local_message_auto_delete_unit": "months",
|
|
},
|
|
)
|
|
assert mock_app.config.local_message_auto_delete_enabled.get() is True
|
|
assert mock_app.config.local_message_auto_delete_value.get() == 3
|
|
assert mock_app.config.local_message_auto_delete_unit.get() == "months"
|
|
await mock_app.update_config(
|
|
{
|
|
"local_message_auto_delete_value": 9999,
|
|
"local_message_auto_delete_unit": "months",
|
|
},
|
|
)
|
|
assert mock_app.config.local_message_auto_delete_value.get() == lmr.MAX_VALUE_MONTHS
|
|
|
|
|
|
def test_apply_calls_cancel_for_hex_hashes(_db_path):
|
|
provider = DatabaseProvider(_db_path)
|
|
DatabaseSchema(provider).initialize()
|
|
db = Database(_db_path)
|
|
now = time.time()
|
|
h = "aa" * 16
|
|
db.messages.upsert_lxmf_message(
|
|
{
|
|
"hash": h,
|
|
"source_hash": h,
|
|
"destination_hash": h,
|
|
"peer_hash": h,
|
|
"state": "sending",
|
|
"progress": 0.0,
|
|
"is_incoming": 0,
|
|
"method": "opportunistic",
|
|
"delivery_attempts": 0,
|
|
"next_delivery_attempt_at": None,
|
|
"title": "",
|
|
"content": "x",
|
|
"fields": None,
|
|
"timestamp": now - 5 * 86400,
|
|
"rssi": None,
|
|
"snr": None,
|
|
"quality": None,
|
|
"is_spam": 0,
|
|
"reply_to_hash": None,
|
|
"attachments_stripped": 0,
|
|
},
|
|
)
|
|
cancel = MagicMock()
|
|
lmr.apply_local_message_retention(
|
|
db.messages,
|
|
cancel,
|
|
value=1,
|
|
unit="days",
|
|
now=now,
|
|
)
|
|
cancel.assert_called()
|
|
assert db.messages.count_lxmf_messages() == 0
|
|
db.close_all()
|
|
provider.close_all()
|