mirror of
https://github.com/langgenius/dify.git
synced 2026-07-21 18:58:35 +08:00
163 lines
5.1 KiB
Python
163 lines
5.1 KiB
Python
from __future__ import annotations
|
|
|
|
import inspect
|
|
from collections.abc import Callable
|
|
from types import SimpleNamespace
|
|
from typing import cast
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
from werkzeug.exceptions import Forbidden
|
|
|
|
from controllers.console.apikey import BaseApiKeyListResource, BaseApiKeyResource
|
|
from models import Account
|
|
from models.account import AccountStatus, TenantAccountRole
|
|
from models.enums import ApiTokenType
|
|
from models.model import ApiToken, App
|
|
|
|
|
|
def _make_list_resource() -> BaseApiKeyListResource:
|
|
resource = BaseApiKeyListResource()
|
|
resource.resource_type = ApiTokenType.APP
|
|
resource.resource_model = App
|
|
resource.resource_id_field = "app_id"
|
|
resource.token_prefix = "app-"
|
|
return resource
|
|
|
|
|
|
def _make_key_resource() -> BaseApiKeyResource:
|
|
resource = BaseApiKeyResource()
|
|
resource.resource_type = ApiTokenType.APP
|
|
resource.resource_model = App
|
|
resource.resource_id_field = "app_id"
|
|
return resource
|
|
|
|
|
|
def _make_account(role: TenantAccountRole) -> Account:
|
|
account = Account(
|
|
name="Test User",
|
|
email=f"{role.value}@example.com",
|
|
status=AccountStatus.ACTIVE,
|
|
)
|
|
account.id = f"{role.value}-user"
|
|
account.role = role
|
|
return account
|
|
|
|
|
|
def test_list_api_keys_uses_injected_session_and_tenant_id() -> None:
|
|
resource = _make_list_resource()
|
|
raw_get = cast(
|
|
Callable[[BaseApiKeyListResource, object, str, str], dict[str, object]],
|
|
inspect.unwrap(BaseApiKeyListResource.get),
|
|
)
|
|
session = MagicMock()
|
|
session.execute.return_value.scalar_one_or_none.return_value = SimpleNamespace()
|
|
api_key = SimpleNamespace(
|
|
id="key-1",
|
|
type=ApiTokenType.APP,
|
|
token="app-token",
|
|
last_used_at=None,
|
|
created_at=None,
|
|
)
|
|
session.scalars.return_value.all.return_value = [api_key]
|
|
|
|
result = raw_get(resource, session, "app-1", "tenant-1")
|
|
|
|
session.execute.assert_called_once()
|
|
session.scalars.assert_called_once()
|
|
assert result == {
|
|
"data": [
|
|
{
|
|
"id": "key-1",
|
|
"type": "app",
|
|
"token": "app-token",
|
|
"last_used_at": None,
|
|
"created_at": None,
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
def test_create_api_key_uses_injected_session_and_tenant_id() -> None:
|
|
resource = _make_list_resource()
|
|
raw_post = cast(
|
|
Callable[[BaseApiKeyListResource, object, str, str], tuple[dict[str, object], int]],
|
|
inspect.unwrap(BaseApiKeyListResource.post),
|
|
)
|
|
session = MagicMock()
|
|
session.execute.return_value.scalar_one_or_none.return_value = SimpleNamespace()
|
|
session.scalar.return_value = 0
|
|
|
|
def add_api_token(api_token: ApiToken) -> None:
|
|
api_token.id = "key-1"
|
|
|
|
with patch(
|
|
"controllers.console.apikey.ApiToken.generate_api_key", return_value="app-generated-token"
|
|
) as generate_api_key:
|
|
session.add.side_effect = add_api_token
|
|
|
|
result, status = raw_post(resource, session, "app-1", "tenant-1")
|
|
|
|
assert status == 201
|
|
assert result["token"] == "app-generated-token"
|
|
api_token = session.add.call_args.args[0]
|
|
assert api_token.app_id == "app-1"
|
|
assert api_token.tenant_id == "tenant-1"
|
|
assert api_token.type == ApiTokenType.APP
|
|
generate_api_key.assert_called_once_with("app-", 24, session=session)
|
|
session.execute.assert_called_once()
|
|
session.scalar.assert_called_once()
|
|
session.commit.assert_called_once()
|
|
|
|
|
|
def test_delete_api_key_rejects_non_admin_account() -> None:
|
|
resource = _make_key_resource()
|
|
raw_delete = cast(
|
|
Callable[[BaseApiKeyResource, object, str, str, str, Account], tuple[str, int]],
|
|
inspect.unwrap(BaseApiKeyResource.delete),
|
|
)
|
|
session = MagicMock()
|
|
session.execute.return_value.scalar_one_or_none.return_value = SimpleNamespace()
|
|
|
|
with pytest.raises(Forbidden):
|
|
raw_delete(
|
|
resource,
|
|
session,
|
|
"app-1",
|
|
"key-1",
|
|
"tenant-1",
|
|
_make_account(TenantAccountRole.NORMAL),
|
|
)
|
|
|
|
session.execute.assert_called_once()
|
|
session.scalar.assert_not_called()
|
|
|
|
|
|
def test_delete_api_key_uses_injected_session_user_and_tenant() -> None:
|
|
resource = _make_key_resource()
|
|
raw_delete = cast(
|
|
Callable[[BaseApiKeyResource, object, str, str, str, Account], tuple[str, int]],
|
|
inspect.unwrap(BaseApiKeyResource.delete),
|
|
)
|
|
api_key = SimpleNamespace(token="app-token", type=ApiTokenType.APP)
|
|
session = MagicMock()
|
|
session.execute.return_value.scalar_one_or_none.return_value = SimpleNamespace()
|
|
session.scalar.return_value = api_key
|
|
|
|
with patch("controllers.console.apikey.ApiTokenCache.delete") as delete_cache:
|
|
result, status = raw_delete(
|
|
resource,
|
|
session,
|
|
"app-1",
|
|
"key-1",
|
|
"tenant-1",
|
|
_make_account(TenantAccountRole.OWNER),
|
|
)
|
|
|
|
delete_cache.assert_called_once_with("app-token", ApiTokenType.APP)
|
|
assert session.execute.call_count == 2
|
|
session.scalar.assert_called_once()
|
|
session.commit.assert_called_once()
|
|
assert result == ""
|
|
assert status == 204
|