blob: d00b6fface37e97c34ea45b73bbaba80b0d323bd [file] [edit]
import datetime
import os
from typing import Dict, List, Optional
import pytest
from b4.review import messages
class TestGetDb:
"""Tests for get_db() and database creation."""
def test_creates_database(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
db_path = messages._get_db_path()
assert os.path.exists(db_path)
assert db_path.endswith('messages.sqlite3')
conn.close()
def test_creates_schema(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
cursor = conn.execute(
"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
)
tables = [row[0] for row in cursor.fetchall()]
assert 'messages' in tables
assert 'schema_version' in tables
conn.close()
def test_sets_schema_version(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
version = conn.execute('SELECT version FROM schema_version').fetchone()[0]
assert version == messages.SCHEMA_VERSION
conn.close()
def test_reopens_existing(self, tmp_path: pytest.TempPathFactory) -> None:
conn1 = messages.get_db()
messages.set_flag(conn1, 'test@example.com', 'Seen')
conn1.close()
conn2 = messages.get_db()
assert messages.get_flags(conn2, 'test@example.com') == 'Seen'
conn2.close()
class TestGetFlags:
"""Tests for get_flags()."""
def test_unknown_msgid(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
assert messages.get_flags(conn, 'unknown@example.com') == ''
conn.close()
def test_returns_flags(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
messages.set_flag(conn, 'test@example.com', 'Seen')
assert messages.get_flags(conn, 'test@example.com') == 'Seen'
conn.close()
class TestGetFlagsBulk:
"""Tests for get_flags_bulk()."""
def test_empty_list(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
assert messages.get_flags_bulk(conn, []) == {}
conn.close()
def test_returns_known_only(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
messages.set_flag(conn, 'a@example.com', 'Seen')
messages.set_flag(conn, 'b@example.com', 'Flagged')
result = messages.get_flags_bulk(
conn, ['a@example.com', 'b@example.com', 'c@example.com']
)
assert 'a@example.com' in result
assert 'b@example.com' in result
assert 'c@example.com' not in result
conn.close()
class TestSetFlag:
"""Tests for set_flag()."""
def test_creates_new_row(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
messages.set_flag(
conn, 'new@example.com', 'Seen', msg_date='2026-03-05T10:00:00'
)
flags = messages.get_flags(conn, 'new@example.com')
assert 'Seen' in flags
conn.close()
def test_adds_to_existing(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
messages.set_flag(conn, 'multi@example.com', 'Seen')
messages.set_flag(conn, 'multi@example.com', 'Flagged')
flags = messages.get_flags(conn, 'multi@example.com')
assert 'Seen' in flags
assert 'Flagged' in flags
conn.close()
def test_idempotent(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
messages.set_flag(conn, 'idem@example.com', 'Seen')
messages.set_flag(conn, 'idem@example.com', 'Seen')
flags = messages.get_flags(conn, 'idem@example.com')
assert flags == 'Seen'
conn.close()
def test_flags_sorted(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
messages.set_flag(conn, 'sort@example.com', 'Seen')
messages.set_flag(conn, 'sort@example.com', 'Flagged')
messages.set_flag(conn, 'sort@example.com', 'Answered')
flags = messages.get_flags(conn, 'sort@example.com')
assert flags == 'Answered Flagged Seen'
conn.close()
class TestSetFlagsBulk:
"""Tests for set_flags_bulk()."""
def test_sets_multiple(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
entries: List[Dict[str, Optional[str]]] = [
{'msgid': 'a@example.com', 'msg_date': '2026-01-01T00:00:00'},
{'msgid': 'b@example.com', 'msg_date': '2026-01-02T00:00:00'},
{'msgid': 'c@example.com', 'msg_date': None},
]
messages.set_flags_bulk(conn, entries, 'Seen')
result = messages.get_flags_bulk(
conn, ['a@example.com', 'b@example.com', 'c@example.com']
)
assert all('Seen' in v for v in result.values())
conn.close()
def test_skips_empty_msgid(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
entries = [
{'msgid': '', 'msg_date': None},
{'msgid': 'valid@example.com', 'msg_date': None},
]
messages.set_flags_bulk(conn, entries, 'Seen')
result = messages.get_flags_bulk(conn, ['', 'valid@example.com'])
assert '' not in result
assert 'valid@example.com' in result
conn.close()
def test_adds_to_existing_flags(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
messages.set_flag(conn, 'exist@example.com', 'Seen')
entries = [{'msgid': 'exist@example.com', 'msg_date': None}]
messages.set_flags_bulk(conn, entries, 'Answered')
flags = messages.get_flags(conn, 'exist@example.com')
assert 'Seen' in flags
assert 'Answered' in flags
conn.close()
class TestRemoveFlag:
"""Tests for remove_flag()."""
def test_removes_single_flag(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
messages.set_flag(conn, 'rm@example.com', 'Seen')
messages.set_flag(conn, 'rm@example.com', 'Flagged')
messages.remove_flag(conn, 'rm@example.com', 'Flagged')
flags = messages.get_flags(conn, 'rm@example.com')
assert 'Seen' in flags
assert 'Flagged' not in flags
conn.close()
def test_deletes_row_when_empty(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
messages.set_flag(conn, 'del@example.com', 'Seen')
messages.remove_flag(conn, 'del@example.com', 'Seen')
assert messages.get_flags(conn, 'del@example.com') == ''
conn.close()
def test_noop_on_unknown(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
messages.remove_flag(conn, 'nonexistent@example.com', 'Seen')
conn.close()
def test_noop_on_absent_flag(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
messages.set_flag(conn, 'noop@example.com', 'Seen')
messages.remove_flag(conn, 'noop@example.com', 'Flagged')
assert messages.get_flags(conn, 'noop@example.com') == 'Seen'
conn.close()
class TestCleanupOld:
"""Tests for cleanup_old()."""
def test_removes_old_entries(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
old_date = (
datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=200)
).isoformat()
messages.set_flag(conn, 'old@example.com', 'Seen', msg_date=old_date)
messages.set_flag(
conn,
'recent@example.com',
'Seen',
msg_date=datetime.datetime.now(datetime.timezone.utc).isoformat(),
)
deleted = messages.cleanup_old(conn, max_days=180)
assert deleted == 1
assert messages.get_flags(conn, 'old@example.com') == ''
assert messages.get_flags(conn, 'recent@example.com') == 'Seen'
conn.close()
def test_keeps_null_date(self, tmp_path: pytest.TempPathFactory) -> None:
conn = messages.get_db()
messages.set_flag(conn, 'nodate@example.com', 'Seen')
deleted = messages.cleanup_old(conn, max_days=0)
assert deleted == 0
assert messages.get_flags(conn, 'nodate@example.com') == 'Seen'
conn.close()