"""Mail from the address book is never filed as spam (cross-plugin, §C.4). Exercises the real lookup against the contacts plugin's own table, which is what the hook consults. Requires both plugins installed. """ from __future__ import annotations import uuid from collections.abc import AsyncIterator import pytest_asyncio from sqlalchemy import delete, text from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine from sqlalchemy.pool import NullPool from webmail_plugin_antispam.policy import sender_in_contacts from app.config import get_settings from app.identity.passwords import hash_password from app.models import User from app.plugins.sdk import sdk _SDK = sdk("antispam") @pytest_asyncio.fixture async def user_with_contact() -> AsyncIterator[uuid.UUID]: engine = create_async_engine(get_settings().database_url, poolclass=NullPool) sm = async_sessionmaker(engine, expire_on_commit=False) async with sm() as s: user = User( handle=f"ac{uuid.uuid4().hex[:8]}@test.local", password_hash=hash_password("pw") ) s.add(user) await s.commit() user_id = user.id async with _SDK.session(user_id) as s: book_id = ( await s.execute( text( "INSERT INTO plg_contacts_books (user_id, name) VALUES (:u, 'Test') " "RETURNING id" ), {"u": str(user_id)}, ) ).scalar() await s.execute( text( "INSERT INTO plg_contacts_contacts " " (user_id, book_id, vcard_uid, name_display, default_email, emails) " "VALUES (:u, :b, :v, 'A Friend', 'Friend@Example.COM', " " CAST(:e AS jsonb))" ), { "u": str(user_id), "b": str(book_id), "v": str(uuid.uuid4()), "e": '[{"value": "Other.Address@Example.com", "type": "work"}]', }, ) await s.commit() try: yield user_id finally: async with sm() as s: await s.execute( text("DELETE FROM plg_contacts_books WHERE user_id = :u"), {"u": str(user_id)} ) await s.execute(delete(User).where(User.id == user_id)) await s.commit() await engine.dispose() async def test_primary_address_matches_case_insensitively(user_with_contact: uuid.UUID) -> None: """Contacts store what the user typed, so the comparison cannot be exact.""" for probe in ("friend@example.com", "Friend@Example.COM", " FRIEND@EXAMPLE.com "): assert await sender_in_contacts(user_with_contact, probe) is True async def test_secondary_addresses_in_the_emails_array_match( user_with_contact: uuid.UUID, ) -> None: assert await sender_in_contacts(user_with_contact, "other.address@example.com") is True async def test_a_stranger_does_not_match(user_with_contact: uuid.UUID) -> None: assert await sender_in_contacts(user_with_contact, "nobody@example.com") is False assert await sender_in_contacts(user_with_contact, "") is False async def test_another_users_contacts_do_not_count(user_with_contact: uuid.UUID) -> None: """RLS: one user's address book must not shield another user's spam.""" assert await sender_in_contacts(uuid.uuid4(), "friend@example.com") is False