Files
DEMO-AGENT/tests/test_feishu_api_services.py

201 lines
6.5 KiB
Python

import json
from django.utils import timezone
import pytest
from review_agent.models import FeishuAccessTokenCache
from review_agent.notifications.context import NotificationContext
from review_agent.notifications.feishu_message_api import send_personal_message
from review_agent.notifications.feishu_token import app_id_hash, get_tenant_access_token
from review_agent.notifications.message_builder import build_feishu_post_message
from review_agent.notifications.recipient import resolve_configured_personal_recipient
pytestmark = pytest.mark.django_db
class FakeResponse:
def __init__(self, payload, status_code=200):
self.payload = payload
self.status_code = status_code
self.text = json.dumps(payload, ensure_ascii=False)
def json(self):
return self.payload
def test_token_service_fetches_and_caches(monkeypatch, settings):
settings.FEISHU_APP_ID = "cli_a"
settings.FEISHU_APP_SECRET = "secret"
calls = []
def fake_post(*args, **kwargs):
calls.append(kwargs)
return FakeResponse({"code": 0, "tenant_access_token": "tenant-token", "expire": 7200})
monkeypatch.setattr("review_agent.notifications.feishu_token.httpx.post", fake_post)
first = get_tenant_access_token()
second = get_tenant_access_token()
assert first.ok
assert second.tenant_access_token == "tenant-token"
assert len(calls) == 1
assert FeishuAccessTokenCache.objects.get(app_id_hash=app_id_hash("cli_a")).is_valid()
def test_token_service_refreshes_expired_cache(monkeypatch, settings):
settings.FEISHU_APP_ID = "cli_a"
settings.FEISHU_APP_SECRET = "secret"
FeishuAccessTokenCache.objects.create(
app_id_hash=app_id_hash("cli_a"),
tenant_access_token="old",
expires_at=timezone.now() - timezone.timedelta(minutes=1),
)
monkeypatch.setattr(
"review_agent.notifications.feishu_token.httpx.post",
lambda *args, **kwargs: FakeResponse({"code": 0, "tenant_access_token": "new", "expire": 7200}),
)
assert get_tenant_access_token().tenant_access_token == "new"
def test_token_service_returns_error_for_api_failure(monkeypatch, settings):
settings.FEISHU_APP_ID = "cli_a"
settings.FEISHU_APP_SECRET = "secret"
monkeypatch.setattr(
"review_agent.notifications.feishu_token.httpx.post",
lambda *args, **kwargs: FakeResponse({"code": 1, "msg": "bad secret"}),
)
result = get_tenant_access_token()
assert not result.ok
assert result.error_message == "bad secret"
def test_recipient_prefers_open_id(settings):
settings.FEISHU_DEFAULT_USER_OPEN_ID = "ou_xxx"
settings.FEISHU_DEFAULT_USER_ID = "user_xxx"
settings.FEISHU_DEFAULT_TARGET_NAME = "负责人"
target = resolve_configured_personal_recipient()
assert target.ok
assert target.identifier_type == "open_id"
assert target.identifier_value == "ou_xxx"
def test_recipient_uses_user_id_when_open_id_missing(settings):
settings.FEISHU_DEFAULT_USER_OPEN_ID = ""
settings.FEISHU_DEFAULT_USER_ID = "user_xxx"
target = resolve_configured_personal_recipient()
assert target.ok
assert target.identifier_type == "user_id"
def test_recipient_missing(settings):
settings.FEISHU_DEFAULT_USER_OPEN_ID = ""
settings.FEISHU_DEFAULT_USER_ID = ""
target = resolve_configured_personal_recipient()
assert not target.ok
assert target.error_code == "recipient_missing"
def test_build_feishu_post_message_contains_summary(settings):
settings.PUBLIC_BASE_URL = "http://example.test"
settings.FEISHU_DEFAULT_USER_OPEN_ID = "ou_xxx"
target = resolve_configured_personal_recipient()
context = NotificationContext(
workflow_type="file_summary",
workflow_name="自动汇总",
workflow_batch_id=1,
workflow_batch_no="FS-001",
workflow_status="success",
trigger_user_id=1,
trigger_username="owner",
title="自动汇总完成",
summary_lines=("文件 3 个", "异常 0 个"),
next_step="查看汇总结果",
result_path="/summary/1/",
)
payload = build_feishu_post_message(context, target)
assert payload["receive_id"] == "ou_xxx"
content = json.loads(payload["content"])
assert content["zh_cn"]["title"] == "自动汇总完成"
assert "http://example.test/summary/1/" in payload["content"]
def test_send_personal_message_success(monkeypatch, settings):
settings.FEISHU_MESSAGE_API_URL = "http://feishu/messages"
requests = []
def fake_post(*args, **kwargs):
requests.append(kwargs)
return FakeResponse({"code": 0, "data": {"message_id": "om_xxx"}})
monkeypatch.setattr("review_agent.notifications.feishu_message_api.httpx.post", fake_post)
result = send_personal_message(
tenant_access_token="token",
receive_id_type="open_id",
payload={"receive_id": "ou_xxx"},
)
assert result.ok
assert result.external_message_id == "om_xxx"
assert requests[0]["headers"]["Authorization"] == "Bearer token"
def test_send_personal_message_api_error(monkeypatch, settings):
settings.FEISHU_MESSAGE_API_URL = "http://feishu/messages"
monkeypatch.setattr(
"review_agent.notifications.feishu_message_api.httpx.post",
lambda *args, **kwargs: FakeResponse({"code": 230001, "msg": "bad receive_id"}),
)
result = send_personal_message(
tenant_access_token="token",
receive_id_type="open_id",
payload={"receive_id": "bad"},
)
assert not result.ok
assert result.error_code == "230001"
def test_send_personal_message_refreshes_token_once(monkeypatch, settings):
settings.FEISHU_MESSAGE_API_URL = "http://feishu/messages"
settings.FEISHU_APP_ID = "cli_a"
settings.FEISHU_APP_SECRET = "secret"
calls = {"message": 0}
def fake_message_post(*args, **kwargs):
calls["message"] += 1
if calls["message"] == 1:
return FakeResponse({"code": 99991663, "msg": "token expired"})
return FakeResponse({"code": 0, "data": {"message_id": "om_retry"}})
monkeypatch.setattr("review_agent.notifications.feishu_message_api.httpx.post", fake_message_post)
monkeypatch.setattr(
"review_agent.notifications.feishu_token.httpx.post",
lambda *args, **kwargs: FakeResponse({"code": 0, "tenant_access_token": "fresh", "expire": 7200}),
)
result = send_personal_message(
tenant_access_token="stale",
receive_id_type="open_id",
payload={"receive_id": "ou_xxx"},
)
assert result.ok
assert result.refreshed_token
assert calls["message"] == 2