mirror of
https://github.com/langgenius/dify.git
synced 2026-09-05 16:55:14 +08:00
195 lines
7.0 KiB
Python
195 lines
7.0 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import UTC, datetime
|
|
from unittest.mock import Mock
|
|
|
|
import pytest
|
|
|
|
from machinery.context import RequestContext
|
|
from services.account_education_service import (
|
|
AccountEducationGateway,
|
|
AccountEducationRateLimiter,
|
|
AccountEducationService,
|
|
)
|
|
from services.account_errors import EducationRateLimitExceededError
|
|
from services.account_ports import AccountRepository
|
|
from services.entities.account_entities import (
|
|
AccountEducationActivation,
|
|
AccountEducationAutocomplete,
|
|
AccountEducationStatus,
|
|
AccountEducationVerification,
|
|
AccountSnapshot,
|
|
)
|
|
|
|
|
|
def _context() -> RequestContext:
|
|
return RequestContext(
|
|
request_id="request-1",
|
|
trace_id="trace-1",
|
|
account_id="account-1",
|
|
active_workspace_id="workspace-1",
|
|
)
|
|
|
|
|
|
def _account() -> AccountSnapshot:
|
|
return AccountSnapshot(
|
|
id="account-1",
|
|
name="Student",
|
|
email="student@example.edu",
|
|
avatar=None,
|
|
is_password_set=True,
|
|
interface_language="en-US",
|
|
interface_theme="light",
|
|
timezone="UTC",
|
|
last_login_at=None,
|
|
last_login_ip=None,
|
|
status="active",
|
|
initialized_at=datetime(2026, 1, 1),
|
|
created_at=datetime(2026, 1, 1),
|
|
)
|
|
|
|
|
|
def _rate_limiter() -> Mock:
|
|
rate_limiter = Mock(spec=AccountEducationRateLimiter)
|
|
rate_limiter.is_rate_limited.return_value = False
|
|
return rate_limiter
|
|
|
|
|
|
def test_verify_reads_account_before_billing_gateway_call() -> None:
|
|
accounts = Mock(spec=AccountRepository)
|
|
accounts.get.return_value = _account()
|
|
education = Mock(spec=AccountEducationGateway)
|
|
verification_rate_limiter = _rate_limiter()
|
|
activation_rate_limiter = _rate_limiter()
|
|
events: list[str] = []
|
|
verification_rate_limiter.is_rate_limited.side_effect = lambda _key: events.append("check") or False
|
|
verification_rate_limiter.increment_rate_limit.side_effect = lambda _key: events.append("increment")
|
|
education.verify.side_effect = lambda **_kwargs: (
|
|
events.append("verify") or AccountEducationVerification(token="education-token")
|
|
)
|
|
service = AccountEducationService(
|
|
accounts=accounts,
|
|
education=education,
|
|
verification_rate_limiter=verification_rate_limiter,
|
|
activation_rate_limiter=activation_rate_limiter,
|
|
)
|
|
|
|
result = service.verify(_context())
|
|
|
|
assert result == AccountEducationVerification(token="education-token")
|
|
assert events == ["check", "increment", "verify"]
|
|
accounts.get.assert_called_once_with("account-1")
|
|
verification_rate_limiter.is_rate_limited.assert_called_once_with("student@example.edu")
|
|
verification_rate_limiter.increment_rate_limit.assert_called_once_with("student@example.edu")
|
|
education.verify.assert_called_once_with(account_id="account-1")
|
|
activation_rate_limiter.is_rate_limited.assert_not_called()
|
|
|
|
|
|
def test_status_and_autocomplete_delegate_framework_neutral_contracts() -> None:
|
|
accounts = Mock(spec=AccountRepository)
|
|
education = Mock(spec=AccountEducationGateway)
|
|
status = AccountEducationStatus(
|
|
result=True,
|
|
is_student=True,
|
|
expire_at=datetime(2027, 1, 1, tzinfo=UTC),
|
|
allow_refresh=False,
|
|
)
|
|
autocomplete = AccountEducationAutocomplete(data=("Example University",), curr_page=0, has_next=False)
|
|
education.status.return_value = status
|
|
education.autocomplete.return_value = autocomplete
|
|
service = AccountEducationService(
|
|
accounts=accounts,
|
|
education=education,
|
|
verification_rate_limiter=_rate_limiter(),
|
|
activation_rate_limiter=_rate_limiter(),
|
|
)
|
|
|
|
assert service.status(_context()) == status
|
|
assert service.autocomplete(_context(), keywords="Example", page=0, limit=20) == autocomplete
|
|
education.status.assert_called_once_with("account-1")
|
|
education.autocomplete.assert_called_once_with(keywords="Example", page=0, limit=20)
|
|
|
|
|
|
def test_activate_delegates_account_and_workspace_context() -> None:
|
|
accounts = Mock(spec=AccountRepository)
|
|
accounts.get.return_value = _account()
|
|
education = Mock(spec=AccountEducationGateway)
|
|
activation = AccountEducationActivation(message="success")
|
|
verification_rate_limiter = _rate_limiter()
|
|
activation_rate_limiter = _rate_limiter()
|
|
events: list[str] = []
|
|
activation_rate_limiter.is_rate_limited.side_effect = lambda _key: events.append("check") or False
|
|
activation_rate_limiter.increment_rate_limit.side_effect = lambda _key: events.append("increment")
|
|
education.activate.side_effect = lambda **_kwargs: events.append("activate") or activation
|
|
service = AccountEducationService(
|
|
accounts=accounts,
|
|
education=education,
|
|
verification_rate_limiter=verification_rate_limiter,
|
|
activation_rate_limiter=activation_rate_limiter,
|
|
)
|
|
|
|
result = service.activate(
|
|
_context(),
|
|
token="education-token",
|
|
institution="Dify University",
|
|
role="Student",
|
|
)
|
|
|
|
assert result == activation
|
|
assert events == ["check", "increment", "activate"]
|
|
activation_rate_limiter.is_rate_limited.assert_called_once_with("student@example.edu")
|
|
activation_rate_limiter.increment_rate_limit.assert_called_once_with("student@example.edu")
|
|
verification_rate_limiter.is_rate_limited.assert_not_called()
|
|
education.activate.assert_called_once_with(
|
|
account_id="account-1",
|
|
tenant_id="workspace-1",
|
|
token="education-token",
|
|
institution="Dify University",
|
|
role="Student",
|
|
)
|
|
|
|
|
|
def test_verify_rejects_rate_limited_request() -> None:
|
|
accounts = Mock(spec=AccountRepository)
|
|
accounts.get.return_value = _account()
|
|
education = Mock(spec=AccountEducationGateway)
|
|
verification_rate_limiter = _rate_limiter()
|
|
verification_rate_limiter.is_rate_limited.return_value = True
|
|
service = AccountEducationService(
|
|
accounts=accounts,
|
|
education=education,
|
|
verification_rate_limiter=verification_rate_limiter,
|
|
activation_rate_limiter=_rate_limiter(),
|
|
)
|
|
|
|
with pytest.raises(EducationRateLimitExceededError):
|
|
service.verify(_context())
|
|
|
|
verification_rate_limiter.increment_rate_limit.assert_not_called()
|
|
education.verify.assert_not_called()
|
|
|
|
|
|
def test_activate_rejects_rate_limited_request() -> None:
|
|
accounts = Mock(spec=AccountRepository)
|
|
accounts.get.return_value = _account()
|
|
education = Mock(spec=AccountEducationGateway)
|
|
activation_rate_limiter = _rate_limiter()
|
|
activation_rate_limiter.is_rate_limited.return_value = True
|
|
service = AccountEducationService(
|
|
accounts=accounts,
|
|
education=education,
|
|
verification_rate_limiter=_rate_limiter(),
|
|
activation_rate_limiter=activation_rate_limiter,
|
|
)
|
|
|
|
with pytest.raises(EducationRateLimitExceededError):
|
|
service.activate(
|
|
_context(),
|
|
token="education-token",
|
|
institution="Dify University",
|
|
role="Student",
|
|
)
|
|
|
|
activation_rate_limiter.increment_rate_limit.assert_not_called()
|
|
education.activate.assert_not_called()
|