Files
MeshChatX/tests/backend/test_local_message_retention.py
T

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()