AutoTeam-F / tests /unit /test_mail_simplelogin.py
ZRainbow's picture
feat(round-12 S2): mail provider fallback chain + addy.io + simplelogin
5041525
Raw
History Blame Contribute Delete
5.9 kB
"""SimpleLogin provider 单元测试 — 全 mock requests,无真实 HTTP。"""
from __future__ import annotations
from unittest.mock import MagicMock
import pytest
from autoteam.mail import simplelogin as mod
from autoteam.mail.fallback import MailProviderUnavailable
def _make_response(status: int, body: dict | None = None, text: str = "") -> MagicMock:
r = MagicMock()
r.status_code = status
r.text = text or ("" if body is None else "{}")
r.json = MagicMock(return_value=body if body is not None else {})
return r
def _make_client(monkeypatch) -> mod.SimpleLoginClient:
monkeypatch.setenv("SIMPLELOGIN_API_KEY", "key-abc")
monkeypatch.setenv("SIMPLELOGIN_BASE_URL", "https://sl.example.com")
monkeypatch.setenv("SIMPLELOGIN_HOSTNAME", "openai.com")
client = mod.SimpleLoginClient()
client.session = MagicMock()
return client
def test_init_missing_api_key_raises_unavailable(monkeypatch):
monkeypatch.delenv("SIMPLELOGIN_API_KEY", raising=False)
with pytest.raises(MailProviderUnavailable) as exc:
mod.SimpleLoginClient()
assert "SIMPLELOGIN_API_KEY" in str(exc.value)
def test_init_uses_default_base_url_when_unset(monkeypatch):
monkeypatch.setenv("SIMPLELOGIN_API_KEY", "key-abc")
monkeypatch.delenv("SIMPLELOGIN_BASE_URL", raising=False)
client = mod.SimpleLoginClient()
assert client.base_url == mod.DEFAULT_BASE_URL
def test_headers_use_simplelogin_authentication_header(monkeypatch):
client = _make_client(monkeypatch)
h = client._headers()
assert h["Authentication"] == "key-abc"
assert "Authorization" not in h
assert h["Accept"] == "application/json"
def test_login_calls_user_info(monkeypatch):
client = _make_client(monkeypatch)
client.session.request.return_value = _make_response(200, {"email": "me@example.com"})
out = client.login()
assert out == "key-abc"
args, _ = client.session.request.call_args
assert args[0] == "GET"
assert "/api/user_info" in args[1]
def test_login_auth_failure_raises(monkeypatch):
client = _make_client(monkeypatch)
client.session.request.return_value = _make_response(401, text="bad key")
with pytest.raises(Exception, match="401"):
client.login()
def test_create_temp_email_random_alias(monkeypatch):
client = _make_client(monkeypatch)
client.session.request.return_value = _make_response(
201, {"id": 42, "alias": "auto.xyz@aleeas.com"}
)
aid, email = client.create_temp_email(prefix="autoteam")
assert aid == 42
assert email == "auto.xyz@aleeas.com"
args, kw = client.session.request.call_args
assert args[0] == "POST"
assert "/api/alias/random/new" in args[1]
assert kw["params"] == {"hostname": "openai.com"}
assert "note" in kw["json"]
def test_create_temp_email_response_missing_fields_raises(monkeypatch):
client = _make_client(monkeypatch)
client.session.request.return_value = _make_response(201, {"id": 99})
with pytest.raises(Exception, match="缺"):
client.create_temp_email()
def test_list_accounts_paginates_and_stops_when_empty(monkeypatch):
client = _make_client(monkeypatch)
page0 = _make_response(
200,
{"aliases": [
{"id": 1, "email": "a@x.com"},
{"id": 2, "email": "b@x.com"},
]},
)
page1 = _make_response(200, {"aliases": []})
client.session.request.side_effect = [page0, page1]
rows = client.list_accounts(size=100)
assert len(rows) == 2
assert rows[0]["accountId"] == 1
assert rows[0]["email"] == "a@x.com"
# 翻了 2 页
assert client.session.request.call_count == 2
def test_list_accounts_stops_at_target_size(monkeypatch):
client = _make_client(monkeypatch)
page0 = _make_response(
200,
{"aliases": [
{"id": i, "email": f"x{i}@x.com"} for i in range(20)
]},
)
client.session.request.return_value = page0
rows = client.list_accounts(size=5)
# 单页就够,目标 5 条命中后立即停
assert len(rows) == 5
def test_delete_account_by_id(monkeypatch):
client = _make_client(monkeypatch)
client.session.request.return_value = _make_response(204)
res = client.delete_account(42)
assert res["code"] == 200
args, _ = client.session.request.call_args
assert args[0] == "DELETE"
assert "/api/aliases/42" in args[1]
def test_delete_account_by_email_resolves_id(monkeypatch):
client = _make_client(monkeypatch)
list_resp = _make_response(
200,
{"aliases": [{"id": 7, "email": "match@x.com"}]},
)
page2 = _make_response(200, {"aliases": []})
del_resp = _make_response(204)
client.session.request.side_effect = [list_resp, page2, del_resp]
res = client.delete_account("match@x.com")
assert res["code"] == 200
last_args = client.session.request.call_args_list[-1]
assert "/api/aliases/7" in last_args.args[1]
def test_inbox_methods_return_empty_with_warning(monkeypatch, caplog):
client = _make_client(monkeypatch)
with caplog.at_level("WARNING", logger="autoteam.mail.simplelogin"):
assert client.search_emails_by_recipient("foo@x.com") == []
assert client.list_emails(7) == []
assert client.delete_emails_for("foo@x.com") == 0
msgs = " ".join(r.message for r in caplog.records)
assert "inbox" in msgs.lower() or "alias forwarding" in msgs.lower()
def test_implements_full_mail_provider_abc(monkeypatch):
from autoteam.mail.base import MailProvider
client = _make_client(monkeypatch)
assert isinstance(client, MailProvider)
for name in (
"login",
"create_temp_email",
"list_accounts",
"delete_account",
"search_emails_by_recipient",
"list_emails",
"delete_emails_for",
):
assert callable(getattr(client, name))