# backend/tests/test_connector_contract.py — C1 인터페이스 계약 # mail/calendar 도메인의 실 커넥터가 모두 BaseConnector 의 fetch/normalize/write/sync 를 구현. import importlib import pytest from app.connectors.base import BaseConnector CONNECTORS = [ # phase-16+: mail/calendar 는 mock 제거 → 실 커넥터만(Gmail/Outlook/IMAP, Google 캘린더/ICS) "mail.real_gmail.GmailConnector", "mail.real_outlook.OutlookConnector", "mail.real_imap.ImapConnector", "calendar.real_google.GoogleCalendarConnector", "calendar.real_outlook.OutlookCalendarConnector", "calendar.ics_import.IcsConnector", ] def _load(path: str): mod, cls = path.rsplit(".", 1) return getattr(importlib.import_module("app.connectors." + mod), cls) @pytest.mark.parametrize("path", CONNECTORS) def test_connector_is_concrete_subclass(path): cls = _load(path) assert issubclass(cls, BaseConnector), path # 추상 미구현이 없어야 인스턴스화 가능 assert not getattr(cls, "__abstractmethods__", frozenset()), ( path, cls.__abstractmethods__, ) for m in ("fetch", "normalize", "write", "sync", "event_for"): assert callable(getattr(cls, m)), (path, m) def test_covers_mail_calendar_domains(): domains = {p.split(".", 1)[0] for p in CONNECTORS} assert domains == {"mail", "calendar"} assert len(CONNECTORS) == 6 # 실 커넥터만(Gmail/Outlook/IMAP, Google/Outlook 캘린더/ICS)