You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
86 lines
2.8 KiB
Python
86 lines
2.8 KiB
Python
# backend/tests/test_federation_real.py — C7 real(가짜) sync → event_bus 발행
|
|
# 같은 코드 경로(GmailConnector.normalize/write/sync)로 mail.received 가 실제 발행됨을 증명.
|
|
from app import seed as seedmod
|
|
from app.automation.event_bus import bus
|
|
from app.connectors.base import RawRecord
|
|
from app.connectors.mail.real_gmail import GmailConnector
|
|
from app.models import ConnectorAccount, ConnectorDomain, ConnectorMode, ConnState
|
|
|
|
|
|
def _mail_account(s):
|
|
# phase-16+: 메일 mock 계정 시드 제거 → real 연결 계정을 직접 생성.
|
|
a = ConnectorAccount(
|
|
id="ca-mail-personal", domain=ConnectorDomain.mail, provider="gmail",
|
|
mode=ConnectorMode.real, state=ConnState.connected, external_account_id="me@gmail.com",
|
|
name="me@gmail.com",
|
|
)
|
|
s.add(a)
|
|
s.commit()
|
|
return a
|
|
|
|
|
|
class FakeGmailConnector(GmailConnector):
|
|
"""network 없이 fetch 만 가짜로 — normalize/write/sync 는 real 코드 그대로."""
|
|
|
|
payloads: list = []
|
|
|
|
def fetch(self, session, *, full=False):
|
|
for p in self.payloads:
|
|
yield RawRecord(
|
|
external_id=p["id"],
|
|
payload=p,
|
|
etag=str(p.get("historyId", "")),
|
|
external_updated_at=None,
|
|
)
|
|
|
|
|
|
_MSG = {
|
|
"id": "gmail-real-1",
|
|
"historyId": "9001",
|
|
"labelIds": ["UNREAD", "IMPORTANT"],
|
|
"snippet": "현우님 온보딩 시안 v3 도착",
|
|
"internalDate": "1718000000000",
|
|
"payload": {
|
|
"headers": [
|
|
{"name": "From", "value": "hyunwoo@lumi.co"},
|
|
{"name": "Subject", "value": "온보딩 시안 v3"},
|
|
]
|
|
},
|
|
}
|
|
|
|
|
|
def test_real_sync_publishes_mail_received_once(session):
|
|
s, _ = session
|
|
seedmod.run_seed(session=s, reset=True)
|
|
acct = _mail_account(s)
|
|
|
|
before = len([e for e in bus.history if e.type == "mail.received"])
|
|
conn = FakeGmailConnector(acct)
|
|
conn.payloads = [_MSG]
|
|
r1 = conn.sync(s)
|
|
assert r1.upserted == 1 and r1.events_published == ["mail.received"]
|
|
|
|
# 같은 historyId(etag) 재공급 → 멱등(이벤트 0)
|
|
conn2 = FakeGmailConnector(acct)
|
|
conn2.payloads = [_MSG]
|
|
r2 = conn2.sync(s)
|
|
assert r2.upserted == 0 and r2.events_published == []
|
|
|
|
after = len([e for e in bus.history if e.type == "mail.received"])
|
|
assert after - before == 1 # 새 메일당 정확히 1회
|
|
|
|
|
|
def test_real_sync_failure_does_not_crash(session):
|
|
s, _ = session
|
|
seedmod.run_seed(session=s, reset=True)
|
|
acct = _mail_account(s)
|
|
|
|
class BoomConnector(GmailConnector):
|
|
def fetch(self, session, *, full=False):
|
|
raise RuntimeError("network down")
|
|
|
|
res = BoomConnector(acct).sync(s) # 예외가 앱을 멈추지 않음
|
|
assert res.errors == 1 and "network down" in res.detail
|
|
s.refresh(acct)
|
|
assert acct.state == "error"
|