from __future__ import annotations from datetime import UTC, datetime, timedelta import unittest from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from govoplan_access.backend.db import models as access_models # noqa: F401 from govoplan_core.core.provider_governance import ExternalProviderStateContext from govoplan_core.db.base import Base from govoplan_mail.backend.db.models import ( MailBounceSource, MailDeliveryCommand, MailMailboxFolderIndex, MailMailboxMessageIndex, MailServerEndpoint, MailServerProfile, ) from govoplan_mail.backend.manifest import manifest from govoplan_mail.backend.provider_state import ( IMAP_PROVIDER_ID, SMTP_PROVIDER_ID, imap_provider_states, smtp_provider_states, ) class MailProviderStateTests(unittest.TestCase): def setUp(self) -> None: self.engine = create_engine("sqlite+pysqlite:///:memory:", future=True) Base.metadata.create_all( self.engine, tables=( MailServerProfile.__table__, MailServerEndpoint.__table__, MailDeliveryCommand.__table__, MailMailboxFolderIndex.__table__, MailMailboxMessageIndex.__table__, MailBounceSource.__table__, ), ) self.session = sessionmaker(bind=self.engine, expire_on_commit=False)() self.profile = MailServerProfile( id="profile-1", tenant_id="tenant-1", scope_type="tenant", scope_id="tenant-1", name="Mail", slug="mail", is_active=True, smtp_config={"host": "smtp.example.test", "port": 587}, imap_config={"host": "imap.example.test", "port": 993}, ) self.session.add( self.profile ) self.session.add( MailMailboxFolderIndex( id="folder-1", tenant_id="tenant-1", profile_id=self.profile.id, folder="INBOX", indexed_at=datetime.now(UTC), ) ) self.session.commit() def tearDown(self) -> None: self.session.close() self.engine.dispose() def test_smtp_state_exposes_reconciliation_without_server_or_secrets(self) -> None: self.session.add( MailDeliveryCommand( id="command-1", tenant_id="tenant-1", command_type="message", source_module="notifications", source_resource_type="notification", idempotency_key="notification-1", canonical_request_hash="a" * 64, profile_id=self.profile.id, expected_smtp_transport_revision="revision-1", message_sha256="b" * 64, message_size_bytes=10, recipient_count=1, status="outcome_unknown", expires_at=datetime.now(UTC) + timedelta(days=1), ) ) self.session.flush() state = smtp_provider_states( ExternalProviderStateContext(session=self.session, tenant_id="tenant-1") )[0] self.assertEqual("warning", state.health) self.assertEqual("pending", state.conflict) self.assertEqual("attention", state.recovery) self.assertEqual(1, state.metrics["outcome_unknown_commands"]) self.assertNotIn("smtp.example.test", str(state.to_dict())) def test_imap_state_is_fresh_tenant_bounded_and_registered(self) -> None: state = imap_provider_states( ExternalProviderStateContext(session=self.session, tenant_id="tenant-1") )[0] self.assertEqual("healthy", state.health) self.assertEqual("current", state.freshness) self.assertEqual("external_mirror", state.authority_mode) self.assertNotIn("imap.example.test", str(state.to_dict())) self.assertEqual( (), imap_provider_states( ExternalProviderStateContext( session=self.session, tenant_id="tenant-2", ) ), ) self.assertEqual( {SMTP_PROVIDER_ID, IMAP_PROVIDER_ID}, {item.id for item in manifest.external_providers}, ) self.assertEqual( {SMTP_PROVIDER_ID, IMAP_PROVIDER_ID}, { item.provider_id for item in manifest.external_provider_state_providers }, ) if __name__ == "__main__": unittest.main()